summaryrefslogtreecommitdiffstats
path: root/rfc1035
diff options
context:
space:
mode:
Diffstat (limited to 'rfc1035')
-rw-r--r--rfc1035/configure.ac1
-rw-r--r--rfc1035/rfc1035.h98
-rw-r--r--rfc1035/rfc1035mxlist.c42
-rw-r--r--rfc1035/rfc1035qa.c31
-rw-r--r--rfc1035/rfc1035reply.c9
-rw-r--r--rfc1035/rfc1035resolve.c368
-rw-r--r--rfc1035/rfc1035search.c141
-rw-r--r--rfc1035/rfc1035udp.c202
-rw-r--r--rfc1035/spf.c62
-rw-r--r--rfc1035/testlookup.c118
-rw-r--r--rfc1035/testspf.c2
-rw-r--r--rfc1035/testsuite.txt1
12 files changed, 870 insertions, 205 deletions
diff --git a/rfc1035/configure.ac b/rfc1035/configure.ac
index efae100..5748f05 100644
--- a/rfc1035/configure.ac
+++ b/rfc1035/configure.ac
@@ -16,6 +16,7 @@ AC_CONFIG_HEADERS(config.h)
dnl Checks for programs.
AC_PROG_AWK
AC_PROG_CC
+AC_PROG_CC_C99
AC_PROG_INSTALL
AC_PROG_LN_S
AC_LIBTOOL_DLOPEN
diff --git a/rfc1035/rfc1035.h b/rfc1035/rfc1035.h
index 7b9c4e7..9a92c1b 100644
--- a/rfc1035/rfc1035.h
+++ b/rfc1035/rfc1035.h
@@ -228,6 +228,22 @@ int rfc1035_resolve_cname(
#define RFC1035_X_RANDOMIZE 1 /* Randomize query results */
+/*
+** rfc1035_resolve_cname_multiple is a version of rfc1035_resolve_cname
+** that accepts an array of qtypes, and issues a query for each qtype.
+**
+** qtypes points to a 0-terminated list of RRs.
+*/
+int rfc1035_resolve_cname_multiple(struct rfc1035_res *res,
+ char *namebuf,
+ /* RFC1035_MAXNAMESIZE buffer with
+ ** the name to query */
+ unsigned char *qtypes,
+ unsigned qclass,
+ struct rfc1035_reply **ptr,
+ int x_flags);
+
+
/*
** Always call replyfree when done.
*/
@@ -363,17 +379,6 @@ int rfc1035_wait_query(int, /* File descriptor from rfc1035_open */
unsigned); /* Number of seconds to wait, use 0 for default */
/* Like reply, but we select for writing */
-char *rfc1035_recv_udp(int, /* File descriptor from rfc1035_open */
- const struct sockaddr *, int,
- /* Expecting reply from this IP address */
- int *, /* * will be set to point to # of bytes received */
- const char *); /* Original query, used to validate id # */
- /* Returns ptr to dynamically allocated memory containing the reply,
- ** or NULL if error. Errno will be set EAGAIN if we should try
- ** again, because the message received was not in response
- ** to the query.
- */
-
char *rfc1035_query_udp(struct rfc1035_res *,
int, /* file descriptor */
const struct sockaddr *, int, /* Attempt number */
@@ -623,6 +628,77 @@ struct rfc1035_ifconf {
struct rfc1035_ifconf *rfc1035_ifconf(int *errflag);
void rfc1035_ifconf_free(struct rfc1035_ifconf *ifconf_list);
+/*
+** Outstanding UDP queries.
+*/
+
+ /* common query buffer */
+
+struct querybuf {
+ char qbuf[512];
+ unsigned qbuflen;
+ } ;
+
+struct rfc1035_udp_query_response;
+
+ /* How many queries, and the queries */
+
+struct rfc1035_udp_query_responses {
+ int n_queries;
+ struct rfc1035_udp_query_response *queries;
+};
+
+ /* Each query, and response already receives so far, if any. */
+
+struct rfc1035_udp_query_response {
+ const char *query;
+ unsigned querylen;
+ char *response;
+ unsigned resplen;
+};
+
+/*
+** Allocate a new rfc1035_udp_query_responses.
+** Copies each query into the rfc1035_udp_query_response, each response is
+** initialized to null.
+*/
+
+struct rfc1035_udp_query_responses *
+rfc1035_udp_query_response_alloc(const char **queries,
+ const unsigned *querylens,
+ int n_queries);
+
+struct rfc1035_udp_query_responses *
+rfc1035_udp_query_response_alloc_bis(struct querybuf *queries,
+ int n_queries);
+
+/*
+** Send all queries via UDPs, wait for responses.
+**
+** Returns non-0 if all queries are received, 0 if not. EAGAIN indicates
+** a timeout, and this can be called again with the same
+** rfc1035_udp_query_responses object; this resends all queries for which
+** no response was received, and this will wait for the remaining responses.
+*/
+int rfc1035_udp_query_multi(struct rfc1035_res *res,
+ /* Socket: */
+ int fd,
+ /* Where to send it to */
+ const struct sockaddr *sin, int sin_len,
+ /* The queries */
+ struct rfc1035_udp_query_responses *qr,
+
+ /* How long to wait for responses, in seconds. */
+ unsigned w);
+
+/*
+** Deallocate rfc1035_udp_query_responses. Each non-NULL response pointer
+** gets free()d.
+*/
+
+void rfc1035_udp_query_response_free(struct rfc1035_udp_query_responses *);
+
+
#ifdef __cplusplus
}
#endif
diff --git a/rfc1035/rfc1035mxlist.c b/rfc1035/rfc1035mxlist.c
index de2448e..48c5f3c 100644
--- a/rfc1035/rfc1035mxlist.c
+++ b/rfc1035/rfc1035mxlist.c
@@ -91,6 +91,7 @@ static int add_arecords(struct rfc1035_res *res, struct rfc1035_mxlist **list,
{
#if RFC1035_IPV6
struct in6_addr in;
+ int rc2;
int first_a=RFC1035_TYPE_A;
int second_a=RFC1035_TYPE_AAAA;
const char *prefer_ipv6=getenv("ESMTP_PREFER_IPV6_MX");
@@ -134,30 +135,41 @@ records weren't found.
if (mxreply && !(opts & RFC1035_MX_QUERYALL))
{
- if ((rc=harvest_records(res, list, mxreply, mxpreference,
- mxname, second_a, &found, HARVEST_NODUPE,
- port))
- != RFC1035_MX_OK)
- return (rc);
+ rc2=harvest_records(res, list, mxreply, mxpreference,
+ mxname, second_a, &found, HARVEST_NODUPE,
+ port);
+
+ if (rc2 != RFC1035_MX_OK && rc2 != RFC1035_MX_SOFTERR)
+ return rc2;
+
+ rc=harvest_records(res, list, mxreply, mxpreference,
+ mxname, first_a, &found, 0, port);
- if ((rc=harvest_records(res, list, mxreply, mxpreference,
- mxname, first_a, &found, 0, port))
- != RFC1035_MX_OK)
+ if (rc != RFC1035_MX_OK && rc != RFC1035_MX_SOFTERR)
return (rc);
+ if (rc == RFC1035_MX_SOFTERR && rc2 == RFC1035_MX_SOFTERR)
+ return rc;
+
if (found) return (RFC1035_MX_OK);
}
- if ((rc=harvest_records(res, list, mxreply, mxpreference, mxname,
- second_a, &found, HARVEST_AUTOQUERY|HARVEST_NODUPE, port))
- != RFC1035_MX_OK)
- return (rc);
+ rc2=harvest_records(res, list, mxreply, mxpreference, mxname,
+ second_a, &found, HARVEST_AUTOQUERY|HARVEST_NODUPE,
+ port);
- if ((rc=harvest_records(res, list, mxreply, mxpreference, mxname,
- first_a, &found, HARVEST_AUTOQUERY, port))
- != RFC1035_MX_OK)
+ if (rc2 != RFC1035_MX_OK && rc2 != RFC1035_MX_SOFTERR)
+ return rc2;
+
+ rc=harvest_records(res, list, mxreply, mxpreference, mxname,
+ first_a, &found, HARVEST_AUTOQUERY, port);
+
+ if (rc != RFC1035_MX_OK && rc != RFC1035_MX_SOFTERR)
return (rc);
+ if (rc == RFC1035_MX_SOFTERR && rc2 == RFC1035_MX_SOFTERR)
+ return rc;
+
if (found) return (RFC1035_MX_OK);
#else
diff --git a/rfc1035/rfc1035qa.c b/rfc1035/rfc1035qa.c
index 46fc204..ffed34f 100644
--- a/rfc1035/rfc1035qa.c
+++ b/rfc1035/rfc1035qa.c
@@ -1,5 +1,5 @@
/*
-** Copyright 1998 - 2011 Double Precision, Inc.
+** Copyright 1998 - 2019 Double Precision, Inc.
** See COPYING for distribution information.
*/
@@ -8,6 +8,7 @@
#include <string.h>
#include <stdlib.h>
#include <arpa/inet.h>
+#include <idna.h>
/* Convenient function to do forward IP lookup */
@@ -16,8 +17,9 @@
static int rfc1035_a_ipv4(struct rfc1035_res *res,
const char *name, struct in_addr **iaptr, unsigned *iasize)
#else
-int rfc1035_a(struct rfc1035_res *res,
- const char *name, RFC1035_ADDR **iaptr, unsigned *iasize)
+static int rfc1035_unicode(struct rfc1035_res *res,
+ const char *name,
+ RFC1035_ADDR **iaptr, unsigned *iasize)
#endif
{
struct rfc1035_reply *reply;
@@ -111,8 +113,9 @@ unsigned i;
return (0);
}
-int rfc1035_a(struct rfc1035_res *res,
- const char *name, struct in6_addr **iaptr, unsigned *iasize)
+static int rfc1035_unicode(struct rfc1035_res *res,
+ const char *name, struct in6_addr **iaptr,
+ unsigned *iasize)
{
struct rfc1035_reply *reply;
int n, o;
@@ -236,3 +239,21 @@ unsigned k;
return (0);
}
#endif
+
+int rfc1035_a(struct rfc1035_res *res,
+ const char *name,
+ RFC1035_ADDR **iaptr, unsigned *iasize)
+{
+ char *p;
+ int r;
+
+ /* Convert requested hostname to UTF-8, with fallback */
+
+ if (idna_to_unicode_8z8z(name, &p, 0) != IDNA_SUCCESS)
+ return rfc1035_unicode(res, name, iaptr, iasize);
+
+ r=rfc1035_unicode(res, p, iaptr, iasize);
+
+ free(p);
+ return r;
+}
diff --git a/rfc1035/rfc1035reply.c b/rfc1035/rfc1035reply.c
index ac1e87e..ab43dfb 100644
--- a/rfc1035/rfc1035reply.c
+++ b/rfc1035/rfc1035reply.c
@@ -489,7 +489,10 @@ void rfc1035_rr_rand_ar(struct rfc1035_reply *rr)
void rfc1035_rr_rand(struct rfc1035_reply *rr)
{
- rfc1035_rr_rand_an(rr);
- rfc1035_rr_rand_ns(rr);
- rfc1035_rr_rand_ar(rr);
+ if (rr->ancount > 1)
+ rfc1035_rr_rand_an(rr);
+ if (rr->nscount > 1)
+ rfc1035_rr_rand_ns(rr);
+ if (rr->arcount > 1)
+ rfc1035_rr_rand_ar(rr);
}
diff --git a/rfc1035/rfc1035resolve.c b/rfc1035/rfc1035resolve.c
index b8bb91f..57b4139 100644
--- a/rfc1035/rfc1035resolve.c
+++ b/rfc1035/rfc1035resolve.c
@@ -15,11 +15,6 @@
#include <string.h>
#include <idna.h>
-struct querybuf {
- char qbuf[512];
- unsigned qbuflen;
- } ;
-
static void putqbuf(const char *p, unsigned l, void *q)
{
struct querybuf *qp=(struct querybuf *)q;
@@ -88,19 +83,41 @@ struct rfc1035_reply
}
static struct rfc1035_reply
+*rfc1035_resolve_multiple_attempt(struct rfc1035_res *res,
+ struct rfc1035_udp_query_responses *resps,
+ int udpfd,
+ int af,
+ const RFC1035_ADDR *sin,
+ unsigned current_timeout);
+
+static struct rfc1035_reply
+*rfc1035_resolve_multiple_attempt_tcp(struct rfc1035_res *res,
+ char const*query,
+ unsigned querylen,
+ int af,
+ const RFC1035_ADDR *sin,
+ int isaxfr,
+ unsigned current_timeout);
+
+static struct rfc1035_reply
*rfc1035_resolve_multiple_idna(struct rfc1035_res *res,
int opcode,
const struct rfc1035_query *queries,
unsigned nqueries)
{
- struct querybuf qbuf;
+ struct querybuf qbuf[nqueries];
int udpfd;
- int attempt;
+ unsigned attempt;
const RFC1035_ADDR *ns;
unsigned nscount;
unsigned current_timeout, timeout_backoff;
unsigned nbackoff, backoff_num;
int af;
+ unsigned i;
+ int isaxfr=0;
+ struct rfc1035_udp_query_responses *resps=0;
+
+ struct rfc1035_reply *rfcreply=0;
static const char fakereply[]={0, 0, 0, RFC1035_RCODE_SERVFAIL,
0, 0,
0, 0,
@@ -113,18 +130,34 @@ static struct rfc1035_reply
if (res->rfc1035_good_ns >= nscount)
res->rfc1035_good_ns=0;
- qbuf.qbuflen=0;
- if ( rfc1035_mkquery(res,
- opcode, queries, nqueries, &putqbuf, &qbuf))
+ for (i=0; i<nqueries; i++)
{
- errno=EINVAL;
- return (0);
+ qbuf[i].qbuflen=0;
+ if ( rfc1035_mkquery(res,
+ opcode, &queries[i], 1, &putqbuf, &qbuf[i]))
+ {
+ errno=EINVAL;
+ return (0);
+ }
+ if (queries[i].qtype == RFC1035_TYPE_AXFR)
+ {
+ isaxfr=1;
+ break;
+ }
}
+ if (isaxfr && nqueries > 1)
+ return rfc1035_replyparse(fakereply, sizeof(fakereply));
+
/* Prepare the UDP socket */
if ((udpfd=rfc1035_open_udp(&af)) < 0) return (0);
+ /* Prepare responses array for multple queries */
+
+ if (!isaxfr)
+ resps=rfc1035_udp_query_response_alloc_bis(qbuf, nqueries);
+
/* Keep trying until we get an answer from a nameserver */
current_timeout=res->rfc1035_timeout_initial;
@@ -133,148 +166,243 @@ static struct rfc1035_reply
if (!nbackoff) nbackoff=RFC1035_DEFAULT_MAXIMUM_BACKOFF;
timeout_backoff=current_timeout;
- for (backoff_num=0; backoff_num < nbackoff; backoff_num++,
- current_timeout *= timeout_backoff)
-
- for ( attempt=0; attempt < nscount; ++attempt)
+ for (backoff_num=0; backoff_num < nbackoff; backoff_num++,
+ current_timeout *= timeout_backoff)
{
- int nbytes;
- char *reply;
- struct rfc1035_reply *rfcreply=0;
-
- const RFC1035_ADDR *sin=&ns[(res->rfc1035_good_ns+attempt) % nscount];
- int sin_len=sizeof(*sin);
-
- int dotcp=0, isaxfr=0;
- unsigned i;
-
- for (i=0; i<nqueries; i++)
- if (queries[i].qtype == RFC1035_TYPE_AXFR)
+ for ( attempt=0; attempt < nscount; ++attempt)
+ {
+ const RFC1035_ADDR *sin=
+ &ns[(res->rfc1035_good_ns+attempt) % nscount];
+
+ rfcreply=isaxfr ?
+ rfc1035_resolve_multiple_attempt_tcp
+ (res,
+ qbuf[0].qbuf,
+ qbuf[0].qbuflen,
+ af,
+ sin,
+ 1,
+ current_timeout)
+ : rfc1035_resolve_multiple_attempt
+ (res,
+ resps,
+ udpfd,
+ af,
+ sin,
+ current_timeout);
+
+ if (rfcreply)
{
- dotcp=1;
- isaxfr=1;
+ res->rfc1035_good_ns=
+ (res->rfc1035_good_ns + attempt) %
+ nscount;
break;
}
+ }
- if (isaxfr && nqueries > 1)
- return (rfc1035_replyparse(fakereply,
- sizeof(fakereply)));
+ if (rfcreply)
+ break;
+ }
- if (!dotcp)
- {
+ sox_close(udpfd);
+
+ if (resps)
+ rfc1035_udp_query_response_free(resps);
+
+ if (!rfcreply)
+ rfcreply=rfc1035_replyparse(fakereply, sizeof(fakereply));
+
+ return (rfcreply);
+}
+
+static struct rfc1035_reply
+*rfc1035_resolve_multiple_attempt(struct rfc1035_res *res,
+ struct rfc1035_udp_query_responses *resps,
+ int udpfd,
+ int af,
+ const RFC1035_ADDR *sin,
+ unsigned current_timeout)
+{
+ struct rfc1035_reply *rfcreply=0, **rfcpp=&rfcreply;
+ int sin_len=sizeof(*sin);
+ int dotcp=0;
+ unsigned n, nqueries=resps->n_queries;
+
+ if (!resps)
+ return NULL;
+
+ if (!dotcp)
+ {
/* Send the query via UDP */
RFC1035_NETADDR addrbuf;
const struct sockaddr *addrptr;
int addrptrlen;
- if (rfc1035_mkaddress(af, &addrbuf,
- sin, htons(53),
- &addrptr, &addrptrlen))
- continue;
+ if (rfc1035_mkaddress(af, &addrbuf,
+ sin, htons(53),
+ &addrptr, &addrptrlen))
+ return NULL;
- if ((reply=rfc1035_query_udp(res, udpfd, addrptr,
- addrptrlen, qbuf.qbuf, qbuf.qbuflen, &nbytes,
- current_timeout)) == 0)
- continue;
+ if (rfc1035_udp_query_multi(res, udpfd, addrptr,
+ addrptrlen, resps, current_timeout) == 0)
+ return NULL;
- res->rfc1035_good_ns= (res->rfc1035_good_ns + attempt) %
- nscount;
+ /* Parse the replies */
- /* Parse the reply */
+ for (n = 0; n < nqueries; ++n)
+ {
+ struct rfc1035_udp_query_response *reply=&resps->queries[n];
+ if (!reply->response)
+ break; // How come? rfc1035_udp_query_multi succeeded...
- rfcreply=rfc1035_replyparse(reply, nbytes);
- if (!rfcreply)
+ *rfcpp=rfc1035_replyparse(reply->response, reply->resplen);
+ if (!*rfcpp)
{
- free(reply);
- if (errno == ENOMEM) break;
- continue;
- /* Bad response from the server, try the next one. */
+ free(reply->response); // possibly unparseable
+ reply->response=0;
+ break;
}
- rfcreply->mallocedbuf=reply;
- /*
- ** If the reply came back with the truncated bit set,
- ** retry the query via TCP.
- */
- if (rfcreply->tc)
- {
+ if ((*rfcpp)->tc)
dotcp=1;
- rfc1035_replyfree(rfcreply);
- }
+ (*rfcpp)->mallocedbuf=reply->response;
+ reply->response=0;
+ rfcpp=&(*rfcpp)->next;
}
- if (dotcp)
+ if (n < nqueries)
{
- int tcpfd;
- struct rfc1035_reply *firstreply=0, *lastreply=0;
-
- if ((tcpfd=rfc1035_open_tcp(res, sin)) < 0)
- continue; /*
- ** Can't connect via TCP,
- ** try the next server.
- */
-
- reply=rfc1035_query_tcp(res, tcpfd, qbuf.qbuf,
- qbuf.qbuflen, &nbytes, current_timeout);
+ if (rfcreply)
+ rfc1035_replyfree(rfcreply);
+ return NULL;
+ }
+ }
- if (!reply)
+ if (dotcp)
+ {
+ n = 0;
+ for (rfcpp=&rfcreply; *rfcpp; rfcpp=&(*rfcpp)->next, ++n)
+ {
+ /*
+ * UDP replies are all in, some were truncated.
+ */
+ if ((*rfcpp)->tc)
{
- sox_close(tcpfd);
- continue;
- }
-
- res->rfc1035_good_ns= (res->rfc1035_good_ns
- + attempt) % nscount;
+ struct rfc1035_reply *next=(*rfcpp)->next, *newrfc;
+ struct rfc1035_udp_query_response *reply=&resps->queries[n];
- rfcreply=rfc1035_replyparse(reply, nbytes);
- if (!rfcreply)
- {
- free(reply);
- sox_close(tcpfd);
- continue;
- }
- rfcreply->mallocedbuf=reply;
- firstreply=lastreply=rfcreply;
- while (isaxfr && rfcreply->rcode == 0)
- {
- if ((reply=rfc1035_recv_tcp(res,
- tcpfd, &nbytes, current_timeout))==0)
+ newrfc=rfc1035_resolve_multiple_attempt_tcp(res,
+ reply->query, reply->querylen,
+ af, sin, 0, current_timeout);
+ if (!newrfc)
break;
- rfcreply=rfc1035_replyparse(reply, nbytes);
- if (!rfcreply)
- {
- free(reply);
- rfc1035_replyfree(firstreply);
- firstreply=0;
- break;
- }
- rfcreply->mallocedbuf=reply;
- lastreply->next=rfcreply;
- lastreply=rfcreply;
-
- if ( rfcreply->ancount &&
- rfcreply->anptr[0].rrtype ==
- RFC1035_TYPE_SOA)
- break;
+ /*
+ * Replace the truncated reply in the linked list
+ */
+ (*rfcpp)->next=0; // only free this
+ rfc1035_replyfree(*rfcpp);
+ *rfcpp=newrfc;
+ (*rfcpp)->next=next;
}
- sox_close(tcpfd);
- if (!firstreply)
- return (0);
- rfcreply=firstreply;
}
- memcpy(&rfcreply->server_addr, sin, sin_len);
- sox_close(udpfd);
- return (rfcreply);
+
+ if (n < nqueries && rfcreply)
+ {
+ rfc1035_replyfree(rfcreply);
+ rfcreply=0;
+ }
}
+ if (rfcreply)
+ memcpy(&rfcreply->server_addr, sin, sin_len);
+
+ return (rfcreply);
+}
+
+static struct rfc1035_reply
+*rfc1035_resolve_multiple_attempt_tcp(struct rfc1035_res *res,
+ char const*query,
+ unsigned querylen,
+ int af,
+ const RFC1035_ADDR *sin,
+ int isaxfr,
+ unsigned current_timeout)
+{
+ int nbytes;
+ char *reply;
+ struct rfc1035_reply *rfcreply;
+ unsigned i;
+
/*
- ** Return a fake server failure reply, when we couldn't contact
- ** any name server.
+ ** First record in axfr will be an SOA. Start searching for the
+ ** trailing SOA starting with record 1. In case of multiple responses
+ ** we'll start looking with element 0, again.
*/
- sox_close(udpfd);
- return (rfc1035_replyparse(fakereply, sizeof(fakereply)));
+ unsigned check_soa=1;
+
+ int tcpfd;
+ struct rfc1035_reply *firstreply=0, *lastreply=0;
+
+ if ((tcpfd=rfc1035_open_tcp(res, sin)) < 0)
+ return NULL; /*
+ ** Can't connect via TCP,
+ ** try the next server.
+ */
+
+ reply=rfc1035_query_tcp(res, tcpfd, query,
+ querylen, &nbytes, current_timeout);
+
+ if (!reply)
+ {
+ sox_close(tcpfd);
+ return NULL;
+ }
+
+ rfcreply=rfc1035_replyparse(reply, nbytes);
+ if (!rfcreply)
+ {
+ free(reply);
+ sox_close(tcpfd);
+ return NULL;
+ }
+ rfcreply->mallocedbuf=reply;
+ firstreply=lastreply=rfcreply;
+ while (isaxfr && rfcreply->rcode == 0)
+ {
+ for (i=check_soa; i<rfcreply->ancount; ++i)
+ {
+ if (rfcreply->anptr[i].rrtype ==
+ RFC1035_TYPE_SOA)
+ break;
+ }
+
+ if (i < rfcreply->ancount)
+ break; /* Found trailing SOA */
+
+ check_soa=0;
+
+ if ((reply=rfc1035_recv_tcp(res,
+ tcpfd, &nbytes,
+ current_timeout))==0)
+ break;
+
+ rfcreply=rfc1035_replyparse(reply, nbytes);
+ if (!rfcreply)
+ {
+ free(reply);
+ rfc1035_replyfree(firstreply);
+ firstreply=0;
+ break;
+ }
+ rfcreply->mallocedbuf=reply;
+ lastreply->next=rfcreply;
+ lastreply=rfcreply;
+ }
+ sox_close(tcpfd);
+
+ return firstreply;
}
struct rfc1035_reply *rfc1035_resolve(
diff --git a/rfc1035/rfc1035search.c b/rfc1035/rfc1035search.c
index 019f521..01d0025 100644
--- a/rfc1035/rfc1035search.c
+++ b/rfc1035/rfc1035search.c
@@ -141,7 +141,8 @@ int rfc1035_replysearch_all(const struct rfc1035_res *res,
return (-1);
}
-int rfc1035_resolve_cname(struct rfc1035_res *res, char *namebuf,
+int rfc1035_resolve_cname(struct rfc1035_res *res,
+ char *namebuf,
unsigned qtype,
unsigned qclass,
struct rfc1035_reply **ptr,
@@ -212,3 +213,141 @@ int recursion_count=10;
return (-1);
}
}
+
+int rfc1035_resolve_cname_multiple(struct rfc1035_res *res,
+ char *namebuf,
+ unsigned char *qtypes,
+ unsigned qclass,
+ struct rfc1035_reply **ptr,
+ int x_flags)
+{
+ int n, query_again;
+ int retry_count=10;
+ unsigned nqueries=strlen((char*)qtypes), u, good, cname;
+ struct rfc1035_query qu[nqueries];
+ struct rfc1035_reply *p;
+
+ char savename[RFC1035_MAXNAMESIZE+1],
+ cmpname1[RFC1035_MAXNAMESIZE+1],
+ cmpname2[RFC1035_MAXNAMESIZE+1];
+
+ static const char forbidden[] =
+ {
+ RFC1035_TYPE_CNAME,
+ RFC1035_TYPE_AXFR,
+ RFC1035_TYPE_ANY,
+ 0
+ };
+
+ *ptr=NULL;
+
+ if (nqueries == 0)
+ return -1;
+
+ strcpy(savename, namebuf);
+ for (u=0; u<nqueries; ++u)
+ {
+ qu[u].name=namebuf;
+ qu[u].qclass=qclass;
+ qu[u].qtype=qtypes[u];
+
+ if (strchr(forbidden, qtypes[u]))
+ {
+ if (nqueries == 1)
+ {
+ return rfc1035_resolve_cname(res, namebuf,
+ qtypes[0],
+ qclass,
+ ptr,
+ x_flags);
+ }
+ return -1;
+ }
+ }
+
+ do // query loop
+ {
+ if (*ptr)
+ rfc1035_replyfree( *ptr );
+
+ if ( (*ptr=rfc1035_resolve_multiple(res, RFC1035_OPCODE_QUERY,
+ qu, nqueries)) == 0)
+ return (-1);
+
+ query_again=0;
+
+ for (;;) // change name loop
+ {
+ int fatal = 0;
+
+ good = cname = 0;
+ for (u=0, p=*ptr; p; ++u, p=p->next)
+ {
+ if (p->rcode != RFC1035_RCODE_NOERROR ||
+ (n=rfc1035_replysearch_all(res,
+ p, namebuf,
+ qu[u].qtype,
+ qclass, 0))
+ < 0)
+ continue;
+ else if (p->allrrs[n]->rrtype == qu[u].qtype)
+ {
+ ++good;
+ }
+ else // CNAME (not the queried type).
+ {
+ if (rfc1035_replyhostname(p, p->anptr[n].rr.domainname,
+ cname? cmpname1: cmpname2) == 0)
+ fatal=-1;
+
+ else if (cname && strcmp(cmpname1, cmpname2))
+ query_again=1; // different aliases: try again
+
+ ++cname;
+ }
+ }
+
+ if (good == 0 && cname == 0)
+ {
+ return (-1);
+ }
+
+ if (good > 0 && cname > 0)
+ query_again=1; // have both cnames and non-cnames
+
+ if (query_again)
+ {
+ if (--retry_count > 0)
+ {
+ /* Query back from the original alias. */
+ strcpy(namebuf, savename);
+ break;
+ }
+
+ fatal=RFC1035_ERR_CNAME_RECURSIVE;
+ }
+
+ if (fatal)
+ {
+ rfc1035_replyfree( *ptr );
+ *ptr=0;
+ return fatal;
+ }
+
+ if (good)
+ break;
+
+ /* CNAME found. Return alias to caller, and restart from there */
+
+ strcpy(namebuf, cmpname1);
+ }
+
+ } while (query_again);
+
+ if (x_flags & RFC1035_X_RANDOMIZE)
+ for (p=*ptr; p; p=p->next)
+ if (p->rcode == RFC1035_RCODE_NOERROR)
+ rfc1035_rr_rand(p);
+
+ return (0);
+}
diff --git a/rfc1035/rfc1035udp.c b/rfc1035/rfc1035udp.c
index 29dac33..2a4ed34 100644
--- a/rfc1035/rfc1035udp.c
+++ b/rfc1035/rfc1035udp.c
@@ -40,7 +40,86 @@ int rfc1035_send_udp(int fd, const struct sockaddr *sin, int sin_len,
return (-1);
}
-static int dorecv(int fd, char *bufptr, int buflen, int flags,
+static struct rfc1035_udp_query_responses *
+rfc1035_udp_query_response_alloc_common(int n_queries)
+{
+ struct rfc1035_udp_query_response *buf=
+ calloc(n_queries, sizeof(struct rfc1035_udp_query_response));
+
+ struct rfc1035_udp_query_responses *resps;
+
+ if (!buf)
+ return 0;
+
+ resps=malloc(sizeof(struct rfc1035_udp_query_responses));
+
+ if (!resps)
+ {
+ free(buf);
+ return 0;
+ }
+ resps->n_queries=n_queries;
+ resps->queries=buf;
+
+ return resps;
+}
+
+struct rfc1035_udp_query_responses *
+rfc1035_udp_query_response_alloc(const char **queries,
+ const unsigned *querylens,
+ int n_queries)
+{
+ struct rfc1035_udp_query_responses *resps =
+ rfc1035_udp_query_response_alloc_common(n_queries);
+
+ if (!resps)
+ return 0;
+
+ for (int n=0; n<n_queries; ++n)
+ {
+ resps->queries[n].query=queries[n];
+ resps->queries[n].querylen=querylens[n];
+ }
+
+ return resps;
+}
+
+
+struct rfc1035_udp_query_responses *
+rfc1035_udp_query_response_alloc_bis(struct querybuf *queries,
+ int n_queries)
+{
+ struct rfc1035_udp_query_responses *resps =
+ rfc1035_udp_query_response_alloc_common(n_queries);
+
+ if (!resps)
+ return 0;
+
+
+ for (int n=0; n<n_queries; ++n)
+ {
+ resps->queries[n].query=queries[n].qbuf;
+ resps->queries[n].querylen=queries[n].qbuflen;
+ }
+
+ return resps;
+}
+
+void rfc1035_udp_query_response_free(struct rfc1035_udp_query_responses *resps)
+{
+ int n;
+
+ for (n=0; n<resps->n_queries; ++n)
+ {
+ if (resps->queries[n].response)
+ free(resps->queries[n].response);
+ }
+
+ free(resps->queries);
+ free(resps);
+}
+
+static int dorecv(int fd, char *bufptr, unsigned buflen, int flags,
struct sockaddr *addr, socklen_t *addrlen)
{
socklen_t len;
@@ -52,9 +131,9 @@ socklen_t len;
return (len);
}
-char *rfc1035_recv_udp(int fd,
+static int rfc1035_recv_one_udp_response(int fd,
const struct sockaddr *addrshouldfrom, int addrshouldfrom_len,
- int *buflen, const char *query)
+ struct rfc1035_udp_query_responses *queries)
{
int len;
@@ -72,23 +151,23 @@ socklen_t addrfromlen;
char rfc1035_buf[512];
char *bufptr=rfc1035_buf;
char *mallocedbuf=0;
+int i;
+unsigned buflen=sizeof(rfc1035_buf);
- *buflen=sizeof(rfc1035_buf);
-
- while ((len=dorecv(fd, bufptr, *buflen, MSG_PEEK, 0, 0)) >= *buflen )
+ while ((len=dorecv(fd, bufptr, buflen, MSG_PEEK, 0, 0)) >= buflen )
{
- if (len == *buflen) len += 511;
+ if (len == buflen) len += 511;
++len;
if (mallocedbuf) free(mallocedbuf);
mallocedbuf=(char *)malloc(len);
if (!mallocedbuf) return (0);
bufptr= mallocedbuf;
- *buflen=len;
+ buflen=len;
}
addrfromlen=sizeof(addrfrom);
- if (len < 0 || (len=dorecv(fd, bufptr, *buflen, 0,
+ if (len < 0 || (len=dorecv(fd, bufptr, buflen, 0,
(struct sockaddr *)&addrfrom, &addrfromlen)) < 0)
{
if (mallocedbuf)
@@ -97,7 +176,7 @@ char *mallocedbuf=0;
return (0);
}
- *buflen=len;
+ buflen=len;
if ( !rfc1035_same_ip( &addrfrom, addrfromlen,
addrshouldfrom, addrshouldfrom_len))
@@ -109,56 +188,111 @@ char *mallocedbuf=0;
return (0);
}
- if ( *buflen < 2)
- {
- if (mallocedbuf)
- free(mallocedbuf);
- errno=EIO;
- return (0);
- }
-
- if ( query && (bufptr[0] != query[0] || bufptr[1] != query[1]
- || (unsigned char)(bufptr[2] & 0x80) == 0 ))
+ if ( buflen < 2)
{
if (mallocedbuf)
free(mallocedbuf);
errno=EAGAIN;
return (0);
}
- if (!mallocedbuf)
+
+ for (i=0; i<queries->n_queries; ++i)
{
- if ((mallocedbuf=malloc( *buflen )) == 0)
- return (0);
+ if (queries->queries[i].response)
+ continue; /* Already received this one */
+
+ if ( bufptr[0] != queries->queries[i].query[0] ||
+ bufptr[1] != queries->queries[i].query[1]
+ || (unsigned char)(bufptr[2] & 0x80) == 0 )
+ continue;
+
+ if (!mallocedbuf)
+ {
+ if ((mallocedbuf=malloc( buflen )) == 0)
+ return (0);
- memcpy(mallocedbuf, bufptr, *buflen);
- bufptr=mallocedbuf;
+ memcpy(mallocedbuf, bufptr, buflen);
+ bufptr=mallocedbuf;
+ }
+
+ queries->queries[i].response=mallocedbuf;
+ queries->queries[i].resplen=buflen;
+ return 1;
}
- return (bufptr);
+
+ if (mallocedbuf)
+ free(mallocedbuf);
+ errno=EAGAIN;
+ return (0);
}
char *rfc1035_query_udp(struct rfc1035_res *res,
int fd, const struct sockaddr *sin, int sin_len,
const char *query, unsigned query_len, int *buflen, unsigned w)
{
+ struct rfc1035_udp_query_responses *resps=
+ rfc1035_udp_query_response_alloc(&query, &query_len, 1);
+ char *bufptr=0;
+
+ if (!resps)
+ return 0;
+
+ if (rfc1035_udp_query_multi(res, fd, sin, sin_len, resps, w))
+ {
+ bufptr=resps->queries[0].response;
+ *buflen=resps->queries[0].resplen;
+ resps->queries[0].response=0;
+ }
+
+ rfc1035_udp_query_response_free(resps);
+ return bufptr;
+}
+
+int rfc1035_udp_query_multi(struct rfc1035_res *res,
+ int fd, const struct sockaddr *sin, int sin_len,
+ struct rfc1035_udp_query_responses *qr,
+ unsigned w)
+{
time_t current_time, final_time;
-char *rc;
+int i;
time(&current_time);
- if (rfc1035_send_udp(fd, sin, sin_len, query, query_len))
- return (0);
+ for (i=0; i<qr->n_queries; ++i)
+ {
+ if (qr->queries[i].response)
+ continue; /* Already sent it */
+ if (rfc1035_send_udp(fd, sin, sin_len,
+ qr->queries[i].query,
+ qr->queries[i].querylen))
+ return (0);
+ }
final_time=current_time+w;
- while (current_time < final_time)
+ while (1)
{
- if (rfc1035_wait_reply(fd, final_time-current_time))
+ for (i=0; i<qr->n_queries; ++i)
+ if (!qr->queries[i].response)
+ break;
+
+ if (i == qr->n_queries)
+ return 1; /* Everything received */
+
+ if (current_time >= final_time)
break;
- rc=rfc1035_recv_udp(fd, sin, sin_len, buflen, query);
- if (rc) return (rc);
+ if (rfc1035_wait_reply(fd, final_time-current_time))
+ break;
- if (errno != EAGAIN) break;
+ if (!rfc1035_recv_one_udp_response(fd,
+ sin,
+ sin_len,
+ qr))
+ {
+ if (errno != EAGAIN)
+ return 0;
+ }
time(&current_time);
}
diff --git a/rfc1035/spf.c b/rfc1035/spf.c
index f811aad..7b9d40b 100644
--- a/rfc1035/spf.c
+++ b/rfc1035/spf.c
@@ -22,6 +22,7 @@
#include <time.h>
#endif
#endif
+#include <idna.h>
struct rfc1035_spf_info {
const char *mailfrom;
@@ -1313,8 +1314,69 @@ static int do_expand(const char *str, struct rfc1035_spf_info *info,
static char *expandc(const char *ipaddr);
static char *expandi(const char *ipaddr);
+static char *get_macro2(struct rfc1035_spf_info *info, char name);
+
static char *get_macro(struct rfc1035_spf_info *info, char name)
{
+ char *v=get_macro2(info, name);
+ char *p;
+ char *buf;
+ char *newbuf;
+
+ if (!v)
+ return v;
+
+ switch (name) {
+ case 's':
+ /* s = responsible-sender */
+
+ p=strrchr(v, '@');
+
+ /* Find domain, convert to ACE */
+ if (!p || idna_to_ascii_8z(++p, &buf, 0) != IDNA_SUCCESS)
+ break;
+
+ /* Buffer for local part + ACE domain */
+
+ newbuf=malloc(p-v+strlen(buf)+1);
+
+ if (!newbuf)
+ {
+ free(buf);
+ break;
+ }
+
+ /* Rebuild address with an ACE domain */
+
+ memcpy(newbuf, v, p-v);
+ strcpy(newbuf+(p-v), buf);
+ free(v);
+ v=newbuf;
+ free(buf);
+ break;
+ case 'o':
+ /* o = responsible-domain */
+ case 'd':
+ /* d = current-domain */
+ case 'p':
+ /* p = SMTP client domain name */
+ case 'h':
+ /* h = HELO/EHLO domain */
+ case 'r':
+ /* r = receiving domain */
+
+ if (idna_to_ascii_8z(v, &buf, 0) == IDNA_SUCCESS)
+ {
+ free(v);
+ v=buf;
+ }
+ }
+
+ return v;
+}
+
+static char *get_macro2(struct rfc1035_spf_info *info, char name)
+{
char *p;
const char *cp;
diff --git a/rfc1035/testlookup.c b/rfc1035/testlookup.c
index a406411..75c701f 100644
--- a/rfc1035/testlookup.c
+++ b/rfc1035/testlookup.c
@@ -36,6 +36,7 @@ char *q=malloc(strlen(p)+1), *r;
r);
}
}
+ free(q);
rfc1035_init_ns(res, ia, i);
}
@@ -73,13 +74,65 @@ static void spflookup(const char *current_domain)
}
}
+static int get_q_type_pass(const char *p,
+ void (*cb)(unsigned char, void *),
+ void *ptr)
+{
+ char qbuf[strlen(p)+1];
+ char *q;
+
+ strcpy(qbuf, p);
+
+ for (q=qbuf; (q=strtok(q, ", \t\r\n")); q=0)
+ {
+ int n=rfc1035_type_strtoi(q);
+
+ if (n < 0)
+ return -1;
+
+ (*cb)(n, ptr);
+ }
+
+ return 0;
+}
+
+static void get_q_type_count(unsigned char c, void *ptr)
+{
+ ++*(size_t *)ptr;
+}
+
+static void get_q_type_save(unsigned char c, void *ptr)
+{
+ *(*(unsigned char **)ptr)++=c;
+}
+
+static unsigned char *get_q_type(const char *p)
+{
+ size_t n=0;
+ unsigned char *buf, *q;
+
+ errno=EINVAL;
+ if (get_q_type_pass(p, &get_q_type_count, &n) < 0)
+ return 0;
+
+ if ((buf=(unsigned char *)malloc(n+1)) == 0)
+ return 0;
+
+ q=buf;
+ get_q_type_pass(p, &get_q_type_save, &q);
+
+ *q=0;
+
+ return buf;
+}
+
int main(int argc, char **argv)
{
struct rfc1035_res res;
struct rfc1035_reply *replyp;
int argn;
const char *q_name;
-int q_type;
+unsigned char *q_type;
int q_class;
int q_xflag=0;
int q_rflag=0;
@@ -227,19 +280,27 @@ char ptrbuf[RFC1035_MAXNAMESIZE+1];
fprintf(stderr, "%s error.\n", errno == ENOENT ? "Hard":"Soft");
exit(1);
}
-
- q_type= -1;
+
+ q_type=0;
if (argn < argc)
{
if (strcmp(argv[argn], "spf") == 0)
- q_type= -2;
- else
- q_type=rfc1035_type_strtoi(argv[argn++]);
+ {
+ spflookup(q_name);
+ exit(0);
+ }
+ q_type=get_q_type(argv[argn]);
+ if (!q_type)
+ {
+ perror(argv[argn]);
+ exit(1);
+ }
+ argn++;
}
- if (q_type == -1)
- q_type=q_xflag ? RFC1035_TYPE_PTR:RFC1035_TYPE_ANY;
+ if (q_type == 0)
+ q_type=get_q_type(q_xflag ? "PTR":"ANY");
q_class= -1;
if (argn < argc)
@@ -247,22 +308,49 @@ char ptrbuf[RFC1035_MAXNAMESIZE+1];
if (q_class < 0)
q_class=RFC1035_CLASS_IN;
- if (q_type == -2)
+ if (q_type[0] && q_type[1])
{
- spflookup(q_name);
- exit(0);
- }
+ char namebuf[RFC1035_MAXNAMESIZE+1];
- replyp=rfc1035_resolve(&res, RFC1035_OPCODE_QUERY,
- q_name, q_type, q_class);
+ namebuf[0]=0;
+ strncat(namebuf, q_name, RFC1035_MAXNAMESIZE);
+ if (rfc1035_resolve_cname_multiple(&res, namebuf,
+ q_type, q_class,
+ &replyp,
+ RFC1035_X_RANDOMIZE)
+ < 0)
+ replyp=0;
+ }
+ else
+ {
+ replyp=rfc1035_resolve(&res, RFC1035_OPCODE_QUERY,
+ q_name, q_type[0], q_class);
+ }
+ free(q_type);
if (!replyp)
{
perror(argv[0]);
exit(1);
}
- rfc1035_dump(replyp, stdout);
+ if (q_type[0] && q_type[1])
+ {
+ struct rfc1035_reply *q;
+
+ for (q=replyp; q; q=q->next)
+ {
+ struct rfc1035_reply *s=q->next;
+
+ q->next=0;
+ rfc1035_dump(q, stdout);
+ q->next=s;
+ }
+ }
+ else
+ {
+ rfc1035_dump(replyp, stdout);
+ }
rfc1035_replyfree(replyp);
rfc1035_destroy_resolv(&res);
return (0);
diff --git a/rfc1035/testspf.c b/rfc1035/testspf.c
index a28482f..aad9567 100644
--- a/rfc1035/testspf.c
+++ b/rfc1035/testspf.c
@@ -23,6 +23,7 @@ static struct testsuite_s {
{"spf3.email-scan.com","192.168.1.10","spf1.email-scan.com","spf1","example.com"},
{"spf3.email-scan.com","191.168.2.10","spf1.email-scan.com","spf3.test","example.com"},
{"spf3.email-scan.com","1234:5678::9ABC","spf1.email-scan.com","spf1","example.com"},
+ {"spf3.испытание.email-scan.com","1234:5678::9ABC","испытание.email-scan.com","испытание.email-scan.com","испытание.email-scan.com"},
{"spf4.email-scan.com","192.168.2.10","spf1.email-scan.com","10-1-168-192","example.com"},
{"spf5.email-scan.com","::ffff:192.168.1.0","spf5.email-scan.com","helo","example.com"},
{"spf5.email-scan.com","::ffff:192.168.1.1","spf5.email-scan.com","helo","example.com"},
@@ -117,4 +118,3 @@ int main(int argc, char **argv)
return testspf(argv[1], argv[2], argv[3], argv[4], argv[5]);
}
-
diff --git a/rfc1035/testsuite.txt b/rfc1035/testsuite.txt
index aa98c1a..c500184 100644
--- a/rfc1035/testsuite.txt
+++ b/rfc1035/testsuite.txt
@@ -3,6 +3,7 @@ fail: Address does not pass the Sender Policy Framework
pass
fail: Error: l:postmaster, s:postmaster@spf3.email-scan.com, o:spf3.email-scan.com, d:spf3.email-scan.com, c:191.168.2.10, i:191.168.2.10, p:spf1.email-scan.com, v:in-addr, h:spf3.test, r:example.com, H:spf3%2Etest
fail: Error: l:postmaster, s:postmaster@spf3.email-scan.com, o:spf3.email-scan.com, d:spf3.email-scan.com, c:1234:5678::9ABC, i:1.2.3.4.5.6.7.8.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.9.a.b.c, p:spf1.email-scan.com, v:ip6, h:spf1, r:example.com, H:spf1
+fail: Error: l:postmaster, s:postmaster@spf3.xn--80akhbyknj4f.email-scan.com, o:spf3.xn--80akhbyknj4f.email-scan.com, d:spf3.xn--80akhbyknj4f.email-scan.com, c:1234:5678::9ABC, i:1.2.3.4.5.6.7.8.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.9.a.b.c, p:xn--80akhbyknj4
pass
pass
fail: Address does not pass the Sender Policy Framework