diff options
Diffstat (limited to 'rfc1035')
| -rw-r--r-- | rfc1035/configure.ac | 1 | ||||
| -rw-r--r-- | rfc1035/rfc1035.h | 98 | ||||
| -rw-r--r-- | rfc1035/rfc1035mxlist.c | 42 | ||||
| -rw-r--r-- | rfc1035/rfc1035qa.c | 31 | ||||
| -rw-r--r-- | rfc1035/rfc1035reply.c | 9 | ||||
| -rw-r--r-- | rfc1035/rfc1035resolve.c | 368 | ||||
| -rw-r--r-- | rfc1035/rfc1035search.c | 141 | ||||
| -rw-r--r-- | rfc1035/rfc1035udp.c | 202 | ||||
| -rw-r--r-- | rfc1035/spf.c | 62 | ||||
| -rw-r--r-- | rfc1035/testlookup.c | 118 | ||||
| -rw-r--r-- | rfc1035/testspf.c | 2 | ||||
| -rw-r--r-- | rfc1035/testsuite.txt | 1 | 
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(¤t_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(¤t_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 | 
