/* 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;