|
|
@@ -326,208 +326,222 @@ rspamd_dns_resolv_conf_on_server (struct rdns_resolver *resolver, |
|
|
|
} |
|
|
|
|
|
|
|
static void |
|
|
|
rspamd_dns_resolver_config_ucl (struct rspamd_config *cfg, |
|
|
|
struct rspamd_dns_resolver *dns_resolver, |
|
|
|
const ucl_object_t *dns_section) |
|
|
|
rspamd_process_fake_reply (struct rspamd_config *cfg, |
|
|
|
struct rspamd_dns_resolver *dns_resolver, |
|
|
|
const ucl_object_t *cur_arr) |
|
|
|
{ |
|
|
|
const ucl_object_t *fake_replies, *cur; |
|
|
|
const ucl_object_t *cur; |
|
|
|
ucl_object_iter_t it; |
|
|
|
|
|
|
|
/* Process fake replies */ |
|
|
|
fake_replies = ucl_object_lookup_any (dns_section, "fake_records", |
|
|
|
"fake_replies", NULL); |
|
|
|
it = ucl_object_iterate_new (cur_arr); |
|
|
|
|
|
|
|
if (fake_replies && ucl_object_type (fake_replies) == UCL_ARRAY) { |
|
|
|
it = ucl_object_iterate_new (fake_replies); |
|
|
|
while ((cur = ucl_object_iterate_safe (it, true))) { |
|
|
|
const ucl_object_t *type_obj, *name_obj, *code_obj, *replies_obj; |
|
|
|
enum rdns_request_type rtype = RDNS_REQUEST_A; |
|
|
|
enum dns_rcode rcode = RDNS_RC_NOERROR; |
|
|
|
struct rdns_reply_entry *replies = NULL; |
|
|
|
const gchar *name = NULL; |
|
|
|
|
|
|
|
if (ucl_object_type (cur) != UCL_OBJECT) { |
|
|
|
continue; |
|
|
|
} |
|
|
|
|
|
|
|
name_obj = ucl_object_lookup (cur, "name"); |
|
|
|
if (name_obj == NULL || |
|
|
|
(name = ucl_object_tostring (name_obj)) == NULL) { |
|
|
|
msg_err_config ("no name for fake dns reply"); |
|
|
|
continue; |
|
|
|
} |
|
|
|
|
|
|
|
while ((cur = ucl_object_iterate_safe (it, true))) { |
|
|
|
const ucl_object_t *type_obj, *name_obj, *code_obj, *replies_obj; |
|
|
|
enum rdns_request_type rtype = RDNS_REQUEST_A; |
|
|
|
enum dns_rcode rcode = RDNS_RC_NOERROR; |
|
|
|
struct rdns_reply_entry *replies = NULL; |
|
|
|
const gchar *name = NULL; |
|
|
|
type_obj = ucl_object_lookup (cur, "type"); |
|
|
|
if (type_obj) { |
|
|
|
rtype = rdns_type_fromstr (ucl_object_tostring (type_obj)); |
|
|
|
|
|
|
|
if (ucl_object_type (cur) != UCL_OBJECT) { |
|
|
|
if (rtype == RDNS_REQUEST_INVALID) { |
|
|
|
msg_err_config ("invalid type for %s: %s", name, |
|
|
|
ucl_object_tostring (type_obj)); |
|
|
|
continue; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
name_obj = ucl_object_lookup (cur, "name"); |
|
|
|
if (name_obj == NULL || |
|
|
|
(name = ucl_object_tostring (name_obj)) == NULL) { |
|
|
|
msg_err_config ("no name for fake dns reply"); |
|
|
|
code_obj = ucl_object_lookup_any (cur, "code", "rcode", NULL); |
|
|
|
if (code_obj) { |
|
|
|
rcode = rdns_rcode_fromstr (ucl_object_tostring (code_obj)); |
|
|
|
|
|
|
|
if (rcode == RDNS_RC_INVALID) { |
|
|
|
msg_err_config ("invalid rcode for %s: %s", name, |
|
|
|
ucl_object_tostring (code_obj)); |
|
|
|
continue; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
type_obj = ucl_object_lookup (cur, "type"); |
|
|
|
if (type_obj) { |
|
|
|
rtype = rdns_type_fromstr (ucl_object_tostring (type_obj)); |
|
|
|
if (rcode == RDNS_RC_NOERROR) { |
|
|
|
/* We want replies to be set for this rcode */ |
|
|
|
replies_obj = ucl_object_lookup (cur, "replies"); |
|
|
|
|
|
|
|
if (rtype == RDNS_REQUEST_INVALID) { |
|
|
|
msg_err_config ("invalid type for %s: %s", name, |
|
|
|
ucl_object_tostring (type_obj)); |
|
|
|
continue; |
|
|
|
} |
|
|
|
if (replies_obj == NULL || ucl_object_type (replies_obj) != UCL_ARRAY) { |
|
|
|
msg_err_config ("invalid replies for fake DNS record %s", name); |
|
|
|
continue; |
|
|
|
} |
|
|
|
|
|
|
|
code_obj = ucl_object_lookup_any (cur, "code", "rcode", NULL); |
|
|
|
if (code_obj) { |
|
|
|
rcode = rdns_rcode_fromstr (ucl_object_tostring (code_obj)); |
|
|
|
ucl_object_iter_t rep_it; |
|
|
|
const ucl_object_t *rep_obj; |
|
|
|
|
|
|
|
if (rcode == RDNS_RC_INVALID) { |
|
|
|
msg_err_config ("invalid rcode for %s: %s", name, |
|
|
|
ucl_object_tostring (code_obj)); |
|
|
|
continue; |
|
|
|
} |
|
|
|
} |
|
|
|
rep_it = ucl_object_iterate_new (replies_obj); |
|
|
|
|
|
|
|
if (rcode == RDNS_RC_NOERROR) { |
|
|
|
/* We want replies to be set for this rcode */ |
|
|
|
replies_obj = ucl_object_lookup (cur, "replies"); |
|
|
|
while ((rep_obj = ucl_object_iterate_safe (rep_it, true))) { |
|
|
|
const gchar *str_rep = ucl_object_tostring (rep_obj); |
|
|
|
struct rdns_reply_entry *rep; |
|
|
|
gchar **svec; |
|
|
|
|
|
|
|
if (replies_obj == NULL || ucl_object_type (replies_obj) != UCL_ARRAY) { |
|
|
|
msg_err_config ("invalid replies for fake DNS record %s", name); |
|
|
|
if (str_rep == NULL) { |
|
|
|
msg_err_config ("invalid reply element for fake DNS record %s", |
|
|
|
name); |
|
|
|
continue; |
|
|
|
} |
|
|
|
|
|
|
|
ucl_object_iter_t rep_it; |
|
|
|
const ucl_object_t *rep_obj; |
|
|
|
|
|
|
|
rep_it = ucl_object_iterate_new (replies_obj); |
|
|
|
rep = calloc (1, sizeof (*rep)); |
|
|
|
g_assert (rep != NULL); |
|
|
|
|
|
|
|
while ((rep_obj = ucl_object_iterate_safe (rep_it, true))) { |
|
|
|
const gchar *str_rep = ucl_object_tostring (rep_obj); |
|
|
|
struct rdns_reply_entry *rep; |
|
|
|
gchar **svec; |
|
|
|
rep->type = rtype; |
|
|
|
rep->ttl = 0; |
|
|
|
|
|
|
|
if (str_rep == NULL) { |
|
|
|
msg_err_config ("invalid reply element for fake DNS record %s", |
|
|
|
name); |
|
|
|
continue; |
|
|
|
switch (rtype) { |
|
|
|
case RDNS_REQUEST_A: |
|
|
|
if (inet_pton (AF_INET, str_rep, &rep->content.a.addr) != 1) { |
|
|
|
msg_err_config ("invalid A reply element for fake " |
|
|
|
"DNS record %s: %s", |
|
|
|
name, str_rep); |
|
|
|
free (rep); |
|
|
|
} |
|
|
|
|
|
|
|
rep = calloc (1, sizeof (*rep)); |
|
|
|
g_assert (rep != NULL); |
|
|
|
|
|
|
|
rep->type = rtype; |
|
|
|
rep->ttl = 0; |
|
|
|
|
|
|
|
switch (rtype) { |
|
|
|
case RDNS_REQUEST_A: |
|
|
|
if (inet_pton (AF_INET, str_rep, &rep->content.a.addr) != 1) { |
|
|
|
msg_err_config ("invalid A reply element for fake " |
|
|
|
"DNS record %s: %s", |
|
|
|
name, str_rep); |
|
|
|
free (rep); |
|
|
|
} |
|
|
|
else { |
|
|
|
DL_APPEND (replies, rep); |
|
|
|
} |
|
|
|
break; |
|
|
|
case RDNS_REQUEST_NS: |
|
|
|
rep->content.ns.name = strdup (str_rep); |
|
|
|
else { |
|
|
|
DL_APPEND (replies, rep); |
|
|
|
break; |
|
|
|
case RDNS_REQUEST_PTR: |
|
|
|
rep->content.ptr.name = strdup (str_rep); |
|
|
|
} |
|
|
|
break; |
|
|
|
case RDNS_REQUEST_NS: |
|
|
|
rep->content.ns.name = strdup (str_rep); |
|
|
|
DL_APPEND (replies, rep); |
|
|
|
break; |
|
|
|
case RDNS_REQUEST_PTR: |
|
|
|
rep->content.ptr.name = strdup (str_rep); |
|
|
|
DL_APPEND (replies, rep); |
|
|
|
break; |
|
|
|
case RDNS_REQUEST_MX: |
|
|
|
svec = g_strsplit_set (str_rep, " :", -1); |
|
|
|
|
|
|
|
if (svec && svec[0] && svec[1]) { |
|
|
|
rep->content.mx.priority = strtoul (svec[0], NULL, 10); |
|
|
|
rep->content.mx.name = strdup (svec[1]); |
|
|
|
DL_APPEND (replies, rep); |
|
|
|
break; |
|
|
|
case RDNS_REQUEST_MX: |
|
|
|
svec = g_strsplit_set (str_rep, " :", -1); |
|
|
|
|
|
|
|
if (svec && svec[0] && svec[1]) { |
|
|
|
rep->content.mx.priority = strtoul (svec[0], NULL, 10); |
|
|
|
rep->content.mx.name = strdup (svec[1]); |
|
|
|
DL_APPEND (replies, rep); |
|
|
|
} |
|
|
|
else { |
|
|
|
msg_err_config ("invalid MX reply element for fake " |
|
|
|
"DNS record %s: %s", |
|
|
|
name, str_rep); |
|
|
|
free (rep); |
|
|
|
} |
|
|
|
|
|
|
|
g_strfreev (svec); |
|
|
|
break; |
|
|
|
case RDNS_REQUEST_TXT: |
|
|
|
rep->content.txt.data = strdup (str_rep); |
|
|
|
} |
|
|
|
else { |
|
|
|
msg_err_config ("invalid MX reply element for fake " |
|
|
|
"DNS record %s: %s", |
|
|
|
name, str_rep); |
|
|
|
free (rep); |
|
|
|
} |
|
|
|
|
|
|
|
g_strfreev (svec); |
|
|
|
break; |
|
|
|
case RDNS_REQUEST_TXT: |
|
|
|
rep->content.txt.data = strdup (str_rep); |
|
|
|
DL_APPEND (replies, rep); |
|
|
|
break; |
|
|
|
case RDNS_REQUEST_SOA: |
|
|
|
svec = g_strsplit_set (str_rep, " :", -1); |
|
|
|
|
|
|
|
/* 7 elements */ |
|
|
|
if (svec && svec[0] && svec[1] && svec[2] && |
|
|
|
svec[3] && svec[4] && svec[5] && svec[6]) { |
|
|
|
rep->content.soa.mname = strdup (svec[0]); |
|
|
|
rep->content.soa.admin = strdup (svec[1]); |
|
|
|
rep->content.soa.serial = strtoul (svec[2], NULL, 10); |
|
|
|
rep->content.soa.refresh = strtol (svec[3], NULL, 10); |
|
|
|
rep->content.soa.retry = strtol (svec[4], NULL, 10); |
|
|
|
rep->content.soa.expire = strtol (svec[5], NULL, 10); |
|
|
|
rep->content.soa.minimum = strtoul (svec[6], NULL, 10); |
|
|
|
DL_APPEND (replies, rep); |
|
|
|
break; |
|
|
|
case RDNS_REQUEST_SOA: |
|
|
|
svec = g_strsplit_set (str_rep, " :", -1); |
|
|
|
|
|
|
|
/* 7 elements */ |
|
|
|
if (svec && svec[0] && svec[1] && svec[2] && |
|
|
|
svec[3] && svec[4] && svec[5] && svec[6]) { |
|
|
|
rep->content.soa.mname = strdup (svec[0]); |
|
|
|
rep->content.soa.admin = strdup (svec[1]); |
|
|
|
rep->content.soa.serial = strtoul (svec[2], NULL, 10); |
|
|
|
rep->content.soa.refresh = strtol (svec[3], NULL, 10); |
|
|
|
rep->content.soa.retry = strtol (svec[4], NULL, 10); |
|
|
|
rep->content.soa.expire = strtol (svec[5], NULL, 10); |
|
|
|
rep->content.soa.minimum = strtoul (svec[6], NULL, 10); |
|
|
|
DL_APPEND (replies, rep); |
|
|
|
} |
|
|
|
else { |
|
|
|
msg_err_config ("invalid MX reply element for fake " |
|
|
|
"DNS record %s: %s", |
|
|
|
name, str_rep); |
|
|
|
free (rep); |
|
|
|
} |
|
|
|
|
|
|
|
g_strfreev (svec); |
|
|
|
break; |
|
|
|
case RDNS_REQUEST_AAAA: |
|
|
|
if (inet_pton (AF_INET6, str_rep, &rep->content.aaa.addr) != 1) { |
|
|
|
msg_err_config ("invalid AAAA reply element for fake " |
|
|
|
"DNS record %s: %s", |
|
|
|
name, str_rep); |
|
|
|
free (rep); |
|
|
|
} |
|
|
|
else { |
|
|
|
DL_APPEND (replies, rep); |
|
|
|
} |
|
|
|
case RDNS_REQUEST_SRV: |
|
|
|
default: |
|
|
|
msg_err_config ("invalid or unsupported reply element " |
|
|
|
"for fake DNS record %s(%s): %s", |
|
|
|
name, rdns_str_from_type (rtype), str_rep); |
|
|
|
} |
|
|
|
else { |
|
|
|
msg_err_config ("invalid MX reply element for fake " |
|
|
|
"DNS record %s: %s", |
|
|
|
name, str_rep); |
|
|
|
free (rep); |
|
|
|
} |
|
|
|
|
|
|
|
g_strfreev (svec); |
|
|
|
break; |
|
|
|
case RDNS_REQUEST_AAAA: |
|
|
|
if (inet_pton (AF_INET6, str_rep, &rep->content.aaa.addr) != 1) { |
|
|
|
msg_err_config ("invalid AAAA reply element for fake " |
|
|
|
"DNS record %s: %s", |
|
|
|
name, str_rep); |
|
|
|
free (rep); |
|
|
|
break; |
|
|
|
} |
|
|
|
else { |
|
|
|
DL_APPEND (replies, rep); |
|
|
|
} |
|
|
|
case RDNS_REQUEST_SRV: |
|
|
|
default: |
|
|
|
msg_err_config ("invalid or unsupported reply element " |
|
|
|
"for fake DNS record %s(%s): %s", |
|
|
|
name, rdns_str_from_type (rtype), str_rep); |
|
|
|
free (rep); |
|
|
|
break; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
ucl_object_iterate_free (rep_it); |
|
|
|
ucl_object_iterate_free (rep_it); |
|
|
|
|
|
|
|
if (replies) { |
|
|
|
struct rdns_reply_entry *tmp_entry; |
|
|
|
guint i = 0; |
|
|
|
DL_COUNT (replies, tmp_entry, i); |
|
|
|
if (replies) { |
|
|
|
struct rdns_reply_entry *tmp_entry; |
|
|
|
guint i = 0; |
|
|
|
DL_COUNT (replies, tmp_entry, i); |
|
|
|
|
|
|
|
msg_info_config ("added fake record: %s(%s); %d replies", name, |
|
|
|
rdns_str_from_type (rtype), i); |
|
|
|
rdns_resolver_set_fake_reply (dns_resolver->r, |
|
|
|
name, rtype, rcode, replies); |
|
|
|
} |
|
|
|
else { |
|
|
|
msg_warn_config ("record %s has no replies, not adding", |
|
|
|
name); |
|
|
|
} |
|
|
|
msg_info_config ("added fake record: %s(%s); %d replies", name, |
|
|
|
rdns_str_from_type (rtype), i); |
|
|
|
rdns_resolver_set_fake_reply (dns_resolver->r, |
|
|
|
name, rtype, rcode, replies); |
|
|
|
} |
|
|
|
else { |
|
|
|
/* This entry returns some non valid code, no replies are possible */ |
|
|
|
replies_obj = ucl_object_lookup (cur, "replies"); |
|
|
|
|
|
|
|
if (replies_obj) { |
|
|
|
msg_warn_config ("replies are set for non-successful return " |
|
|
|
"code for %s(%s), they will be ignored", name, rdns_str_from_type (rtype)); |
|
|
|
} |
|
|
|
msg_warn_config ("record %s has no replies, not adding", |
|
|
|
name); |
|
|
|
} |
|
|
|
} |
|
|
|
else { |
|
|
|
/* This entry returns some non valid code, no replies are possible */ |
|
|
|
replies_obj = ucl_object_lookup (cur, "replies"); |
|
|
|
|
|
|
|
rdns_resolver_set_fake_reply (dns_resolver->r, |
|
|
|
name, rtype, rcode, NULL); |
|
|
|
if (replies_obj) { |
|
|
|
msg_warn_config ("replies are set for non-successful return " |
|
|
|
"code for %s(%s), they will be ignored", name, rdns_str_from_type (rtype)); |
|
|
|
} |
|
|
|
|
|
|
|
rdns_resolver_set_fake_reply (dns_resolver->r, |
|
|
|
name, rtype, rcode, NULL); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
ucl_object_iterate_free (it); |
|
|
|
ucl_object_iterate_free (it); |
|
|
|
} |
|
|
|
|
|
|
|
static void |
|
|
|
rspamd_dns_resolver_config_ucl (struct rspamd_config *cfg, |
|
|
|
struct rspamd_dns_resolver *dns_resolver, |
|
|
|
const ucl_object_t *dns_section) |
|
|
|
{ |
|
|
|
const ucl_object_t *fake_replies; |
|
|
|
|
|
|
|
/* Process fake replies */ |
|
|
|
fake_replies = ucl_object_lookup_any (dns_section, "fake_records", |
|
|
|
"fake_replies", NULL); |
|
|
|
|
|
|
|
if (fake_replies && ucl_object_type (fake_replies) == UCL_ARRAY) { |
|
|
|
const ucl_object_t *cur_arr; |
|
|
|
|
|
|
|
DL_FOREACH (fake_replies, cur_arr) { |
|
|
|
rspamd_process_fake_reply (cfg, dns_resolver, cur_arr); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
@@ -604,15 +618,19 @@ rspamd_dns_resolver_init (rspamd_logger_t *logger, |
|
|
|
|
|
|
|
if (cfg->rcl_obj) { |
|
|
|
/* Configure additional options */ |
|
|
|
const ucl_object_t *opts_section, *dns_section; |
|
|
|
const ucl_object_t *opts_section, *dns_section, *tmp; |
|
|
|
|
|
|
|
opts_section = ucl_object_lookup (cfg->rcl_obj, "options"); |
|
|
|
|
|
|
|
if (opts_section) { |
|
|
|
dns_section = ucl_object_lookup (opts_section, "dns"); |
|
|
|
/* TODO: implement a more simple merge logic */ |
|
|
|
DL_FOREACH (opts_section, tmp) { |
|
|
|
dns_section = ucl_object_lookup (opts_section, "dns"); |
|
|
|
|
|
|
|
if (dns_section) { |
|
|
|
rspamd_dns_resolver_config_ucl (cfg, dns_resolver, dns_section); |
|
|
|
if (dns_section) { |
|
|
|
rspamd_dns_resolver_config_ucl (cfg, dns_resolver, |
|
|
|
dns_section); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |