/* This file is part of mailfromd.
Copyright (C) 2006, 2007 Sergey Poznyakoff
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3, or (at your option)
any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see . */
#define MF_SOURCE_NAME MF_SOURCE_DNSCACHE
#ifdef HAVE_CONFIG_H
# include
#endif
#include
#include
#include
#include
#include
#include
#include
#include
#include "mailfromd.h"
static mf_status
parse_value(int qtype, const char *keystr,
int argc, char **argv, char **rbuf, size_t rcnt)
{
size_t i;
char *p;
time_t exp = strtoul(argv[0], &p, 0);
if (*p) {
debug1(1, "cannot parse cached value: error near %s",
p);
return mf_failure;
}
if (time(NULL) >= exp) {
debug2(10, "db cache record for %d,%s has expired",
qtype, keystr);
return mf_failure;
}
if (argc == 1)
return mf_not_found;
for (i = 0; i < argc-1; i++) {
rbuf[i] = argv[i + 1];
argv[i + 1] = NULL;
}
for (; i < rcnt; i++)
rbuf[i] = NULL;
return mf_success;
}
static int
dns_make_key(int type, const char *keystr, DBM_DATUM *key)
{
char *p;
char *pfx;
size_t len;
switch (type) {
case T_A:
pfx = "A";
break;
case T_PTR:
pfx = "PTR";
break;
case T_MX:
pfx = "MX";
break;
default:
mu_error(_("Query type %d is not yet supported by %s"),
type, __FILE__);
return 1;
}
len = strlen(pfx) + 1 + strlen(keystr) + 1;
p = xmalloc(len);
memset (key, 0, sizeof *key);
MU_DATUM_PTR(*key) = p;
MU_DATUM_SIZE(*key) = len;
strcpy(p, pfx);
strcat(p, " ");
strcat(p, keystr);
return 0;
}
static void
dns_free_key(DBM_DATUM *key)
{
free(MU_DATUM_PTR(*key));
}
mf_status
dns_cache_get(int type, const char *keystr, char **rbuf, size_t rcnt)
{
mf_status rc;
DBM_FILE db;
DBM_DATUM key;
DBM_DATUM contents;
int res;
if (!dns_cache_format->enabled)
return mf_failure;
if (dns_make_key(type, keystr, &key))
return mf_failure;
debug1(50, "%s: looking up in cache", MU_DATUM_PTR(key));
if (access(dns_cache_format->dbname, F_OK) && errno == ENOENT)
return mf_failure;
if (mu_dbm_open(dns_cache_format->dbname, &db, MU_STREAM_READ, 0600,
NULL)) {
mu_error(_("mu_dbm_open(%s) failed: %s"),
dns_cache_format->dbname,
mu_dbm_strerror());
dns_free_key(&key);
return mf_failure;
}
memset (&contents, 0, sizeof contents);
if ((res = mu_dbm_fetch(&db, key, &contents)) == 0) {
int argc;
char **argv;
debug4(50, "%s: got %*.*s", MU_DATUM_PTR(key),
MU_DATUM_SIZE(contents), MU_DATUM_SIZE(contents),
MU_DATUM_PTR(contents));
if (mu_argcv_get_n(MU_DATUM_PTR(contents),
MU_DATUM_SIZE(contents), NULL, "",
&argc, &argv)) {
debug1(1, "cannot parse cached value: %s",
mu_strerror(rc));
rc = mf_failure;
} else
rc = parse_value(type, keystr, argc, argv, rbuf, rcnt);
mu_argcv_free(argc, argv);
mu_dbm_datum_free(&contents);
} else {
if (res != MU_ERR_NOENT)
mu_error(_("Cannot fetch `%s' from `%s': %s"),
keystr, db.name, mu_dbm_strerror());
rc = mf_failure;
}
mu_dbm_close(&db);
dns_free_key(&key);
return rc;
}
void
dns_cache_put(int type, const char *keystr, time_t ttl,
char **rbuf, size_t rcnt)
{
DBM_FILE db;
DBM_DATUM key;
DBM_DATUM contents;
char *p;
size_t len;
int res;
if (!dns_cache_format->enabled)
return;
if (dns_make_key(type, keystr, &key))
return;
if (mu_dbm_open(dns_cache_format->dbname, &db, MU_STREAM_RDWR,
0600, NULL)) {
mu_error(_("mu_dbm_open(%s) failed: %s"),
dns_cache_format->dbname,
mu_dbm_strerror());
dns_free_key(&key);
return;
}
res = mu_argcv_string(rcnt, rbuf, &p);
debug2(50, "%s: storing in cache: %s", MU_DATUM_PTR(key), p);
if (res) {
mu_error(_("Cannot convert arg array to string: %s"),
mu_strerror(res));
} else {
char buf[NUMERIC_BUFSIZE_BOUND];
size_t blen;
len = strlen(p);
blen = snprintf(buf, sizeof buf, "%lu",
(unsigned long)
(time(NULL) +
(ttl ? ttl :
dns_cache_format->expire_interval)));
p = xrealloc(p, blen + 1 + len + 1);
memmove(p + blen + 1, p, len + 1);
strcpy(p, buf);
p[blen] = ' ';
memset(&contents, 0, sizeof contents);
MU_DATUM_PTR(contents) = p;
MU_DATUM_SIZE(contents) = blen + 1 + len + 1;
if (mu_dbm_insert(&db, key, contents, 1))
mu_error(_("Cannot insert datum `%s' into `%s': %s"),
keystr, db.name, mu_dbm_strerror());
free(p);
}
mu_dbm_close(&db);
dns_free_key(&key);
}
static void
dns_print_item(const char *key, size_t size, const void *content)
{
char *p;
time_t t = strtoul(content, &p, 0);
printf("%s: ", key);
if (*p != ' ') {
printf("[unrecognized]: %*.*s", size, size, (char*) content);
} else {
format_time_str(stdout, t);
printf("%s\n", p);
}
}
static int
dns_expire_item(const void *content)
{
char *p;
time_t t = strtoul(content, &p, 0);
if (*p != ' ') {
debug1(1, "cannot parse cached value: error near %s",
p);
return 0;
}
return time(NULL) > t;
}
static struct db_format dns_cache_format_struct = {
"dns",
DEFAULT_DNS_DATABASE,
1,
DEFAULT_DNS_NEGATIVE_EXPIRE_INTERVAL,
dns_print_item,
dns_expire_item
};
struct db_format *dns_cache_format = &dns_cache_format_struct;