From 5b78ecce464ef90e4794ed3c6f1d0dcfd5ed2e83 Mon Sep 17 00:00:00 2001 From: Vsevolod Stakhov Date: Wed, 19 Feb 2014 17:29:23 +0000 Subject: Integrate rspamd with librdns. --- src/CMakeLists.txt | 6 +- src/cfg_file.h | 2 +- src/cfg_rcl.c | 2 +- src/dkim.c | 6 +- src/dns.c | 1402 ++------------------------------------------------- src/dns.h | 111 +--- src/lua/lua_dns.c | 16 +- src/lua/lua_http.c | 4 +- src/lua/lua_redis.c | 6 +- src/plugins/surbl.c | 15 +- src/rdns | 2 +- src/smtp_proxy.c | 17 +- src/spf.c | 18 +- 13 files changed, 95 insertions(+), 1512 deletions(-) (limited to 'src') diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 84f980655..b6a0731dd 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -104,10 +104,8 @@ SET_TARGET_PROPERTIES(rspamd-server PROPERTIES LINKER_LANGUAGE C COMPILE_FLAGS " TARGET_LINK_LIBRARIES(rspamd-server rspamd-lua) TARGET_LINK_LIBRARIES(rspamd-server rspamd-json) TARGET_LINK_LIBRARIES(rspamd-server rspamd-cdb) -TARGET_LINK_LIBRARIES(rspamd-server rspamd-util) -IF(LIBJUDY_LIBRARY) - TARGET_LINK_LIBRARIES(rspamd-server Judy) -ENDIF(LIBJUDY_LIBRARY) +TARGET_LINK_LIBRARIES(rspamd-server rspamd-util) +TARGET_LINK_LIBRARIES(rspamd-server rdns) IF(CMAKE_COMPILER_IS_GNUCC) SET_TARGET_PROPERTIES(rspamd-server PROPERTIES COMPILE_FLAGS "-DRSPAMD_LIB -fno-strict-aliasing") ENDIF(CMAKE_COMPILER_IS_GNUCC) diff --git a/src/cfg_file.h b/src/cfg_file.h index 6f3489455..13acb158f 100644 --- a/src/cfg_file.h +++ b/src/cfg_file.h @@ -363,7 +363,7 @@ struct config_file { gchar* history_file; /**< file to save rolling history */ - guint32 dns_timeout; /**< timeout in milliseconds for waiting for dns reply */ + gdouble dns_timeout; /**< timeout in milliseconds for waiting for dns reply */ guint32 dns_retransmits; /**< maximum retransmits count */ guint32 dns_throttling_errors; /**< maximum errors for starting resolver throttling */ guint32 dns_throttling_time; /**< time in seconds for DNS throttling */ diff --git a/src/cfg_rcl.c b/src/cfg_rcl.c index 5793b5010..2d0f9b186 100644 --- a/src/cfg_rcl.c +++ b/src/cfg_rcl.c @@ -1065,7 +1065,7 @@ rspamd_rcl_config_init (void) rspamd_rcl_add_default_handler (sub, "dns_nameserver", rspamd_rcl_parse_struct_string_list, G_STRUCT_OFFSET (struct config_file, nameservers), 0); rspamd_rcl_add_default_handler (sub, "dns_timeout", rspamd_rcl_parse_struct_time, - G_STRUCT_OFFSET (struct config_file, dns_timeout), RSPAMD_CL_FLAG_TIME_INTEGER); + G_STRUCT_OFFSET (struct config_file, dns_timeout), RSPAMD_CL_FLAG_TIME_FLOAT); rspamd_rcl_add_default_handler (sub, "dns_retransmits", rspamd_rcl_parse_struct_integer, G_STRUCT_OFFSET (struct config_file, dns_retransmits), RSPAMD_CL_FLAG_INT_32); rspamd_rcl_add_default_handler (sub, "dns_sockets", rspamd_rcl_parse_struct_integer, diff --git a/src/dkim.c b/src/dkim.c index fe48668aa..2b74d53ce 100644 --- a/src/dkim.c +++ b/src/dkim.c @@ -749,17 +749,17 @@ rspamd_dkim_parse_key (const gchar *txt, gsize *keylen, GError **err) /* Get TXT request data and parse it */ static void -rspamd_dkim_dns_cb (struct rspamd_dns_reply *reply, gpointer arg) +rspamd_dkim_dns_cb (struct rdns_reply *reply, gpointer arg) { struct rspamd_dkim_key_cbdata *cbdata = arg; rspamd_dkim_key_t *key = NULL; GError *err = NULL; - struct rspamd_reply_entry *elt; + struct rdns_reply_entry *elt; gsize keylen = 0; if (reply->code != DNS_RC_NOERROR) { g_set_error (&err, DKIM_ERROR, DKIM_SIGERROR_NOKEY, "dns request to %s failed: %s", cbdata->ctx->dns_key, - dns_strerror (reply->code)); + rdns_strerror (reply->code)); cbdata->handler (NULL, 0, cbdata->ctx, cbdata->ud, err); } else { diff --git a/src/dns.c b/src/dns.c index a74e5b692..5b2f928b2 100644 --- a/src/dns.c +++ b/src/dns.c @@ -25,1329 +25,94 @@ #include "config.h" #include "dns.h" -#include "dns_private.h" #include "main.h" #include "utlist.h" #include "uthash.h" -#include "ottery.h" +#include "rdns_event.h" -#ifdef HAVE_OPENSSL -#include -#endif - -static void dns_retransmit_handler (gint fd, short what, void *arg); - -static guint16 -dns_permutor_generate_id (void) -{ - guint16 id; - - id = ottery_rand_unsigned (); - - return id; -} - -/* Punycode utility */ -static guint -digit (unsigned n) -{ - return "abcdefghijklmnopqrstuvwxyz0123456789"[n]; -} - -static guint -adapt (guint delta, guint numpoints, gint first) -{ - guint k; - - if (first) { - delta = delta / damp; - } - else { - delta /= 2; - } - delta += delta / numpoints; - k = 0; - while (delta > ((base - t_min) * t_max) / 2) { - delta /= base - t_min; - k += base; - } - return k + (((base - t_min + 1) * delta) / (delta + skew)); -} - -/** - * Convert an UCS4 string to a puny-coded DNS label string suitable - * when combined with delimiters and other labels for DNS lookup. - * - * @param in an UCS4 string to convert - * @param in_len the length of in. - * @param out the resulting puny-coded string. The string is not NUL - * terminatied. - * @param out_len before processing out_len should be the length of - * the out variable, after processing it will be the length of the out - * string. - * - * @return returns 0 on success, an wind error code otherwise - * @ingroup wind - */ - -gboolean -punycode_label_toascii(const gunichar *in, gsize in_len, gchar *out, - gsize *out_len) -{ - guint n = initial_n; - guint delta = 0; - guint bias = initial_bias; - guint h = 0; - guint b; - guint i; - guint o = 0; - guint m; - - for (i = 0; i < in_len; ++i) { - if (in[i] < 0x80) { - ++h; - if (o >= *out_len) { - return FALSE; - } - out[o++] = in[i]; - } - } - b = h; - if (b > 0) { - if (o >= *out_len) { - return FALSE; - } - out[o++] = 0x2D; - } - /* is this string punycoded */ - if (h < in_len) { - if (o + 4 >= *out_len) { - return FALSE; - } - memmove (out + 4, out, o); - memcpy (out, "xn--", 4); - o += 4; - } - - while (h < in_len) { - m = (guint) -1; - for (i = 0; i < in_len; ++i) { - - if (in[i] < m && in[i] >= n) { - m = in[i]; - } - } - delta += (m - n) * (h + 1); - n = m; - for (i = 0; i < in_len; ++i) { - if (in[i] < n) { - ++delta; - } - else if (in[i] == n) { - guint q = delta; - guint k; - for (k = base;; k += base) { - guint t; - if (k <= bias) { - t = t_min; - } - else if (k >= bias + t_max) { - t = t_max; - } - else { - t = k - bias; - } - if (q < t) { - break; - } - if (o >= *out_len) { - return -1; - } - out[o++] = digit (t + ((q - t) % (base - t))); - q = (q - t) / (base - t); - } - if (o >= *out_len) { - return -1; - } - out[o++] = digit (q); - /* output */ - bias = adapt (delta, h + 1, h == b); - delta = 0; - ++h; - } - } - ++delta; - ++n; - } - - *out_len = o; - return TRUE; -} - -struct dns_request_key { - guint16 id; - guint16 port; +struct rspamd_dns_resolver { + struct rdns_resolver *r; + struct event_base *ev_base; + gdouble request_timeout; + guint max_retransmits; }; -/** Message compression */ -struct dns_name_table { - guint8 off; - guint8 *label; - guint8 len; - UT_hash_handle hh; +struct rspamd_dns_request_ud { + struct rspamd_async_session *session; + dns_callback_type cb; + gpointer ud; + struct rdns_request *req; }; -static gboolean -try_compress_label (memory_pool_t *pool, guint8 *target, guint8 *start, guint8 len, - guint8 *label, struct dns_name_table **table) -{ - struct dns_name_table *found = NULL; - guint16 pointer; - - HASH_FIND (hh, *table, label, len, found); - if (found != NULL) { - pointer = htons ((guint16)found->off | 0xC0); - memcpy (target, &pointer, sizeof (pointer)); - return TRUE; - } - else { - /* Insert label to list */ - found = memory_pool_alloc (pool, sizeof (struct dns_name_table)); - found->off = target - start; - found->label = label; - found->len = len; - HASH_ADD_KEYPTR (hh, *table, found->label, len, found); - } - - return FALSE; -} - -/** Packet creating functions */ -static void -allocate_packet (struct rspamd_dns_request *req, guint namelen) -{ - namelen += 96 /* header */ - + 2 /* Trailing label */ - + 4 /* Resource type */ - + 11; /* EDNS0 RR */ - req->packet = memory_pool_alloc (req->pool, namelen); - req->pos = 0; - req->packet_len = namelen; -} - static void -make_dns_header (struct rspamd_dns_request *req) +rspamd_dns_fin_cb (gpointer arg) { - struct dns_header *header; + struct rdns_request *req = arg; - /* Set DNS header values */ - header = (struct dns_header *)req->packet; - memset (header, 0 , sizeof (struct dns_header)); - header->qid = dns_permutor_generate_id (); - header->rd = 1; - header->qdcount = htons (1); - header->arcount = htons (1); - req->pos += sizeof (struct dns_header); - req->id = header->qid; -} - -static gboolean -maybe_punycode_label (guint8 *begin, guint8 **res, guint8 **dot, guint *label_len) -{ - gboolean ret = FALSE; - guint8 *p = begin; - - *dot = NULL; - - while (*p) { - if (*p == '.') { - *dot = p; - break; - } - else if ((*p) & 0x80) { - ret = TRUE; - } - p ++; - } - - if (*p) { - *res = p - 1; - *label_len = p - begin; - } - else { - *res = p; - *label_len = p - begin; - } - - return ret; + rdns_request_release (req); } static void -format_dns_name (struct rspamd_dns_request *req, const gchar *name, guint namelen) +rspamd_dns_callback (struct rdns_reply *reply, gpointer ud) { - guint8 *pos = req->packet + req->pos, *end, *dot, *name_pos, *begin; - guint remain = req->packet_len - req->pos - 5, label_len; - struct dns_name_table *table = NULL; - gunichar *uclabel; - glong uclabel_len; - gsize punylabel_len; - guint8 tmp_label[DNS_D_MAXLABEL]; + struct rspamd_dns_request_ud *reqdata = ud; - if (namelen == 0) { - namelen = strlen (name); - } - - begin = (guint8 *)name; - end = (guint8 *)name + namelen; - for (;;) { - /* Check label for unicode characters */ - if (maybe_punycode_label (begin, &name_pos, &dot, &label_len)) { - /* Convert to ucs4 */ - uclabel = g_utf8_to_ucs4_fast ((gchar *)begin, label_len, &uclabel_len); - memory_pool_add_destructor (req->pool, g_free, uclabel); - punylabel_len = DNS_D_MAXLABEL; + reqdata->cb (reply, reqdata->ud); - punycode_label_toascii (uclabel, uclabel_len, (gchar *)tmp_label, &punylabel_len); - /* Try to compress name */ - if (! try_compress_label (req->pool, pos, req->packet, punylabel_len, tmp_label, &table)) { - /* Copy punylabel */ - *pos++ = (guint8)punylabel_len; - memcpy (pos, tmp_label, punylabel_len); - pos += punylabel_len; - } - else { - pos += 2; - } - if (dot) { - remain -= label_len + 1; - begin = dot + 1; - } - else { - break; - } - } - else { - if (dot) { - if (label_len > DNS_D_MAXLABEL) { - msg_err ("dns name component is longer than 63 bytes, should be stripped"); - label_len = DNS_D_MAXLABEL; - } - if (remain < label_len + 1) { - label_len = remain - 1; - msg_err ("no buffer remain for constructing query, strip to %ud", label_len); - } - if (label_len == 0) { - /* Two dots in order, skip this */ - msg_info ("name contains two or more dots in a row, replace it with one dot"); - begin = dot + 1; - continue; - } - /* First try to compress name */ - if (! try_compress_label (req->pool, pos, req->packet, end - begin, begin, &table)) { - *pos++ = (guint8)label_len; - memcpy (pos, begin, label_len); - pos += label_len; - } - else { - pos += 2; - } - remain -= label_len + 1; - begin = dot + 1; - } - else { - if (label_len == 0) { - /* If name is ended with dot */ - break; - } - if (label_len > DNS_D_MAXLABEL) { - msg_err ("dns name component is longer than 63 bytes, should be stripped"); - label_len = DNS_D_MAXLABEL; - } - if (remain < label_len + 1) { - label_len = remain - 1; - msg_err ("no buffer remain for constructing query, strip to %ud", label_len); - } - *pos++ = (guint8)label_len; - memcpy (pos, begin, label_len); - pos += label_len; - break; - } - } - if (remain == 0) { - msg_err ("no buffer space available, aborting"); - break; - } - } - /* Termination label */ - *pos = '\0'; - req->pos += pos - (req->packet + req->pos) + 1; - if (table != NULL) { - HASH_CLEAR (hh, table); - } -} - -static void -make_ptr_req (struct rspamd_dns_request *req, struct in_addr *addr) -{ - gchar ipbuf[sizeof("255.255.255.255.in-addr.arpa")]; - guint32 r; - guint16 *p; - guint8 *addr_p = (guint8 *)&addr->s_addr; - - r = rspamd_snprintf (ipbuf, sizeof(ipbuf), "%d.%d.%d.%d.in-addr.arpa", - addr_p[3], addr_p[2], addr_p[1], addr_p[0]); - allocate_packet (req, r); - make_dns_header (req); - format_dns_name (req, ipbuf, r); - p = (guint16 *)(req->packet + req->pos); - *p++ = htons (DNS_T_PTR); - *p = htons (DNS_C_IN); - req->requested_name = memory_pool_strdup (req->pool, ipbuf); - req->pos += sizeof (guint16) * 2; - req->type = DNS_REQUEST_PTR; -} - -static void -make_a_req (struct rspamd_dns_request *req, const gchar *name) -{ - guint16 *p; - - allocate_packet (req, strlen (name)); - make_dns_header (req); - format_dns_name (req, name, 0); - p = (guint16 *)(req->packet + req->pos); - *p++ = htons (DNS_T_A); - *p = htons (DNS_C_IN); - req->pos += sizeof (guint16) * 2; - req->type = DNS_REQUEST_A; - req->requested_name = name; -} - -#ifdef HAVE_INET_PTON -static void -make_aaa_req (struct rspamd_dns_request *req, const gchar *name) -{ - guint16 *p; - - allocate_packet (req, strlen (name)); - make_dns_header (req); - format_dns_name (req, name, 0); - p = (guint16 *)(req->packet + req->pos); - *p++ = htons (DNS_T_AAAA); - *p = htons (DNS_C_IN); - req->pos += sizeof (guint16) * 2; - req->type = DNS_REQUEST_AAA; - req->requested_name = name; -} -#endif - -static void -make_txt_req (struct rspamd_dns_request *req, const gchar *name) -{ - guint16 *p; - - allocate_packet (req, strlen (name)); - make_dns_header (req); - format_dns_name (req, name, 0); - p = (guint16 *)(req->packet + req->pos); - *p++ = htons (DNS_T_TXT); - *p = htons (DNS_C_IN); - req->pos += sizeof (guint16) * 2; - req->type = DNS_REQUEST_TXT; - req->requested_name = name; -} - -static void -make_mx_req (struct rspamd_dns_request *req, const gchar *name) -{ - guint16 *p; - - allocate_packet (req, strlen (name)); - make_dns_header (req); - format_dns_name (req, name, 0); - p = (guint16 *)(req->packet + req->pos); - *p++ = htons (DNS_T_MX); - *p = htons (DNS_C_IN); - req->pos += sizeof (guint16) * 2; - req->type = DNS_REQUEST_MX; - req->requested_name = name; -} - -static void -make_srv_req (struct rspamd_dns_request *req, const gchar *service, const gchar *proto, const gchar *name) -{ - guint16 *p; - guint len; - gchar *target; - - len = strlen (service) + strlen (proto) + strlen (name) + 5; - - allocate_packet (req, len); - make_dns_header (req); - target = memory_pool_alloc (req->pool, len); - len = rspamd_snprintf (target, len, "_%s._%s.%s", service, proto, name); - format_dns_name (req, target, len); - p = (guint16 *)(req->packet + req->pos); - *p++ = htons (DNS_T_SRV); - *p = htons (DNS_C_IN); - req->pos += sizeof (guint16) * 2; - req->type = DNS_REQUEST_SRV; - req->requested_name = name; -} - -static void -make_spf_req (struct rspamd_dns_request *req, const gchar *name) -{ - guint16 *p; - - allocate_packet (req, strlen (name)); - make_dns_header (req); - format_dns_name (req, name, 0); - p = (guint16 *)(req->packet + req->pos); - *p++ = htons (DNS_T_SPF); - *p = htons (DNS_C_IN); - req->pos += sizeof (guint16) * 2; - req->type = DNS_REQUEST_SPF; - req->requested_name = name; -} - -static void -rspamd_dns_add_edns0 (struct rspamd_dns_request *req) -{ - guint8 *p8; - guint16 *p16; - - p8 = (guint8 *)(req->packet + req->pos); - *p8 = '\0'; /* Name is root */ - p16 = (guint16 *)(req->packet + req->pos + 1); - *p16++ = htons (DNS_T_OPT); - /* UDP packet length */ - *p16++ = htons (UDP_PACKET_SIZE); - /* Extended rcode 00 00 */ - *p16++ = 0; - /* Z 10000000 00000000 to allow dnssec */ - p8 = (guint8 *)p16++; - /* Not a good time for DNSSEC */ - *p8 = 0x00; - /* Length */ - *p16 = 0; - req->pos += sizeof (guint8) + sizeof (guint16) * 5; -} - -static gint -send_dns_request (struct rspamd_dns_request *req) -{ - gint r; - struct rspamd_dns_server *serv = req->io->srv; - - r = send (req->sock, req->packet, req->pos, 0); - if (r == -1) { - if (errno == EAGAIN) { - event_set (&req->io_event, req->sock, EV_WRITE, dns_retransmit_handler, req); - event_base_set (req->resolver->ev_base, &req->io_event); - event_add (&req->io_event, &req->tv); - register_async_event (req->session, (event_finalizer_t)event_del, &req->io_event, - g_quark_from_static_string ("dns resolver")); - return 0; - } - else { - msg_err ("send failed: %s for server %s", strerror (errno), serv->name); - upstream_fail (&serv->up, req->time); - return -1; - } - } - else if (r < req->pos) { - msg_err ("incomplete send over UDP socket, seems to be internal bug"); - event_set (&req->io_event, req->sock, EV_WRITE, dns_retransmit_handler, req); - event_base_set (req->resolver->ev_base, &req->io_event); - event_add (&req->io_event, &req->tv); - register_async_event (req->session, (event_finalizer_t)event_del, &req->io_event, - g_quark_from_static_string ("dns resolver")); - return 0; - } - - return 1; -} - -static void -dns_fin_cb (gpointer arg) -{ - struct rspamd_dns_request *req = arg; - - event_del (&req->timer_event); - g_hash_table_remove (req->io->requests, &req->id); -} - -static guint8 * -decompress_label (guint8 *begin, guint16 *len, guint16 max) -{ - guint16 offset = (*len); - - if (offset > max) { - msg_info ("invalid DNS compression pointer: %d max is %d", (gint)offset, (gint)max); - return NULL; - } - *len = *(begin + offset); - return begin + offset; -} - -#define UNCOMPRESS_DNS_OFFSET(p) (((*(p)) ^ DNS_COMPRESSION_BITS) << 8) + *((p) + 1) - -static guint8 * -dns_request_reply_cmp (struct rspamd_dns_request *req, guint8 *in, gint len) -{ - guint8 *p, *c, *l1, *l2; - guint16 len1, len2; - gint decompressed = 0; - - /* QR format: - * labels - len:octets - * null label - 0 - * class - 2 octets - * type - 2 octets - */ - - /* In p we would store current position in reply and in c - position in request */ - p = in; - c = req->packet + sizeof (struct dns_header); - - for (;;) { - /* Get current label */ - len1 = *p; - len2 = *c; - if (p - in > len) { - msg_info ("invalid dns reply"); - return NULL; - } - /* This may be compressed, so we need to decompress it */ - if (len1 & DNS_COMPRESSION_BITS) { - len1 = UNCOMPRESS_DNS_OFFSET(p); - l1 = decompress_label (in, &len1, len); - if (l1 == NULL) { - return NULL; - } - decompressed ++; - l1 ++; - p += 2; - } - else { - l1 = ++p; - p += len1; - } - if (len2 & DNS_COMPRESSION_BITS) { - len2 = UNCOMPRESS_DNS_OFFSET(p); - l2 = decompress_label (req->packet, &len2, len); - if (l2 == NULL) { - msg_info ("invalid DNS pointer"); - return NULL; - } - decompressed ++; - l2 ++; - c += 2; - } - else { - l2 = ++c; - c += len2; - } - if (len1 != len2) { - return NULL; - } - if (len1 == 0) { - break; - } - - if (memcmp (l1, l2, len1) != 0) { - return NULL; - } - if (decompressed == 2) { - break; - } - } - - /* p now points to the end of QR section */ - /* Compare class and type */ - if (memcmp (p, c, sizeof (guint16) * 2) == 0) { - return p + sizeof (guint16) * 2; - } - return NULL; -} - -#define MAX_RECURSION_LEVEL 10 - -static gboolean -dns_parse_labels (guint8 *in, gchar **target, guint8 **pos, struct rspamd_dns_reply *rep, - gint *remain, gboolean make_name) -{ - guint16 namelen = 0; - guint8 *p = *pos, *begin = *pos, *l, *t, *end = *pos + *remain, *new_pos = *pos; - guint16 llen; - gint length = *remain, new_remain = *remain; - gint ptrs = 0, labels = 0; - gboolean got_compression = FALSE; - - /* First go through labels and calculate name length */ - while (p - begin < length) { - if (ptrs > MAX_RECURSION_LEVEL) { - msg_warn ("dns pointers are nested too much"); - return FALSE; - } - llen = *p; - if (llen == 0) { - if (!got_compression) { - /* In case of compression we have already decremented the processing position */ - new_remain -= sizeof (guint8); - new_pos += sizeof (guint8); - } - break; - } - else if ((llen & DNS_COMPRESSION_BITS)) { - if (end - p > 1) { - ptrs ++; - llen = UNCOMPRESS_DNS_OFFSET(p); - l = decompress_label (in, &llen, end - in); - if (l == NULL) { - msg_info ("invalid DNS pointer"); - return FALSE; - } - if (!got_compression) { - /* Our label processing is finished actually */ - new_remain -= sizeof (guint16); - new_pos += sizeof (guint16); - got_compression = TRUE; - } - if (l < in || l > begin + length) { - msg_warn ("invalid pointer in DNS packet"); - return FALSE; - } - begin = l; - length = end - begin; - p = l + *l + 1; - namelen += *l; - labels ++; - } - else { - msg_warn ("DNS packet has incomplete compressed label, input length: %d bytes, remain: %d", - *remain, new_remain); - return FALSE; - } - } - else { - namelen += llen; - p += llen + 1; - labels ++; - if (!got_compression) { - new_remain -= llen + 1; - new_pos += llen + 1; - } - } - } - - if (!make_name) { - goto end; - } - *target = memory_pool_alloc (rep->request->pool, namelen + labels + 3); - t = (guint8 *)*target; - p = *pos; - begin = *pos; - length = *remain; - /* Now copy labels to name */ - while (p - begin < length) { - llen = *p; - if (llen == 0) { - break; - } - else if (llen & DNS_COMPRESSION_BITS) { - llen = UNCOMPRESS_DNS_OFFSET(p); - l = decompress_label (in, &llen, end - in); - begin = l; - length = end - begin; - p = l + *l + 1; - memcpy (t, l + 1, *l); - t += *l; - *t ++ = '.'; - } - else { - memcpy (t, p + 1, *p); - t += *p; - *t ++ = '.'; - p += *p + 1; - } - } - *(t - 1) = '\0'; -end: - *remain = new_remain; - *pos = new_pos; - - return TRUE; -} - -#define GET16(x) do {(x) = ((*p) << 8) + *(p + 1); p += sizeof (guint16); *remain -= sizeof (guint16); } while(0) -#define GET32(x) do {(x) = ((*p) << 24) + ((*(p + 1)) << 16) + ((*(p + 2)) << 8) + *(p + 3); p += sizeof (guint32); *remain -= sizeof (guint32); } while(0) -#define SKIP(type) do { p += sizeof(type); *remain -= sizeof(type); } while (0) - -static gint -dns_parse_rr (guint8 *in, struct rspamd_reply_entry *elt, guint8 **pos, struct rspamd_dns_reply *rep, gint *remain) -{ - guint8 *p = *pos, parts; - guint16 type, datalen, txtlen, copied, ttl; - gboolean parsed = FALSE; - - /* Skip the whole name */ - if (! dns_parse_labels (in, NULL, &p, rep, remain, FALSE)) { - msg_info ("bad RR name"); - return -1; - } - if (*remain < (gint)sizeof (guint16) * 6) { - msg_info ("stripped dns reply: %d bytes remain", *remain); - return -1; - } - GET16 (type); - GET16 (ttl); - /* Skip class */ - SKIP (guint32); - GET16 (datalen); - /* Now p points to RR data */ - switch (type) { - case DNS_T_A: - if (!(datalen & 0x3) && datalen <= *remain) { - memcpy (&elt->content.a.addr, p, sizeof (struct in_addr)); - p += datalen; - *remain -= datalen; - parsed = TRUE; - elt->type = DNS_REQUEST_A; - } - else { - msg_info ("corrupted A record"); - return -1; - } - break; -#ifdef HAVE_INET_PTON - case DNS_T_AAAA: - if (datalen == sizeof (struct in6_addr) && datalen <= *remain) { - memcpy (&elt->content.aaa.addr, p, sizeof (struct in6_addr)); - p += datalen; - *remain -= datalen; - parsed = TRUE; - elt->type = DNS_REQUEST_AAA; - } - else { - msg_info ("corrupted AAAA record"); - return -1; - } - break; -#endif - case DNS_T_PTR: - if (! dns_parse_labels (in, &elt->content.ptr.name, &p, rep, remain, TRUE)) { - msg_info ("invalid labels in PTR record"); - return -1; - } - parsed = TRUE; - elt->type = DNS_REQUEST_PTR; - break; - case DNS_T_MX: - GET16 (elt->content.mx.priority); - if (! dns_parse_labels (in, &elt->content.mx.name, &p, rep, remain, TRUE)) { - msg_info ("invalid labels in MX record"); - return -1; - } - parsed = TRUE; - elt->type = DNS_REQUEST_MX; - break; - case DNS_T_TXT: - case DNS_T_SPF: - elt->content.txt.data = memory_pool_alloc (rep->request->pool, datalen + 1); - /* Now we should compose data from parts */ - copied = 0; - parts = 0; - while (copied + parts < datalen) { - txtlen = *p; - if (txtlen + copied + parts <= datalen) { - parts ++; - memcpy (elt->content.txt.data + copied, p + 1, txtlen); - copied += txtlen; - p += txtlen + 1; - *remain -= txtlen + 1; - } - else { - break; - } - } - *(elt->content.txt.data + copied) = '\0'; - parsed = TRUE; - elt->type = DNS_REQUEST_TXT; - break; - case DNS_T_SRV: - if (p - *pos > (gint)(*remain - sizeof (guint16) * 3)) { - msg_info ("stripped dns reply while reading SRV record"); - return -1; - } - GET16 (elt->content.srv.priority); - GET16 (elt->content.srv.weight); - GET16 (elt->content.srv.port); - if (! dns_parse_labels (in, &elt->content.srv.target, &p, rep, remain, TRUE)) { - msg_info ("invalid labels in SRV record"); - return -1; - } - parsed = TRUE; - elt->type = DNS_REQUEST_SRV; - break; - case DNS_T_CNAME: - /* Skip cname records */ - p += datalen; - *remain -= datalen; - break; - default: - msg_debug ("unexpected RR type: %d", type); - p += datalen; - *remain -= datalen; - break; - } - *pos = p; - - if (parsed) { - elt->ttl = ttl; - return 1; - } - return 0; -} - -static gboolean -dns_parse_reply (gint sock, guint8 *in, gint r, struct rspamd_dns_resolver *resolver, - struct rspamd_dns_request **req_out, struct rspamd_dns_reply **_rep) -{ - struct dns_header *header = (struct dns_header *)in; - struct rspamd_dns_request *req; - struct rspamd_dns_reply *rep; - struct rspamd_dns_io_channel *ioc; - struct rspamd_reply_entry *elt; - guint8 *pos; - guint16 id; - gint i, t; - - /* First check header fields */ - if (header->qr == 0) { - msg_info ("got request while waiting for reply"); - return FALSE; - } - - /* Find io channel */ - if ((ioc = g_hash_table_lookup (resolver->io_channels, GINT_TO_POINTER (sock))) == NULL) { - msg_err ("io channel is not found for this resolver: %d", sock); - return FALSE; - } - - /* Now try to find corresponding request */ - id = header->qid; - if ((req = g_hash_table_lookup (ioc->requests, &id)) == NULL) { - /* No such requests found */ - msg_debug ("DNS request with id %d has not been found for IO channel", (gint)id); - return FALSE; - } - *req_out = req; - /* - * Now we have request and query data is now at the end of header, so compare - * request QR section and reply QR section - */ - if ((pos = dns_request_reply_cmp (req, in + sizeof (struct dns_header), - r - sizeof (struct dns_header))) == NULL) { - msg_debug ("DNS request with id %d is for different query, ignoring", (gint)id); - return FALSE; - } - /* - * Now pos is in answer section, so we should extract data and form reply - */ - rep = memory_pool_alloc (req->pool, sizeof (struct rspamd_dns_reply)); - rep->request = req; - rep->entries = NULL; - rep->code = header->rcode; - - if (rep->code == DNS_RC_NOERROR) { - r -= pos - in; - /* Extract RR records */ - for (i = 0; i < ntohs (header->ancount); i ++) { - elt = memory_pool_alloc (req->pool, sizeof (struct rspamd_reply_entry)); - t = dns_parse_rr (in, elt, &pos, rep, &r); - if (t == -1) { - msg_info ("incomplete reply"); - break; - } - else if (t == 1) { - DL_APPEND (rep->entries, elt); - } - } - } - - *_rep = rep; - return TRUE; -} - -static void -dns_throttling_cb (gint fd, short what, void *arg) -{ - struct rspamd_dns_resolver *resolver = arg; - - resolver->throttling = FALSE; - resolver->errors = 0; - msg_info ("stop DNS throttling after %d seconds", (int)resolver->throttling_time.tv_sec); - event_del (&resolver->throttling_event); -} - -static void -dns_check_throttling (struct rspamd_dns_resolver *resolver) -{ - if (resolver->errors > resolver->max_errors && !resolver->throttling) { - msg_info ("starting DNS throttling after %ud errors", resolver->errors); - /* Init throttling timeout */ - resolver->throttling = TRUE; - evtimer_set (&resolver->throttling_event, dns_throttling_cb, resolver); - event_base_set (resolver->ev_base, &resolver->throttling_event); - event_add (&resolver->throttling_event, &resolver->throttling_time); - } -} - -static void -dns_read_cb (gint fd, short what, void *arg) -{ - struct rspamd_dns_resolver *resolver = arg; - struct rspamd_dns_request *req = NULL; - gint r; - struct rspamd_dns_reply *rep; - guint8 in[UDP_PACKET_SIZE]; - - /* This function is called each time when we have data on one of server's sockets */ - - /* First read packet from socket */ - r = read (fd, in, sizeof (in)); - if (r > (gint)(sizeof (struct dns_header) + sizeof (struct dns_query))) { - if (dns_parse_reply (fd, in, r, resolver, &req, &rep)) { - /* Decrease errors count */ - if (rep->request->resolver->errors > 0) { - rep->request->resolver->errors --; - } - upstream_ok (&rep->request->io->srv->up, rep->request->time); - rep->request->func (rep, rep->request->arg); - remove_normal_event (req->session, dns_fin_cb, req); - } - } -} - -static void -dns_timer_cb (gint fd, short what, void *arg) -{ - struct rspamd_dns_request *req = arg; - struct rspamd_dns_reply *rep; - struct rspamd_dns_server *serv; - gint r; - - /* Retransmit dns request */ - req->retransmits ++; - serv = req->io->srv; - if (req->retransmits >= req->resolver->max_retransmits) { - msg_err ("maximum number of retransmits expired for resolving %s of type %s", - req->requested_name, dns_strtype (req->type)); - dns_check_throttling (req->resolver); - req->resolver->errors ++; - goto err; - } - - if (req->sock == -1) { - goto err; - } - /* Add other retransmit event */ - r = send_dns_request (req); - if (r == -1) { - goto err; - } - - msg_debug ("retransmit DNS request with ID %d", (int)req->id); - evtimer_add (&req->timer_event, &req->tv); - - return; -err: - msg_debug ("error on retransmitting DNS request with ID %d", (int)req->id); - rep = memory_pool_alloc0 (req->pool, sizeof (struct rspamd_dns_reply)); - rep->request = req; - rep->code = DNS_RC_SERVFAIL; - if (serv) { - upstream_fail (&serv->up, rep->request->time); - } - req->func (rep, req->arg); - remove_normal_event (req->session, dns_fin_cb, req); - return; -} - -static void -dns_retransmit_handler (gint fd, short what, void *arg) -{ - struct rspamd_dns_request *req = arg; - struct rspamd_dns_reply *rep; - struct rspamd_dns_server *serv; - gint r; - - remove_normal_event (req->session, (event_finalizer_t)event_del, &req->io_event); - serv = req->io->srv; - - if (what == EV_WRITE) { - /* Retransmit dns request */ - req->retransmits ++; - event_del (&req->io_event); - if (req->retransmits >= req->resolver->max_retransmits) { - msg_err ("maximum number of retransmits expired for %s", req->requested_name); - rep = memory_pool_alloc0 (req->pool, sizeof (struct rspamd_dns_reply)); - rep->request = req; - rep->code = DNS_RC_SERVFAIL; - upstream_fail (&serv->up, rep->request->time); - req->resolver->errors ++; - dns_check_throttling (req->resolver); - - req->func (rep, req->arg); - - return; - } - r = send_dns_request (req); - if (r == -1) { - rep = memory_pool_alloc0 (req->pool, sizeof (struct rspamd_dns_reply)); - rep->request = req; - rep->code = DNS_RC_SERVFAIL; - upstream_fail (&serv->up, rep->request->time); - req->func (rep, req->arg); - - } - else if (r == 1) { - /* Add timer event */ - event_del (&req->timer_event); - evtimer_set (&req->timer_event, dns_timer_cb, req); - event_base_set (req->resolver->ev_base, &req->timer_event); - evtimer_add (&req->timer_event, &req->tv); - - /* Add request to hash table */ - g_hash_table_insert (req->io->requests, &req->id, req); - register_async_event (req->session, (event_finalizer_t)dns_fin_cb, - req, g_quark_from_static_string ("dns resolver")); - } - } + remove_normal_event (reqdata->session, rspamd_dns_fin_cb, reqdata->req); } gboolean make_dns_request (struct rspamd_dns_resolver *resolver, - struct rspamd_async_session *session, memory_pool_t *pool, dns_callback_type cb, - gpointer ud, enum rspamd_request_type type, ...) + struct rspamd_async_session *session, memory_pool_t *pool, dns_callback_type cb, + gpointer ud, enum rdns_request_type type, const char *name) { - va_list args; - struct rspamd_dns_request *req; - struct rspamd_dns_server *serv; - struct in_addr *addr; - const gchar *name, *service, *proto; - gint r; - const gint max_id_cycles = 32; - struct dns_header *header; - - /* If no DNS servers defined silently return FALSE */ - if (resolver->servers_num == 0) { - return FALSE; - } - /* Check throttling */ - if (resolver->throttling) { - return FALSE; - } - - req = memory_pool_alloc (pool, sizeof (struct rspamd_dns_request)); - req->pool = pool; - req->session = session; - req->resolver = resolver; - req->func = cb; - req->arg = ud; - req->type = type; - - va_start (args, type); - switch (type) { - case DNS_REQUEST_PTR: - addr = va_arg (args, struct in_addr *); - make_ptr_req (req, addr); - break; - case DNS_REQUEST_MX: - name = va_arg (args, const gchar *); - make_mx_req (req, name); - break; - case DNS_REQUEST_A: - name = va_arg (args, const gchar *); - make_a_req (req, name); - break; - case DNS_REQUEST_AAA: -#ifdef HAVE_INET_PTON - name = va_arg (args, const gchar *); - make_aaa_req (req, name); - break; -#else - msg_err ("your system has no ipv6 support, cannot make aaa request"); - break; -#endif - case DNS_REQUEST_TXT: - name = va_arg (args, const gchar *); - make_txt_req (req, name); - break; - case DNS_REQUEST_SPF: - name = va_arg (args, const gchar *); - make_spf_req (req, name); - break; - case DNS_REQUEST_SRV: - service = va_arg (args, const gchar *); - proto = va_arg (args, const gchar *); - name = va_arg (args, const gchar *); - make_srv_req (req, service, proto, name); - break; - } - va_end (args); - - /* Add EDNS RR */ - rspamd_dns_add_edns0 (req); - - req->retransmits = 0; - req->time = time (NULL); - if (resolver->is_master_slave) { - serv = (struct rspamd_dns_server *)get_upstream_master_slave (resolver->servers, - resolver->servers_num, sizeof (struct rspamd_dns_server), - req->time, DEFAULT_UPSTREAM_ERROR_TIME, DEFAULT_UPSTREAM_DEAD_TIME, DEFAULT_UPSTREAM_MAXERRORS); - } - else { - serv = (struct rspamd_dns_server *)get_upstream_round_robin (resolver->servers, - resolver->servers_num, sizeof (struct rspamd_dns_server), - req->time, DEFAULT_UPSTREAM_ERROR_TIME, DEFAULT_UPSTREAM_DEAD_TIME, DEFAULT_UPSTREAM_MAXERRORS); - } - if (serv == NULL) { - msg_err ("cannot find suitable server for request"); - return FALSE; - } + struct rdns_request *req; + struct rspamd_dns_request_ud *reqdata; - /* Now select IO channel */ + reqdata = memory_pool_alloc (pool, sizeof (struct rspamd_dns_request_ud)); + reqdata->session = session; + reqdata->cb = cb; + reqdata->ud = ud; - req->io = serv->cur_io_channel; - if (req->io == NULL) { - msg_err ("cannot find suitable io channel for the server %s", serv->name); - return FALSE; - } - serv->cur_io_channel = serv->cur_io_channel->next; - req->sock = req->io->sock; + req = rdns_make_request_full (resolver->r, rspamd_dns_callback, reqdata, + resolver->request_timeout, resolver->max_retransmits, name, 1, type); - if (req->sock == -1) { - return FALSE; - } - - /* Fill timeout */ - msec_to_tv (resolver->request_timeout, &req->tv); - evtimer_set (&req->timer_event, dns_timer_cb, req); - event_base_set (req->resolver->ev_base, &req->timer_event); - - /* Now send request to server */ - r = send_dns_request (req); - - if (r == 1) { - /* Add request to hash table */ - r = 0; - while (g_hash_table_lookup (req->io->requests, &req->id)) { - /* Check for unique id */ - header = (struct dns_header *)req->packet; - header->qid = dns_permutor_generate_id (); - req->id = header->qid; - if (++r > max_id_cycles) { - msg_err ("cannot generate new id for server %s", serv->name); - return FALSE; - } - } - /* Add timer event */ - evtimer_add (&req->timer_event, &req->tv); - g_hash_table_insert (req->io->requests, &req->id, req); - register_async_event (session, (event_finalizer_t)dns_fin_cb, req, + if (req != NULL) { + register_async_event (session, (event_finalizer_t)rspamd_dns_fin_cb, req, g_quark_from_static_string ("dns resolver")); + /* Ref event to free it only when according async event is deleted from the session */ + rdns_request_retain (req); + reqdata->req = req; } - else if (r == -1) { - return FALSE; - } - - return TRUE; -} - -static gboolean -parse_resolv_conf (struct rspamd_dns_resolver *resolver) -{ - FILE *r; - gchar buf[BUFSIZ], *p, addr_holder[16]; - struct rspamd_dns_server *new; - - r = fopen (RESOLV_CONF, "r"); - - if (r == NULL) { - msg_err ("cannot open %s: %s", RESOLV_CONF, strerror (errno)); + else { return FALSE; } - - while (! feof (r)) { - if (fgets (buf, sizeof (buf), r)) { - g_strstrip (buf); - if (g_ascii_strncasecmp (buf, "nameserver", sizeof ("nameserver") - 1) == 0) { - p = buf + sizeof ("nameserver"); - while (*p && g_ascii_isspace (*p)) { - p ++; - } - if (! *p) { - msg_warn ("cannot parse empty nameserver line in resolv.conf"); - continue; - } - else { - if (inet_pton (AF_INET6, p, addr_holder) == 1 || - inet_pton (AF_INET, p, addr_holder) == 1) { - new = &resolver->servers[resolver->servers_num]; - new->name = strdup (p); - resolver->servers_num ++; - } - else { - msg_warn ("cannot parse ip address of nameserver: %s", p); - continue; - } - } - } - } - } - fclose (r); return TRUE; } -/* Hashing utilities */ -static gboolean -dns_id_equal (gconstpointer v1, gconstpointer v2) -{ - return *((const guint16*) v1) == *((const guint16*) v2); -} - -static guint -dns_id_hash (gconstpointer v) -{ - return *(const guint16 *) v; -} - struct rspamd_dns_resolver * dns_resolver_init (struct event_base *ev_base, struct config_file *cfg) { GList *cur; struct rspamd_dns_resolver *new; - gchar *begin, *p, *err, addr_holder[16]; - gint priority, i, j; - struct rspamd_dns_server *serv; - struct rspamd_dns_io_channel *ioc; + gchar *begin, *p, *err; + gint priority; new = g_slice_alloc0 (sizeof (struct rspamd_dns_resolver)); new->ev_base = ev_base; - new->io_channels = g_hash_table_new (g_direct_hash, g_direct_equal); new->request_timeout = cfg->dns_timeout; new->max_retransmits = cfg->dns_retransmits; - new->max_errors = cfg->dns_throttling_errors; - msec_to_tv (cfg->dns_throttling_time, &new->throttling_time); + + new->r = rdns_resolver_new (); + rdns_bind_libevent (new->r, new->ev_base); + rdns_resolver_set_log_level (new->r, cfg->log_level); if (cfg->nameservers == NULL) { /* Parse resolv.conf */ - if (! parse_resolv_conf (new) || new->servers_num == 0) { + if (!rdns_resolver_parse_resolv_conf (new->r, "/etc/resolv.conf")) { msg_err ("cannot parse resolv.conf and no nameservers defined, so no ways to resolve addresses"); return new; } @@ -1360,42 +125,15 @@ dns_resolver_init (struct event_base *ev_base, struct config_file *cfg) if (p != NULL) { *p = '\0'; p ++; - if (!new->is_master_slave) { - priority = strtoul (p, &err, 10); - if (err != NULL && *err != '\0') { - if ((*err == 'm' || *err == 'M' || *err == 's' || *err == 'S')) { - new->is_master_slave = TRUE; - } - else { - msg_info ("bad character '%x', must be 'm' or 's' or a numeric priority", *err); - } - } - } - if (new->is_master_slave) { - if (*p == 'm' || *p == 'M') { - priority = 100; - } - else if (*p == 's' || *p == 'S') { - priority = 1; - } - else { - msg_info ("master/slave mode is turned on, and %c character is invalid", *p); - priority = 0; - } + priority = strtoul (p, &err, 10); + if (err != NULL && *err != '\0') { + msg_info ("bad character '%x', must be 'm' or 's' or a numeric priority", *err); } } else { priority = 0; } - serv = &new->servers[new->servers_num]; - if (inet_pton (AF_INET6, begin, addr_holder) == 1 || - inet_pton (AF_INET, begin, addr_holder) == 1) { - serv->name = strdup (begin); - serv->up.priority = priority; - serv->up.weight = priority; - new->servers_num ++; - } - else { + if (!rdns_resolver_add_server (new->r, begin, 53, priority, cfg->dns_io_per_server)) { msg_warn ("cannot parse ip address of nameserver: %s", begin); cur = g_list_next (cur); continue; @@ -1403,56 +141,10 @@ dns_resolver_init (struct event_base *ev_base, struct config_file *cfg) cur = g_list_next (cur); } - if (new->servers_num == 0) { - msg_err ("no valid nameservers defined, try to parse resolv.conf"); - if (! parse_resolv_conf (new) || new->servers_num == 0) { - msg_err ("cannot parse resolv.conf and no nameservers defined, so no ways to resolve addresses"); - return new; - } - } } - /* Now init io channels to all servers */ - for (i = 0; i < new->servers_num; i ++) { - serv = &new->servers[i]; - for (j = 0; j < (gint)cfg->dns_io_per_server; j ++) { - ioc = g_slice_alloc0 (sizeof (struct rspamd_dns_io_channel)); - ioc->sock = make_universal_socket (serv->name, dns_port, SOCK_DGRAM, TRUE, FALSE, FALSE); - if (ioc->sock == -1) { - msg_warn ("cannot create socket to server %s", serv->name); - } - else { - ioc->requests = g_hash_table_new (dns_id_hash, dns_id_equal); - ioc->srv = serv; - ioc->resolver = new; - event_set (&ioc->ev, ioc->sock, EV_READ | EV_PERSIST, dns_read_cb, new); - event_base_set (new->ev_base, &ioc->ev); - event_add (&ioc->ev, NULL); - CDL_PREPEND (serv->io_channels, ioc); - serv->cur_io_channel = ioc; - g_hash_table_insert (new->io_channels, GINT_TO_POINTER (ioc->sock), ioc); - } - } - } - - return new; -} -const gchar * -dns_strerror (enum dns_rcode rcode) -{ - rcode &= 0xf; - static gchar numbuf[16]; + rdns_resolver_init (new->r); - if ('\0' == dns_rcodes[rcode][0]) { - rspamd_snprintf (numbuf, sizeof (numbuf), "UNKNOWN: %d", (gint)rcode); - return numbuf; - } - return dns_rcodes[rcode]; -} - -const gchar * -dns_strtype (enum rspamd_request_type type) -{ - return dns_types[type]; + return new; } diff --git a/src/dns.h b/src/dns.h index ee52ba13c..e39fb9876 100644 --- a/src/dns.h +++ b/src/dns.h @@ -29,102 +29,9 @@ #include "config.h" #include "mem_pool.h" #include "events.h" -#include "upstream.h" - -struct rspamd_dns_reply; -struct config_file; - -typedef void (*dns_callback_type) (struct rspamd_dns_reply *reply, gpointer arg); - -enum rspamd_request_type { - DNS_REQUEST_A = 0, - DNS_REQUEST_PTR, - DNS_REQUEST_MX, - DNS_REQUEST_TXT, - DNS_REQUEST_SRV, - DNS_REQUEST_SPF, - DNS_REQUEST_AAA -}; - -struct rspamd_dns_request { - memory_pool_t *pool; /**< pool associated with request */ - struct rspamd_dns_resolver *resolver; - struct rspamd_dns_io_channel *io; - dns_callback_type func; - gpointer arg; - struct event timer_event; - struct event io_event; - struct timeval tv; - guint retransmits; - guint16 id; - struct rspamd_async_session *session; - struct rspamd_dns_reply *reply; - guint8 *packet; - const gchar *requested_name; - off_t pos; - guint packet_len; - gint sock; - enum rspamd_request_type type; - time_t time; - struct rspamd_dns_request *next; -}; - -union rspamd_reply_element_un { - struct { - struct in_addr addr; - guint16 addrcount; - } a; -#ifdef HAVE_INET_PTON - struct { - struct in6_addr addr; - } aaa; -#endif - struct { - gchar *name; - } ptr; - struct { - gchar *name; - guint16 priority; - } mx; - struct { - gchar *data; - } txt; - struct { - guint16 priority; - guint16 weight; - guint16 port; - gchar *target; - } srv; -}; - -struct rspamd_reply_entry { - union rspamd_reply_element_un content; - guint16 type; - guint16 ttl; - struct rspamd_reply_entry *prev, *next; -}; - - -enum dns_rcode { - DNS_RC_NOERROR = 0, - DNS_RC_FORMERR = 1, - DNS_RC_SERVFAIL = 2, - DNS_RC_NXDOMAIN = 3, - DNS_RC_NOTIMP = 4, - DNS_RC_REFUSED = 5, - DNS_RC_YXDOMAIN = 6, - DNS_RC_YXRRSET = 7, - DNS_RC_NXRRSET = 8, - DNS_RC_NOTAUTH = 9, - DNS_RC_NOTZONE = 10, -}; - -struct rspamd_dns_reply { - struct rspamd_dns_request *request; - enum dns_rcode code; - struct rspamd_reply_entry *entries; -}; +#include "rdns.h" +struct rspamd_dns_resolver; /* Rspamd DNS API */ @@ -145,17 +52,7 @@ struct rspamd_dns_resolver *dns_resolver_init (struct event_base *ev_base, struc * @return TRUE if request was sent. */ gboolean make_dns_request (struct rspamd_dns_resolver *resolver, - struct rspamd_async_session *session, memory_pool_t *pool, dns_callback_type cb, - gpointer ud, enum rspamd_request_type type, ...); - -/** - * Get textual presentation of DNS error code - */ -const gchar *dns_strerror (enum dns_rcode rcode); - -/** - * Get textual presentation of DNS request type - */ -const gchar *dns_strtype (enum rspamd_request_type type); + struct rspamd_async_session *session, memory_pool_t *pool, + dns_callback_type cb, gpointer ud, enum rdns_request_type type, const char *name); #endif diff --git a/src/lua/lua_dns.c b/src/lua/lua_dns.c index 2bfbe582f..b3d8f768f 100644 --- a/src/lua/lua_dns.c +++ b/src/lua/lua_dns.c @@ -66,12 +66,12 @@ struct lua_dns_cbdata { }; static void -lua_dns_callback (struct rspamd_dns_reply *reply, gpointer arg) +lua_dns_callback (struct rdns_reply *reply, gpointer arg) { struct lua_dns_cbdata *cd = arg; gint i = 0; struct rspamd_dns_resolver **presolver; - struct rspamd_reply_entry *elt; + struct rdns_reply_entry *elt; lua_rawgeti (cd->L, LUA_REGISTRYINDEX, cd->cbref); presolver = lua_newuserdata (cd->L, sizeof (gpointer)); @@ -86,13 +86,6 @@ lua_dns_callback (struct rspamd_dns_reply *reply, gpointer arg) if (reply->code == DNS_RC_NOERROR) { lua_newtable (cd->L); LL_FOREACH (reply->entries, elt) { - if (elt->type != reply->request->type) { - /* - * XXX: Skip additional record here to be compatible - * with the existing plugins - */ - continue; - } switch (elt->type) { case DNS_REQUEST_A: lua_ip_push (cd->L, AF_INET, &elt->content.a.addr); @@ -127,7 +120,7 @@ lua_dns_callback (struct rspamd_dns_reply *reply, gpointer arg) } else { lua_pushnil (cd->L); - lua_pushstring (cd->L, dns_strerror (reply->code)); + lua_pushstring (cd->L, rdns_strerror (reply->code)); } if (cd->user_str != NULL) { @@ -179,7 +172,8 @@ lua_dns_resolver_init (lua_State *L) } static int -lua_dns_resolver_resolve_common (lua_State *L, struct rspamd_dns_resolver *resolver, enum rspamd_request_type type) +lua_dns_resolver_resolve_common (lua_State *L, struct rspamd_dns_resolver *resolver, + enum rdns_request_type type) { struct rspamd_async_session *session, **psession; memory_pool_t *pool, **ppool; diff --git a/src/lua/lua_http.c b/src/lua/lua_http.c index 324807005..15fe08e81 100644 --- a/src/lua/lua_http.c +++ b/src/lua/lua_http.c @@ -299,10 +299,10 @@ lua_http_err_cb (GError * err, void *arg) static void -lua_http_dns_callback (struct rspamd_dns_reply *reply, gpointer arg) +lua_http_dns_callback (struct rdns_reply *reply, gpointer arg) { struct lua_http_ud *ud = arg; - struct rspamd_reply_entry *elt; + struct rdns_reply_entry *elt; struct in_addr ina; struct timeval tv; diff --git a/src/lua/lua_redis.c b/src/lua/lua_redis.c index c0b5c7d52..dedb7850c 100644 --- a/src/lua/lua_redis.c +++ b/src/lua/lua_redis.c @@ -240,14 +240,14 @@ lua_redis_make_request_real (struct lua_redis_userdata *ud) * @param arg user data */ static void -lua_redis_dns_callback (struct rspamd_dns_reply *reply, gpointer arg) +lua_redis_dns_callback (struct rdns_reply *reply, gpointer arg) { struct lua_redis_userdata *ud = arg; - struct rspamd_reply_entry *elt; + struct rdns_reply_entry *elt; if (reply->code != DNS_RC_NOERROR) { - lua_redis_push_error (dns_strerror (reply->code), ud, FALSE); + lua_redis_push_error (rdns_strerror (reply->code), ud, FALSE); return; } else { diff --git a/src/plugins/surbl.c b/src/plugins/surbl.c index 062e0b8ac..eebe63c49 100644 --- a/src/plugins/surbl.c +++ b/src/plugins/surbl.c @@ -58,7 +58,7 @@ static struct surbl_ctx *surbl_module_ctx = NULL; static void surbl_test_url (struct worker_task *task, void *user_data); -static void dns_callback (struct rspamd_dns_reply *reply, gpointer arg); +static void dns_callback (struct rdns_reply *reply, gpointer arg); static void process_dns_results (struct worker_task *task, struct suffix_item *suffix, gchar *url, guint32 addr); @@ -628,7 +628,8 @@ make_surbl_requests (struct uri *url, struct worker_task *task, param->suffix = suffix; param->host_resolve = memory_pool_strdup (task->task_pool, surbl_req); debug_task ("send surbl dns request %s", surbl_req); - if (make_dns_request (task->resolver, task->s, task->task_pool, dns_callback, (void *)param, DNS_REQUEST_A, surbl_req)) { + if (make_dns_request (task->resolver, task->s, task->task_pool, dns_callback, + (void *)param, DNS_REQUEST_A, surbl_req)) { task->dns_requests ++; } } @@ -670,23 +671,25 @@ process_dns_results (struct worker_task *task, struct suffix_item *suffix, gchar } static void -dns_callback (struct rspamd_dns_reply *reply, gpointer arg) +dns_callback (struct rdns_reply *reply, gpointer arg) { struct dns_param *param = (struct dns_param *)arg; struct worker_task *task = param->task; - struct rspamd_reply_entry *elt; + struct rdns_reply_entry *elt; debug_task ("in surbl request callback"); /* If we have result from DNS server, this url exists in SURBL, so increase score */ if (reply->code == DNS_RC_NOERROR && reply->entries) { - msg_info ("<%s> domain [%s] is in surbl %s", param->task->message_id, param->host_resolve, param->suffix->suffix); + msg_info ("<%s> domain [%s] is in surbl %s", param->task->message_id, + param->host_resolve, param->suffix->suffix); elt = reply->entries; if (elt->type == DNS_REQUEST_A) { process_dns_results (param->task, param->suffix, param->host_resolve, (guint32)elt->content.a.addr.s_addr); } } else { - debug_task ("<%s> domain [%s] is not in surbl %s", param->task->message_id, param->host_resolve, param->suffix->suffix); + debug_task ("<%s> domain [%s] is not in surbl %s", + param->task->message_id, param->host_resolve, param->suffix->suffix); } } diff --git a/src/rdns b/src/rdns index 9f1eef254..4a9991016 160000 --- a/src/rdns +++ b/src/rdns @@ -1 +1 @@ -Subproject commit 9f1eef254576b6a632c96d7e8117c75b5e4ec0be +Subproject commit 4a9991016a7f820620817f9d3359810fd02ec5ea diff --git a/src/smtp_proxy.c b/src/smtp_proxy.c index 423a9b0bd..c75cb530d 100644 --- a/src/smtp_proxy.c +++ b/src/smtp_proxy.c @@ -476,7 +476,7 @@ create_smtp_proxy_upstream_connection (struct smtp_proxy_session *session) } static void -smtp_dnsbl_cb (struct rspamd_dns_reply *reply, void *arg) +smtp_dnsbl_cb (struct rdns_reply *reply, void *arg) { struct smtp_proxy_session *session = arg; const gchar *p; @@ -484,13 +484,13 @@ smtp_dnsbl_cb (struct rspamd_dns_reply *reply, void *arg) session->rbl_requests --; - msg_debug ("got reply for %s: %s", reply->request->requested_name, dns_strerror (reply->code)); + msg_debug ("got reply for %s: %s", rdns_request_get_name (reply->request), rdns_strerror (reply->code)); if (session->state != SMTP_PROXY_STATE_REJECT) { if (reply->code == DNS_RC_NOERROR) { /* This means that address is in dnsbl */ - p = reply->request->requested_name; + p = rdns_request_get_name (reply->request); while (*p) { if (*p == '.') { dots ++; @@ -648,11 +648,11 @@ smtp_make_delay (struct smtp_proxy_session *session) * Handle DNS replies */ static void -smtp_dns_cb (struct rspamd_dns_reply *reply, void *arg) +smtp_dns_cb (struct rdns_reply *reply, void *arg) { struct smtp_proxy_session *session = arg; gint res = 0; - struct rspamd_reply_entry *elt; + struct rdns_reply_entry *elt; GList *cur; switch (session->state) @@ -662,7 +662,7 @@ smtp_dns_cb (struct rspamd_dns_reply *reply, void *arg) if (reply->code != DNS_RC_NOERROR) { rspamd_conditional_debug (rspamd_main->logger, session->client_addr.s_addr, __FUNCTION__, "DNS error: %s", - dns_strerror (reply->code)); + rdns_strerror (reply->code)); if (reply->code == DNS_RC_NXDOMAIN) { session->hostname = memory_pool_strdup (session->pool, @@ -691,7 +691,7 @@ smtp_dns_cb (struct rspamd_dns_reply *reply, void *arg) if (reply->code != DNS_RC_NOERROR) { rspamd_conditional_debug (rspamd_main->logger, session->client_addr.s_addr, __FUNCTION__, "DNS error: %s", - dns_strerror (reply->code)); + rdns_strerror (reply->code)); if (reply->code == DNS_RC_NXDOMAIN) { session->hostname = memory_pool_strdup (session->pool, @@ -718,7 +718,8 @@ smtp_dns_cb (struct rspamd_dns_reply *reply, void *arg) if (res == 0) { msg_info( - "cannot find address for hostname: %s, ip: %s", session->hostname, inet_ntoa (session->client_addr)); + "cannot find address for hostname: %s, ip: %s", session->hostname, + inet_ntoa (session->client_addr)); session->hostname = memory_pool_strdup (session->pool, XCLIENT_HOST_UNAVAILABLE); } diff --git a/src/spf.c b/src/spf.c index 2d0b5b8eb..e58d44ab8 100644 --- a/src/spf.c +++ b/src/spf.c @@ -387,11 +387,11 @@ parse_spf_hostmask (struct worker_task *task, const gchar *begin, struct spf_add } static void -spf_record_dns_callback (struct rspamd_dns_reply *reply, gpointer arg) +spf_record_dns_callback (struct rdns_reply *reply, gpointer arg) { struct spf_dns_cb *cb = arg; gchar *begin; - struct rspamd_reply_entry *elt_data; + struct rdns_reply_entry *elt_data; GList *tmp = NULL; struct worker_task *task; struct spf_addr *new_addr; @@ -548,13 +548,13 @@ spf_record_dns_callback (struct rspamd_dns_reply *reply, gpointer arg) else if (reply->code == DNS_RC_NXDOMAIN) { switch (cb->cur_action) { case SPF_RESOLVE_MX: - if (reply->request->type == DNS_REQUEST_MX) { + if (rdns_request_has_type (reply->request, DNS_REQUEST_MX)) { msg_info ("<%s>: spf error for domain %s: cannot find MX record for %s", task->message_id, cb->rec->sender_domain, cb->rec->cur_domain); cb->addr->data.normal.d.in4.s_addr = INADDR_NONE; cb->addr->data.normal.mask = 32; } - else if (reply->request->type != DNS_REQUEST_MX) { + else { msg_info ("<%s>: spf error for domain %s: cannot resolve MX record for %s", task->message_id, cb->rec->sender_domain, cb->rec->cur_domain); cb->addr->data.normal.d.in4.s_addr = INADDR_NONE; @@ -562,16 +562,14 @@ spf_record_dns_callback (struct rspamd_dns_reply *reply, gpointer arg) } break; case SPF_RESOLVE_A: - if (reply->request->type == DNS_REQUEST_A) { - /* XXX: process only one record */ + if (rdns_request_has_type (reply->request, DNS_REQUEST_A)) { cb->addr->data.normal.d.in4.s_addr = INADDR_NONE; cb->addr->data.normal.mask = 32; } break; #ifdef HAVE_INET_PTON case SPF_RESOLVE_AAA: - if (reply->request->type == DNS_REQUEST_AAA) { - /* XXX: process only one record */ + if (rdns_request_has_type (reply->request, DNS_REQUEST_AAA)) { memset (&cb->addr->data.normal.d.in6, 0xff, sizeof (struct in6_addr)); cb->addr->data.normal.mask = 32; } @@ -1343,10 +1341,10 @@ start_spf_parse (struct spf_record *rec, gchar *begin, guint ttl) } static void -spf_dns_callback (struct rspamd_dns_reply *reply, gpointer arg) +spf_dns_callback (struct rdns_reply *reply, gpointer arg) { struct spf_record *rec = arg; - struct rspamd_reply_entry *elt; + struct rdns_reply_entry *elt; rec->requests_inflight --; if (reply->code == DNS_RC_NOERROR) { -- cgit v1.2.3