diff options
-rw-r--r-- | lib/dns.c | 264 | ||||
-rw-r--r-- | lib/dns.h | 10 | ||||
-rw-r--r-- | lib/libmf.h | 2 | ||||
-rw-r--r-- | src/builtin/dns.bi | 67 | ||||
-rw-r--r-- | src/main.c | 9 | ||||
-rw-r--r-- | tests/Makefile.am | 6 | ||||
-rw-r--r-- | tests/atlocal.in | 15 | ||||
-rw-r--r-- | tests/hasmx.at | 2 | ||||
-rw-r--r-- | tests/hostname.at | 13 | ||||
-rw-r--r-- | tests/ismx.at | 3 | ||||
-rw-r--r-- | tests/rescname.at | 11 | ||||
-rw-r--r-- | tests/resolv.c | 121 | ||||
-rw-r--r-- | tests/resolv_a.at | 38 | ||||
-rw-r--r-- | tests/resolv_mx.at | 30 | ||||
-rw-r--r-- | tests/resolv_ptr.at | 33 | ||||
-rw-r--r-- | tests/resolv_ptr_val.at | 29 | ||||
-rw-r--r-- | tests/resolv_spf.at | 27 | ||||
-rw-r--r-- | tests/resolv_txt.at | 28 | ||||
-rw-r--r-- | tests/resolve.at | 11 | ||||
-rw-r--r-- | tests/testsuite.at | 11 |
20 files changed, 566 insertions, 164 deletions
@@ -31,10 +31,8 @@ #include "mailutils/io.h" #include "mailutils/stream.h" -struct mx_buffer { - unsigned pref; - char *name; -}; +#define DEFAULT_QFLAGS \ + (adns_qf_quoteok_cname|adns_qf_cname_loose) static mu_debug_handle_t debug_handle; static adns_state state; @@ -63,30 +61,153 @@ dnsbase_init(void) } void -dnsbase_real_init(void) +dnsbase_real_init(char *configtext) { int rc; int flags; mu_debug_level_t lev; flags = adns_if_nosigpipe; - if (mu_debug_get_category_level(debug_handle, &lev) == 0 && lev) + if (mu_debug_get_category_level(debug_handle, &lev) == 0 + && (lev & MU_DEBUG_LEVEL_MASK(MU_DEBUG_TRACE9))) flags |= adns_if_debug; - rc = adns_init_logfn(&state, flags, NULL, dns_log_cb, NULL); + rc = adns_init_logfn(&state, flags, configtext, dns_log_cb, NULL); if (rc) { mu_diag_funcall(MU_DIAG_ERROR, "adns_init", NULL, rc); exit(1); } } +void +dnsbase_file_init(char *filename) +{ + if (!filename) + dnsbase_real_init(NULL); + else { + mu_stream_t str; + mu_off_t sz; + int rc; + char *cfg; + + rc = mu_file_stream_create(&str, filename, MU_STREAM_READ); + if (rc) { + mu_diag_funcall(MU_DIAG_ERROR, "mu_file_stream_create", + filename, rc); + return; + } + rc = mu_stream_size(str, &sz); + if (rc) { + mu_diag_funcall(MU_DIAG_ERROR, "mu_stream_size", + filename, rc); + mu_stream_destroy(&str); + return; + } + + if (sz > ((size_t)~0)) { + mu_error(_("%s too big"), filename); + mu_stream_destroy(&str); + return; + } + + cfg = mu_alloc(sz + 1); + + rc = mu_stream_read(str, cfg, sz, NULL); + mu_stream_destroy(&str); + if (rc) { + mu_diag_funcall(MU_DIAG_ERROR, "mu_stream_read", + filename, rc); + return; + } + cfg[sz] = 0; + dnsbase_real_init(cfg); + free(cfg); + } +} + static adns_state get_state(void) { if (!state) - dnsbase_real_init(); + dnsbase_real_init(NULL); return state; } - + +static inline size_t +dns_reply_elsize(struct dns_reply *reply) +{ + switch (reply->type) { + case dns_reply_ip: + return sizeof(reply->data.ip[0]); + case dns_reply_str: + return sizeof(reply->data.str[0]); + } + abort(); +} + +void +dns_reply_init(struct dns_reply *reply, dns_reply_type type, size_t count) +{ + reply->type = type; + reply->count = count; + reply->maxcount = count; + if (count) + reply->data.ptr = mu_calloc(count, dns_reply_elsize(reply)); + else + reply->data.ptr = NULL; +} + +void +dns_reply_ip_push(struct dns_reply *reply, void *item) +{ + if (reply->count == reply->maxcount) + reply->data.ip = mu_2nrealloc(reply->data.ip, + &reply->maxcount, + sizeof(reply->data.ip[0])); + reply->data.ip[reply->count++] = *(GACOPYZ_UINT32_T*)item; +} + +void +dns_reply_str_push(struct dns_reply *reply, void *item) +{ + if (reply->count == reply->maxcount) + reply->data.str = mu_2nrealloc(reply->data.ip, + &reply->maxcount, + sizeof(reply->data.str[0])); + reply->data.str[reply->count++] = item; +} + +void +dns_reply_push(struct dns_reply *reply, void *item) +{ + switch (reply->type) { + case dns_reply_ip: + dns_reply_ip_push(reply, item); + break; + case dns_reply_str: + dns_reply_str_push(reply, item); + break; + default: + abort(); + } +} + +void +dns_reply_free(struct dns_reply *reply) +{ + int i; + + switch (reply->type) { + case dns_reply_str: + for (i = 0; i < reply->count; i++) + free(reply->data.str[i]); + free(reply->data.str); + break; + case dns_reply_ip: + free(reply->data.ip); + break; + } +} + int dns_str_is_ipv4(const char *addr) { @@ -144,35 +265,53 @@ adns_to_dns_status(int e) } } +dns_status +soa_check(const char *name, int ip, struct dns_reply *reply) +{ + dns_status status = dns_failure; + int rc; + adns_answer *ans; + + rc = adns_synchronous(get_state(), name, adns_r_soa_raw, + DEFAULT_QFLAGS, + &ans); + if (rc) + return errno_to_dns_status(rc); + status = adns_to_dns_status(ans->status); + if (status == dns_success) { + if (ip) { + status = a_lookup(ans->rrs.soa->mname, reply); + } else { + dns_reply_init(reply, dns_reply_str, 1); + reply->data.str[0] = mu_strdup (ans->rrs.soa->mname); + } + free(ans); + } + return status; +} + static dns_status dns_reply_resolve(struct dns_reply *reply) { - GACOPYZ_UINT32_T *ipbuf = NULL; - size_t ipcount = 0; - size_t ipmax = 0; size_t i; - - ipcount = 0; + struct dns_reply res; + + dns_reply_init(&res, dns_reply_ip, 0); for (i = 0; i < reply->count; i++) { struct dns_reply r; dns_status stat = a_lookup(reply->data.str[i], &r); if (stat == dns_success) { size_t n; - for (n = 0; n < r.count; n++, ipcount++) { - if (ipcount == ipmax) - ipbuf = mu_2nrealloc(ipbuf, &ipmax, - sizeof(ipbuf)); - ipbuf[ipcount] = r.data.ip[n]; + for (n = 0; n < r.count; n++) { + dns_reply_push(&res, &r.data.ip[n]); } dns_reply_free(&r); } } dns_reply_free(reply); - if (ipcount == 0) + *reply = res; + if (res.count == 0) return dns_not_found; - reply->type = dns_reply_ip; - reply->count = ipcount; - reply->data.ip = ipbuf; return dns_success; } @@ -186,7 +325,7 @@ mx_lookup(const char *host, int resolve, struct dns_reply *reply) int i; rc = adns_synchronous(get_state(), host, adns_r_mx, - adns_qf_quoteok_cname|adns_qf_cname_loose, + DEFAULT_QFLAGS, &ans); if (rc) return errno_to_dns_status(rc); @@ -194,10 +333,7 @@ mx_lookup(const char *host, int resolve, struct dns_reply *reply) if (status != dns_success) return status; - reply->type = dns_reply_str; - reply->count = ans->nrrs; - reply->data.str = mu_calloc(reply->count, - sizeof(reply->data.str[0])); + dns_reply_init(reply, dns_reply_str, ans->nrrs); for (i = 0; i < ans->nrrs; i++) reply->data.str[i] = mu_strdup(ans->rrs.inthostaddr[i].ha.host); free(ans); @@ -207,23 +343,6 @@ mx_lookup(const char *host, int resolve, struct dns_reply *reply) return status; } - -void -dns_reply_free(struct dns_reply *reply) -{ - int i; - - switch (reply->type) { - case dns_reply_str: - for (i = 0; i < reply->count; i++) - free(reply->data.str[i]); - free(reply->data.str); - break; - case dns_reply_ip: - free(reply->data.ip); - break; - } -} typedef char IPBUF[3*4+3+1]; @@ -272,8 +391,8 @@ dns_resolve_ipstr(const char *ipstr, const char *domain, char **hbuf) mu_asprintf(&name, "%s.%s", ipstr, domain); } - rc = adns_synchronous(get_state(), name, adns_r_ptr, - adns_qf_quoteok_cname|adns_qf_cname_loose, + rc = adns_synchronous(get_state(), name, adns_r_ptr_raw, + DEFAULT_QFLAGS, &ans); free(name); if (rc) @@ -293,7 +412,7 @@ dns_resolve_hostname(const char *host, char **ipbuf) adns_answer *ans; rc = adns_synchronous(get_state(), host, adns_r_a, - adns_qf_quoteok_cname|adns_qf_cname_loose, + DEFAULT_QFLAGS, &ans); if (rc) return errno_to_dns_status(rc); @@ -313,17 +432,14 @@ a_lookup(const char *host, struct dns_reply *reply) adns_answer *ans; rc = adns_synchronous(get_state(), host, adns_r_a, - adns_qf_quoteok_cname|adns_qf_cname_loose, + DEFAULT_QFLAGS, &ans); if (rc) return errno_to_dns_status(rc); status = adns_to_dns_status(ans->status); if (status == dns_success) { int i; - reply->type = dns_reply_ip; - reply->count = ans->nrrs; - reply->data.ip = mu_calloc(reply->count, - sizeof(reply->data.ip[0])); + dns_reply_init(reply, dns_reply_ip, ans->nrrs); for (i = 0; i < ans->nrrs; i++) reply->data.ip[i] = ans->rrs.inaddr[i].s_addr; } @@ -342,7 +458,7 @@ ptr_lookup(struct in_addr ip, struct dns_reply *reply) ip.s_addr = ntohl(ip.s_addr); mu_asprintf(&name, "%s.in-addr.arpa", inet_ntoa(ip)); rc = adns_synchronous(get_state(), name, adns_r_ptr_raw, - adns_qf_quoteok_cname|adns_qf_cname_loose, + DEFAULT_QFLAGS, &ans); free(name); if (rc) @@ -350,10 +466,7 @@ ptr_lookup(struct in_addr ip, struct dns_reply *reply) status = adns_to_dns_status(ans->status); if (status == dns_success) { int i; - reply->type = dns_reply_str; - reply->count = ans->nrrs; - reply->data.str = mu_calloc(reply->count, - sizeof(reply->data.str[0])); + dns_reply_init(reply, dns_reply_str, ans->nrrs); for (i = 0; i < ans->nrrs; i++) reply->data.str[i] = mu_strdup(ans->rrs.str[i]); } @@ -369,17 +482,14 @@ txt_lookup(const char *name, struct dns_reply *reply) adns_answer *ans; rc = adns_synchronous(get_state(), name, adns_r_txt, - adns_qf_quoteok_cname|adns_qf_cname_loose, + DEFAULT_QFLAGS, &ans); if (rc) return errno_to_dns_status(rc); status = adns_to_dns_status(ans->status); if (status == dns_success) { int i; - reply->type = dns_reply_str; - reply->count = ans->nrrs; - reply->data.str = mu_calloc(reply->count, - sizeof(reply->data.str[0])); + dns_reply_init(reply, dns_reply_str, ans->nrrs); for (i = 0; i < ans->nrrs; i++) reply->data.str[i] = mu_strdup(ans->rrs.manyistr[i]->str); } @@ -419,11 +529,9 @@ ptr_validate(const char *ipstr, struct dns_reply *reply) { struct in_addr ip; size_t i; - int status; + dns_status status; struct dns_reply ptr_reply; - char **sv = NULL; - size_t sc = 0; - size_t sn = 0; + dns_status result = dns_not_found; if (!inet_aton(ipstr, &ip)) return dns_failure; @@ -433,6 +541,8 @@ ptr_validate(const char *ipstr, struct dns_reply *reply) if (status != dns_success) return status; + if (reply) + dns_reply_init(reply, dns_reply_str, 0); for (i = 0; i < ptr_reply.count; i++) { struct dns_reply r; status = a_lookup(ptr_reply.data.str[i], &r); @@ -441,10 +551,10 @@ ptr_validate(const char *ipstr, struct dns_reply *reply) for (k = 0; k < r.count; k++) { if (r.data.ip[k] == ip.s_addr) { - if (sc == sn) - sv = mu_2nrealloc(sv, &sn, - sizeof(sv[0])); - sv[sc++] = mu_strdup(ptr_reply.data.str[i]); + result = dns_success; + if (reply) + dns_reply_push(reply, + mu_strdup(ptr_reply.data.str[i])); break; } } @@ -453,17 +563,7 @@ ptr_validate(const char *ipstr, struct dns_reply *reply) } dns_reply_free(&ptr_reply); - if (sc == 0) { - return dns_not_found; - } - if (reply) { - reply->type = dns_reply_str; - reply->count = sc; - reply->data.str = sv; - } else - mu_argcv_free(sc, sv); - - return dns_success; + return result; } @@ -32,14 +32,20 @@ typedef enum { } dns_reply_type; struct dns_reply { - int count; dns_reply_type type; + int count; + size_t maxcount; union { char **str; GACOPYZ_UINT32_T *ip; + void *ptr; } data; }; +void dnsbase_real_init(char *configtext); +void dnsbase_file_init(char *file); + +void dns_reply_init(struct dns_reply *reply, dns_reply_type type, size_t count); void dns_reply_free(struct dns_reply *r); int dns_str_is_ipv4(const char *addr); @@ -51,6 +57,8 @@ dns_status dns_resolve_ipstr(const char *ipstr, const char *domain, dns_status dns_resolve_hostname(const char *host, char **ipbuf); +dns_status soa_check(const char *name, int ip, struct dns_reply *repl); + dns_status a_lookup(const char *host, struct dns_reply *repl); dns_status ptr_lookup(struct in_addr ip, struct dns_reply *repl); diff --git a/lib/libmf.h b/lib/libmf.h index ebfd0e3e..b2c88801 100644 --- a/lib/libmf.h +++ b/lib/libmf.h @@ -279,7 +279,7 @@ size_t format_time_str(FILE *fp, time_t timestamp); /* dns.c */ -void dnsbase_init(); +void dnsbase_init(void); mf_status dns_to_mf_status(dns_status stat); dns_status mf_to_dns_status(mf_status stat); mf_status resolve_ipstr_domain(const char *ipstr, const char *domain, char **phbuf); diff --git a/src/builtin/dns.bi b/src/builtin/dns.bi index 336d8f40..0b7bfdbc 100644 --- a/src/builtin/dns.bi +++ b/src/builtin/dns.bi @@ -203,59 +203,52 @@ MF_DEFUN(getmx, STRING, STRING domain, OPTIONAL, NUMBER resolve) } END -static int -resolve_host(const char *string, unsigned long *ip) +static dns_status +resolve_host(const char *string, struct dns_reply *reply) { - int rc; struct in_addr addr; - char *ipstr; if (inet_aton(string, &addr)) { - *ip = addr.s_addr; - return 0; + dns_reply_init(reply, dns_reply_ip, 1); + reply->data.ip[0] = addr.s_addr; + return dns_success; } - - if (resolve_hostname(string, &ipstr) != mf_success) - return 1; + return a_lookup(string, reply); +} - rc = inet_aton(ipstr, &addr); - free(ipstr); - if (rc == 0) { - mu_error(_("INTERNAL ERROR at %s:%d: resolve_hostname returned " - "invalid IP address: %s"), - __FILE__, __LINE__, ipstr); - return 1; +static int +dns_replies_intersect(struct dns_reply const *a, struct dns_reply const *b) +{ + int i, j; + + if (a->type == b->type && a->type == dns_reply_ip) { + for (i = 0; i < a->count; i++) + for (j = 0; j < b->count; j++) + if (a->data.ip[i] == b->data.ip[j]) + return 1; } - *ip = addr.s_addr; return 0; } MF_DEFUN(primitive_ismx, NUMBER, STRING domain, STRING ipstr) { - struct dns_reply reply; - mf_status mxstat; - unsigned long ip; + struct dns_reply areply, mxreply; + dns_status status; int rc = 0; - int i; - - MF_ASSERT(resolve_host(ipstr, &ip) == 0, mfe_noresolve, - _("cannot resolve host name %s"), ipstr); - ip = ntohl(ip); - - mxstat = dns_to_mf_status(mx_lookup(domain, 1, &reply)); - if (mxstat != mf_success) { - MF_THROW(mf_status_to_exception(mxstat), + status = resolve_host(ipstr, &areply); + MF_ASSERT(status == dns_success, + mf_status_to_exception(dns_to_mf_status(status)), + _("cannot resolve host name %s"), ipstr); + status = mx_lookup(domain, 1, &mxreply); + if (status != dns_success) { + dns_reply_free(&areply); + MF_THROW(mf_status_to_exception(dns_to_mf_status(status)), _("cannot get MXs for %s"), domain); } - - for (i = 0; i < reply.count; i++) { - if (reply.data.ip[i] == ip) { - rc = 1; - break; - } - } - dns_reply_free(&reply); + rc = dns_replies_intersect(&areply, &mxreply); + dns_reply_free(&areply); + dns_reply_free(&mxreply); MF_RETURN(rc); } END @@ -82,6 +82,8 @@ mu_stream_t mf_strecho; /* Output stream for 'echo' statements */ static int trace_option = ARG_UNSET; static mu_list_t trace_modules; + +static char *resolv_conf_file; /* Preprocessor helper function */ static void @@ -563,6 +565,9 @@ static struct mu_option mailfromd_options[] = { { "relayed-domain-file", 0, N_("FILE"), MU_OPTION_DEFAULT, N_("read relayed domains from FILE"), mu_c_string, NULL, opt_relayed_domain_file }, + { "resolv-conf-file", 0, N_("FILE"), MU_OPTION_DEFAULT, + N_("read resolver configuration from FILE"), + mu_c_string, &resolv_conf_file }, MU_OPTION_GROUP(N_("Preprocessor options")), { "preprocessor", 0, N_("COMMAND"), MU_OPTION_DEFAULT, @@ -1269,7 +1274,9 @@ main(int argc, char **argv) exit(EX_USAGE); init_relayed_domains(); - + if (resolv_conf_file) + dnsbase_file_init(resolv_conf_file); + if (trace_option != ARG_UNSET) do_trace = trace_option; if (trace_modules) { diff --git a/tests/Makefile.am b/tests/Makefile.am index 2f840fc3..672865fd 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -115,6 +115,12 @@ TESTSUITE_AT = \ relayed02.at\ rescname.at\ resolve.at\ + resolv_a.at\ + resolv_ptr.at\ + resolv_txt.at\ + resolv_mx.at\ + resolv_spf.at\ + resolv_ptr_val.at\ rset.at\ shadow.at\ shellmagic.at\ diff --git a/tests/atlocal.in b/tests/atlocal.in index 0bbe0350..d015213e 100644 --- a/tests/atlocal.in +++ b/tests/atlocal.in @@ -57,3 +57,18 @@ puszcza_addr() { host puszcza.gnu.org.ua |\ sed -n '1s/puszcza.gnu.org.ua has address \(.*\)/\1/p' } + +: ${MF_TOPDOMAIN:=mf.gnu.org.ua} +: ${MF_NAMESERVER:=$(resolv -qq soa -ip $MF_TOPDOMAIN)} +export MF_TOPDOMAIN MF_NAMESERVER + +at_resolv_conf() { + if [ -n "$MF_NAMESERVER" ]; then + cat > resolv.conf <<EOF +nameserver $MF_NAMESERVER +EOF + MFOPTS="$MFOPTS --resolv-conf=resolv.conf" + fi +} + +export RES_CONF_TEXT diff --git a/tests/hasmx.at b/tests/hasmx.at index d633ac3f..06df6b9c 100644 --- a/tests/hasmx.at +++ b/tests/hasmx.at @@ -20,7 +20,7 @@ AT_KEYWORDS([dns hasmx]) AT_CHECK([ AT_REQUIRE_DNS cleardb -mailfromd MAILFROMD_OPTIONS --test $ETCDIR/dns.rc mode=hasmx arg1=gnu.org.ua], +mailfromd MAILFROMD_OPTIONS --test $ETCDIR/dns.rc mode=hasmx arg1=test1.$MF_TOPDOMAIN], [EX_OK], [State envfrom: continue ], diff --git a/tests/hostname.at b/tests/hostname.at index 7f5294a2..0bb5ab26 100644 --- a/tests/hostname.at +++ b/tests/hostname.at @@ -14,19 +14,14 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. -AT_SETUP([Resolve cname]) +AT_SETUP([Resolve hostname]) AT_KEYWORDS([dns hostname]) -if `AT_REQUIRE_DNS`; then - IPADDR=`puszcza_addr` - host -tPTR $IPADDR |\ - sed -n '1s/.*\.in-addr.arpa domain name pointer \(.*\)./\1/p' > experr -fi - AT_CHECK([ -test -f experr || AT_SKIP_TEST # Same as AT_REQUIRE_DNS +AT_REQUIRE_DNS cleardb -mailfromd MAILFROMD_OPTIONS --test $ETCDIR/dns.rc mode=hostname arg1=$IPADDR], +echo mail1.test2.$MF_TOPDOMAIN > experr +mailfromd MAILFROMD_OPTIONS --test $ETCDIR/dns.rc mode=hostname arg1=198.51.100.2], [EX_OK], [State envfrom: continue ], diff --git a/tests/ismx.at b/tests/ismx.at index 63be924b..762ce66d 100644 --- a/tests/ismx.at +++ b/tests/ismx.at @@ -20,8 +20,7 @@ AT_KEYWORDS([dns ismx]) AT_CHECK([ AT_REQUIRE_DNS cleardb -name=`host -tMX gnu.org.ua | sed 's/gnu.org.ua mail is handled by [[0-9]][[0-9]]* \(.*\)\./\1/' | head -n 1` -mailfromd MAILFROMD_OPTIONS --test $ETCDIR/dns.rc mode=ismx arg1=gnu.org.ua arg2=$name +mailfromd MAILFROMD_OPTIONS --test $ETCDIR/dns.rc mode=ismx arg1=test1.$MF_TOPDOMAIN arg2=mail.test2.$MF_TOPDOMAIN ], [EX_OK], [State envfrom: continue diff --git a/tests/rescname.at b/tests/rescname.at index 4c8c894d..d451bb26 100644 --- a/tests/rescname.at +++ b/tests/rescname.at @@ -17,18 +17,15 @@ AT_SETUP([Resolve cname]) AT_KEYWORDS([dns rescname]) -if `AT_REQUIRE_DNS`; then - puszcza_addr > experr -fi - AT_CHECK([ -test -f experr || AT_SKIP_TEST # Same as AT_REQUIRE_DNS +AT_REQUIRE_DNS cleardb -mailfromd MAILFROMD_OPTIONS --test $ETCDIR/dns.rc mode=resolve arg1=ps.gnu.org.ua], +mailfromd MAILFROMD_OPTIONS --test $ETCDIR/dns.rc mode=resolve arg1=smtp.test1.$MF_TOPDOMAIN], [EX_OK], [State envfrom: continue ], -[experr]) +[192.0.2.2 +]) AT_CLEANUP diff --git a/tests/resolv.c b/tests/resolv.c index 35d2d80a..a0cbc62c 100644 --- a/tests/resolv.c +++ b/tests/resolv.c @@ -2,14 +2,24 @@ #include <stdio.h> #include <stdio.h> #include <assert.h> +#include <unistd.h> #include <mailutils/mailutils.h> +#include "libmf.h" #include "dns.h" +static int quiet = 0; +static char const *suffix; +static int suffix_len; + void status_print(dns_status status) { + if (quiet > 1) + return; switch (status) { case dns_success: + if (quiet) + return; printf("OK"); break; case dns_not_found: @@ -27,7 +37,7 @@ status_print(dns_status status) } putchar('\n'); } - + static int hostname_cmp(const void *a, const void *b) { @@ -43,7 +53,12 @@ reply_print_str(struct dns_reply *reply, int sorted) qsort(reply->data.str, reply->count, sizeof reply->data.str[0], hostname_cmp); for (i = 0; i < reply->count; i++) { - printf("%s\n", reply->data.str[i]); + char const *str = reply->data.str[i]; + int len = strlen(str); + if (len > suffix_len + && memcmp(str + len - suffix_len, suffix, suffix_len) == 0) + len -= suffix_len; + printf("%*.*s\n", len, len, reply->data.str[i]); } } @@ -77,6 +92,8 @@ reply_print_ip(struct dns_reply *reply, int sorted) void reply_print(struct dns_reply *reply, int sorted) { + if (quiet > 2) + return; switch (reply->type) { case dns_reply_str: reply_print_str(reply, sorted); @@ -89,6 +106,29 @@ reply_print(struct dns_reply *reply, int sorted) abort(); } } + + static int + res_soa(int argc, char **argv) + { + int ip = 0; + struct dns_reply reply; + dns_status status; + + if (argc >= 2) { + if (strcmp(argv[0], "-ip") == 0) { + ip = 1; + argc--; + argv++; + } + } + status = soa_check(argv[0], ip, &reply); + status_print(status); + if (status == dns_success) { + reply_print(&reply, 1); + dns_reply_free(&reply); + } + return status; + } static int res_a(int argc, char **argv) @@ -103,7 +143,7 @@ res_a(int argc, char **argv) reply_print(&reply, 1); dns_reply_free(&reply); } - return 0; + return status; } static int @@ -116,7 +156,7 @@ res_ptr(int argc, char **argv) assert(argc == 1); if (!inet_aton(argv[0], &in)) { mu_error("%s: can't convert address", argv[0]); - return 1; + return dns_failure; } status = ptr_lookup(in, &reply); @@ -125,7 +165,7 @@ res_ptr(int argc, char **argv) reply_print(&reply, 1); dns_reply_free(&reply); } - return 0; + return status; } static int @@ -141,7 +181,7 @@ res_ptr_val(int argc, char **argv) reply_print(&reply, 1); dns_reply_free(&reply); } - return 0; + return status; } static int @@ -157,7 +197,7 @@ res_txt(int argc, char **argv) reply_print(&reply, 1); dns_reply_free(&reply); } - return 0; + return status; } static int @@ -184,7 +224,7 @@ res_mx(int argc, char **argv) reply_print(&reply, 0); dns_reply_free(&reply); } - return 0; + return status; } static int @@ -199,7 +239,7 @@ res_spf(int argc, char **argv) printf("%s\n", record); free(record); } - return 0; + return status; } static int @@ -215,7 +255,7 @@ res_host(int argc, char **argv) printf("%s\n", str); free(str); } - return 0; + return status; } static int @@ -231,7 +271,7 @@ res_ip(int argc, char **argv) printf("%s\n", str); free(str); } - return 0; + return status; } struct mode { @@ -240,6 +280,7 @@ struct mode { }; struct mode mode[] = { + { "soa", res_soa }, { "host", res_host }, { "ip", res_ip }, { "a", res_a }, @@ -255,13 +296,65 @@ int main(int argc, char **argv) { int i; + mu_opool_t op = NULL; + char *stmt; + char *resolv_conf = NULL; mu_set_program_name(argv[0]); mu_stdstream_setup(MU_STDSTREAM_RESET_NONE); - assert(argc > 2); + dnsbase_init(); + while ((i = getopt(argc, argv, "+df:h:qs:S:")) != EOF) { + switch (i) { + case 'd': + mu_debug_enable_category("dns", 3, + MU_DEBUG_LEVEL_UPTO(MU_DEBUG_PROT)); + break; + case 'f': + resolv_conf = optarg; + break; + case 'h': + mu_asprintf(&stmt, "nameserver %s\n", optarg); + if (!op) + mu_opool_create(&op, MU_OPOOL_ENOMEMABRT); + mu_opool_appendz(op, stmt); + free(stmt); + break; + case 'q': + quiet++; + break; + case 's': + if (!op) + mu_opool_create(&op, MU_OPOOL_ENOMEMABRT); + mu_opool_appendz(op, optarg); + mu_opool_append_char(op, '\n'); + break; + case 'S': + suffix = optarg; + suffix_len = strlen(suffix); + break; + default: + exit(1); + } + } + + assert(!(resolv_conf && op)); + + if (resolv_conf) + dnsbase_file_init(resolv_conf); + + if (op) { + mu_opool_append_char(op, 0); + stmt = mu_opool_finish(op, NULL); + dnsbase_real_init(stmt); + mu_opool_destroy(&op); + } + + argc -= optind; + argv += optind; + assert(argc > 1); for (i = 0; mode[i].name; i++) { - if (strcmp(mode[i].name, argv[1]) == 0) - exit(mode[i].func(argc - 2, argv + 2)); + if (strcmp(mode[i].name, argv[0]) == 0) + exit(mode[i].func(argc - 1, argv + 1)); } mu_error("%s:%d: unrecognized mode", __FILE__, __LINE__); abort(); diff --git a/tests/resolv_a.at b/tests/resolv_a.at new file mode 100644 index 00000000..42ab0437 --- /dev/null +++ b/tests/resolv_a.at @@ -0,0 +1,38 @@ +# This file is part of Mailfromd testsuite. -*- Autotest -*- +# Copyright (C) 2017 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 <http://www.gnu.org/licenses/>. + +AT_SETUP([A lookup]) +AT_KEYWORDS([dns resolv resolv_a]) +AT_CHECK([ +AT_REQUIRE_DNS +resolv -f resolv.conf a bkmx.test1.$MF_TOPDOMAIN +], +[0], +[OK +192.0.2.3 +192.0.2.4 +192.0.2.5 +]) + +AT_CHECK([ +AT_REQUIRE_DNS +resolv a nonexistent.$MF_TOPDOMAIN +], +[1], +[NOTFOUND +]) + +AT_CLEANUP diff --git a/tests/resolv_mx.at b/tests/resolv_mx.at new file mode 100644 index 00000000..58f5dd32 --- /dev/null +++ b/tests/resolv_mx.at @@ -0,0 +1,30 @@ +# This file is part of Mailfromd testsuite. -*- Autotest -*- +# Copyright (C) 2017 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 <http://www.gnu.org/licenses/>. + +AT_SETUP([MX lookup]) +AT_KEYWORDS([dns resolv resolv_mx]) +AT_CHECK([ +AT_REQUIRE_DNS +resolv -f resolv.conf -S .$MF_TOPDOMAIN mx test1.$MF_TOPDOMAIN +], +[0], +[OK +mail.test1 +mail.test2 +bkmx.test1 +]) +AT_CLEANUP + |