diff options
Diffstat (limited to 'rfc1035')
33 files changed, 7080 insertions, 0 deletions
| diff --git a/rfc1035/.gitignore b/rfc1035/.gitignore new file mode 100644 index 0000000..5d980f0 --- /dev/null +++ b/rfc1035/.gitignore @@ -0,0 +1,2 @@ +/testlookup +/testspf diff --git a/rfc1035/Makefile.am b/rfc1035/Makefile.am new file mode 100644 index 0000000..759ac78 --- /dev/null +++ b/rfc1035/Makefile.am @@ -0,0 +1,42 @@ +# +# Copyright 1998 - 2011 Double Precision, Inc.  See COPYING for +# distribution information. + + +noinst_LIBRARIES=librfc1035.a + +librfc1035_a_SOURCES= \ +		rfc1035.c rfc1035.h rfc1035an.c \ +		rfc1035dump.c rfc1035dumprrdata.c rfc1035fmttime.c \ +		rfc1035gettxt.c rfc1035ifconf.c \ +		rfc1035ipv6to4.c rfc1035mkaddress.c \ +		rfc1035mksocket.c rfc1035mxlist.c rfc1035mxlist.h rfc1035qa.c \ +		rfc1035qptr.c rfc1035reply.c rfc1035resolve.c rfc1035sameip.c \ +		rfc1035search.c rfc1035sockaddrip.c rfc1035sockaddrport.c \ +		rfc1035str.c rfc1035tcp.c rfc1035udp.c rfc1035bindsource.c \ +\ +		spf.c spf.h + +noinst_PROGRAMS=testlookup testspf + +testlookup_SOURCES=testlookup.c +testlookup_DEPENDENCIES=librfc1035.a ../rfc822/libencode.la ../md5/libmd5.la \ +	../random128/librandom128.la ../soxwrap/libsoxwrap.a \ +	../soxwrap/soxlibs.dep +testlookup_LDADD=librfc1035.a ../rfc822/libencode.la ../soxwrap/libsoxwrap.a ../md5/libmd5.la \ +	../random128/librandom128.la `cat ../soxwrap/soxlibs.dep` +testlookup_LDFLAGS=-static + +EXTRA_DIST=testsuite.txt + +testspf_SOURCES=testspf.c +testspf_DEPENDENCIES=librfc1035.a ../rfc822/libencode.la ../md5/libmd5.la \ +	../random128/librandom128.la ../soxwrap/libsoxwrap.a \ +	../soxwrap/soxlibs.dep +testspf_LDADD=librfc1035.a ../rfc822/libencode.la ../md5/libmd5.la \ +	../random128/librandom128.la ../soxwrap/libsoxwrap.a \ +	`cat ../soxwrap/soxlibs.dep` +testspf_LDFLAGS=-static + +mycheck: +	./testspf -test=1 | diff -U 3 - $(srcdir)/testsuite.txt diff --git a/rfc1035/configure.in b/rfc1035/configure.in new file mode 100644 index 0000000..94ead73 --- /dev/null +++ b/rfc1035/configure.in @@ -0,0 +1,390 @@ +dnl Process this file with autoconf to produce a configure script. +dnl +dnl Copyright 1998 - 2003 Double Precision, Inc.  See COPYING for +dnl distribution information. + +AC_INIT(librfc1035, 0.10, [courier-users@lists.sourceforge.net]) + +>confdefs.h  # Kill PACKAGE_ macros + +AC_CONFIG_SRCDIR(rfc1035.h) +AC_CONFIG_AUX_DIR(../..) +AM_INIT_AUTOMAKE([foreign no-define]) + +AM_CONFIG_HEADER(config.h) + +dnl Checks for programs. +AC_PROG_AWK +AC_PROG_CC +AC_PROG_INSTALL +AC_PROG_LN_S +AC_LIBTOOL_DLOPEN +AC_PROG_LIBTOOL + +dnl Checks for libraries. + +dnl Checks for header files. +AC_HEADER_STDC +AC_CHECK_HEADERS(sys/types.h sys/time.h unistd.h arpa/inet.h netinet/in.h) + +USENSL=no +saveLIBS="$LIBS" +AC_CHECK_LIB(socket,socket,result=yes,result=no) +if test $result = yes; then +        NETLIBS="-lsocket" +else +        AC_CHECK_LIB(socket,socket,result=yes,result=no,-lnsl) +        if test $result = yes; then +                NETLIBS = "-lsocket -lnsl" +                USENSL=yes +        else +                AC_CHECK_LIB(socket,connect,result=yes,result=no) +                if test $result = yes; then +                        NETLIBS="-lsocket" +                else +                        AC_CHECK_LIB(socket,connect,result=yes,result=no,-lnsl) +                        if test $result = yes; then +                                NETLIBS="-lsocket -lnsl" +                                USENSL=yes +                        fi +                fi +        fi +fi + +if test $USENSL != yes; then +	LIBS="$LIBS $NETLIBS" +	AC_TRY_LINK_FUNC(inet_addr, [ : ], +	[ +	        AC_CHECK_LIB(nsl,inet_addr,result=yes,result=no) +	        if test $result = yes; then +	                NETLIBS="$NETLIBS -lnsl" +	        fi +	]) +fi + +LIBS="$saveLIBS $NETLIBS" +AC_CHECK_LIB(socket,socket) + +dnl Checks for typedefs, structures, and compiler characteristics. +AC_C_CONST +AC_HEADER_TIME +AC_SYS_LARGEFILE + +AC_ARG_WITH(int32, +[  --with-int32='type'     use 'type' for an unsigned 32 bit integer type +                          ( default is 'unsigned')], +	int32="$withval", [ + +		AC_MSG_CHECKING(for uint32_t) + +		AC_TRY_COMPILE([ +#if HAVE_SYS_TYPES_H +#include	<sys/types.h> +#endif +		],[ +			uint32_t	i=0; +		], [ AC_MSG_RESULT(yes) ; int32="uint32_t"], [ + +		AC_MSG_RESULT(no) +		AC_MSG_CHECKING(for u_int_32_t) + +		AC_TRY_COMPILE([ +#if HAVE_SYS_TYPES_H +#include	<sys/types.h> +#endif +			],[ +				u_int32_t	i=0; +			], [AC_MSG_RESULT(yes); int32="u_int32_t"],[ + +			AC_MSG_RESULT(no) + +			AC_CHECK_SIZEOF(unsigned, 0) +			if test "$ac_cv_sizeof_unsigned" != 4 +			then +				AC_CHECK_SIZEOF(unsigned long, 0) +				if test "$ac_cv_sizeof_unsigned_long" != 4 +				then +					AC_CHECK_SIZEOF(unsigned short, 0) +					if test "$ac_cv_sizeof_unsigned_short" != 4 +					then +						AC_ERROR(--with-int32 option is required) +					fi +					int32="unsigned short" +				fi +				int32="unsigned long" +			else +				int32="unsigned" +			fi +			]) +		]) +	] +) +UINT32="$int32" + +AC_ARG_WITH(int16, +[  --with-int16='type'     use 'type' for an unsigned 16 bit integer type +                          ( default is 'unsigned')], +	int16="$withval", [ + +		AC_MSG_CHECKING(for uint16_t) + +		AC_TRY_COMPILE([ +#if HAVE_SYS_TYPES_H +#include	<sys/types.h> +#endif +		],[ +			uint16_t	i=0; +		], [ AC_MSG_RESULT(yes) ; int16="uint16_t"], [ + +		AC_MSG_RESULT(no) +		AC_MSG_CHECKING(for u_int_16_t) + +		AC_TRY_COMPILE([ +#if HAVE_SYS_TYPES_H +#include	<sys/types.h> +#endif +			],[ +				u_int16_t	i=0; +			], [AC_MSG_RESULT(yes); int16="u_int16_t"],[ + +			AC_MSG_RESULT(no) + +			AC_CHECK_SIZEOF(unsigned, 0) +			if test "$ac_cv_sizeof_unsigned" != 4 +			then +				AC_CHECK_SIZEOF(unsigned long, 0) +				if test "$ac_cv_sizeof_unsigned_long" != 4 +				then +					AC_CHECK_SIZEOF(unsigned short, 0) +					if test "$ac_cv_sizeof_unsigned_short" != 4 +					then +						AC_ERROR(--with-int16 option is required) +					fi +					int16="unsigned short" +				fi +				int16="unsigned long" +			else +				int16="unsigned" +			fi +			]) +		]) +	] +) +UINT16="$int16" + +AC_CACHE_CHECK([for socklen_t], +	courier_cv_hassocklen_t, +  +AC_COMPILE_IFELSE([ +AC_LANG_SOURCE( [ +#include <sys/types.h> +#include <sys/socket.h> +  +socklen_t sl_t; +],[ +	accept(0, 0, &sl_t); +])], +	courier_cv_hassocklen_t=yes, +	courier_cv_hassocklen_t=no) +) +  +socklen_t="int" +  +if test $courier_cv_hassocklen_t = yes +then +	: +else +	AC_DEFINE_UNQUOTED(socklen_t, int, [ Default definition for socklen_t ]) +fi + +dnl Checks for library functions. + +AC_CHECK_FUNCS(strcasecmp) + +dnl Other checks + +if test "$GCC" = "yes" +then +	CFLAGS="$CFLAGS -Wall" +fi + +CFLAGS="$CFLAGS -I$srcdir/.. -I.." + +dnl Check for IPv6 support + +AC_CACHE_CHECK([for structs in6_addr, sockaddr_in6, and sockaddr_storage], +	rfc1035_cv_hasipv6structs, + +AC_TRY_COMPILE( [ +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> + +        ], [ +struct in6_addr in6a; +struct sockaddr_in6 sain6; +struct sockaddr_storage soas; +int x=PF_INET6; + +	], rfc1035_cv_hasipv6structs=yes, +                rfc1035_cv_hasipv6structs=no ) +) + +changequote() + +LB='[' +RB=']' + +changequote([,]) + +AC_CACHE_CHECK([for IPv6 flavor], +	rfc1035_cv_ipv6flavor, + +if test "$rfc1035_cv_hasipv6structs" = no +then +	rfc1035_cv_ipv6flavor=none +else +AC_TRY_COMPILE( [ +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +] , [ +struct in6_addr in6a; + +	in6a.s6_addr16 $LB 0 $RB =0; +	in6a.s6_addr32 $LB 0 $RB =0; +], +	rfc1035_cv_ipv6flavor="glibc (default)", + +AC_TRY_COMPILE( [ +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> + +        ], [ +struct in6_addr in6a; + +	in6a.__u6_addr.__u6_addr16 $LB 0 $RB =0; +	in6a.__u6_addr.__u6_addr32 $LB 0 $RB =0; +	], +		rfc1035_cv_ipv6flavor="freebsd-4.0", + +AC_TRY_COMPILE( [ +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> + +        ], [ +struct in6_addr in6a; + +       in6a._S6_un._S6_u8 $LB 0 $RB = 0; +       in6a._S6_un._S6_u32 $LB 0 $RB = 0; + +       ], +		rfc1035_cv_ipv6flavor="solaris8", +		rfc1035_cv_ipv6flavor="unknown" +		) +	) +) +fi +) + +RFC1035_FREEBSD40=0 +if test "$rfc1035_cv_ipv6flavor" = "freebsd-4.0" +then +	RFC1035_FREEBSD40=1 +	AC_DEFINE_UNQUOTED(RFC1035_FREEBSD40,1, +	[ Whether IPv6 support is FreeBSD-4.0 style ]) +fi +AC_SUBST(RFC1035_FREEBSD40) + +RFC1035_SOLARIS8=0 + +if test "$rfc1035_cv_ipv6flavor" = "solaris8" +then +	RFC1035_SOLARIS8=1 +	AC_DEFINE_UNQUOTED(RFC1035_SOLARIS8,1, +	[ Whether IPv6 support is Solaris style ]) +fi +AC_SUBST(RFC1035_SOLARIS8) + +AC_CACHE_CHECK([for SIOCGIFCONF], +	rfc1035_cv_siocgifconf, + +AC_TRY_COMPILE( [ + +#include <sys/types.h> +#include <sys/ioctl.h> +#include <net/if.h> +#include <unistd.h> +] , [ +	struct ifreq ifreq_buf; +	struct ifconf ifc; + +	ifc.ifc_len=sizeof(ifreq_buf); +	ifc.ifc_req=&ifreq_buf; + +	ioctl(0, SIOCGIFCONF, &ifc); +], rfc1035_cv_siocgifconf=yes, rfc1035_cv_siocgifconf=no) +) + +if test "$rfc1035_cv_siocgifconf" = "yes" +then +	AC_DEFINE_UNQUOTED(HAVE_SIOCGIFCONF,1, +	[ Whether SIOCGIFCONF ioctl is available ]) +fi + +AC_CACHE_CHECK([for alloca], +	rfc1035_cv_alloca, + +AC_TRY_COMPILE( [ +#include <stdio.h> +#include <stdlib.h> + +        ], [ +char *p=(char *)alloca(10); + +	], rfc1035_cv_alloca=yes, +                rfc1035_cv_alloca,=no ) +) + +ipv6=0 +if test "$rfc1035_cv_hasipv6structs$rfc1035_cv_alloca" = yesyes +then +	if test "$rfc1035_cv_ipv6flavor" != "unknown" +	then +		AC_CHECK_FUNC(inet_pton, [ +			AC_CHECK_FUNC(inet_ntop, ipv6=1) +] +) +	fi +fi + +AC_ARG_WITH(ipv6, [ --without-ipv6               Disable IPv6 support], +[ +case $withval in +y*|Y*) +	if test "$ipv6" = 0 +	then +		AC_MSG_ERROR(IPv6 support not available) +	fi +	;; +*) +	ipv6=0 +	;; +esac +] +) + +RFC1035_IPV6="$ipv6" + +AC_DEFINE_UNQUOTED(RFC1035_UINT32, $UINT32, [ 32bit datatype ]) +AC_DEFINE_UNQUOTED(RFC1035_UINT16, $UINT16, [ 16bit datatype ]) +AC_DEFINE_UNQUOTED(RFC1035_IPV6, $RFC1035_IPV6, +	[ Whether IPv6 support is enabled ]) + +AC_OUTPUT(Makefile) diff --git a/rfc1035/rfc1035.c b/rfc1035/rfc1035.c new file mode 100644 index 0000000..0e3678b --- /dev/null +++ b/rfc1035/rfc1035.c @@ -0,0 +1,496 @@ +/* +** Copyright 1998 - 2011 Double Precision, Inc. +** See COPYING for distribution information. +*/ + +#include	"config.h" +#include	<stdio.h> +#include	"soxwrap/soxwrap.h" +#include	<stdlib.h> +#include	<string.h> +#include	<errno.h> +#include	<sys/types.h> +#if TIME_WITH_SYS_TIME +#include	<sys/time.h> +#include	<time.h> +#else +#if HAVE_SYS_TIME_H +#include	<sys/time.h> +#else +#include	<time.h> +#endif +#endif +#include	<arpa/inet.h> +#if	HAVE_UNISTD_H +#include	<unistd.h> +#endif + +#include	"rfc1035.h" + + +#define ISSPACE(c) (strchr(" \t\r\n", (int)(unsigned char)(c)) != NULL) + +#if RFC1035_IPV6 + +#else + +struct in_addr	rfc1035_addr_any={INADDR_ANY}; + +#endif + +void rfc1035_init_timeout(struct rfc1035_res *res, unsigned s, unsigned n) +{ +	res->rfc1035_timeout_initial=s; +	res->rfc1035_timeout_backoff=n; +} + +void rfc1035_init_ns(struct rfc1035_res *res, const RFC1035_ADDR *a, unsigned n) +{ +unsigned i; +unsigned j; + +	j=0; + +	random128_binary(&res->randseed); +	md5_digest(&res->randseed, sizeof(res->randseed), res->randbuf); +	res->randptr=0; + +	for (i=0; i == 0 || (i<n && i<RFC1035_MAXNS); i++) +	{ +#if RFC1035_IPV6 +	struct in6_addr sin; + +		if (n == 0) +			sin=in6addr_loopback; +		else +			sin=a[(j+i)%n]; + +#else +	struct in_addr sin; + +		if (n == 0) +		{ +			rfc1035_aton("127.0.0.1", &sin); +		} +		else +			sin=a[(j+i)%n]; +		memset(&res->nameservers[i], 0, sizeof(res->nameservers[i])); +		res->nameservers[i]=sin; +#endif +		res->nameservers[i]=sin; +	} +	res->rfc1035_nnameservers=i; + +} + +int rfc1035_init_defaultdomain(struct rfc1035_res *res, const char *p) +{ +char	*q; + +	if (res->rfc1035_defaultdomain) +		free(res->rfc1035_defaultdomain); + +	if ((res->rfc1035_defaultdomain=malloc(strlen(p)+1)) == 0) +		return (-1); + +	strcpy(res->rfc1035_defaultdomain, p); +	for (q=res->rfc1035_defaultdomain; *q; q++) +		if (ISSPACE(*q)) +		{ +			*q=0; +			break; +		} + +	return (0); +} + +void rfc1035_init_dnssec_enable(struct rfc1035_res *res, int flag) +{ +	rfc1035_init_edns_payload(res, flag ? 1280:0); +} + + +void rfc1035_init_edns_payload(struct rfc1035_res *res, int payload_size) +{ +	res->dnssec_payload_size=payload_size; +} + +static char tl(char c) +{ +	if (c >= 'A' && c <= 'Z') +		c += 'a' - 'A'; + +	return c; +} + +void rfc1035_init_resolv(struct rfc1035_res *res) +{ +FILE	*fp=fopen("/etc/resolv.conf", "r"); +char rfc1035_buf[512]; +RFC1035_ADDR ns[RFC1035_MAXNS]; +int nns=0; + +	memset(res, 0, sizeof(*res)); + +	while (fp && fgets(rfc1035_buf, sizeof(rfc1035_buf), fp)) +	{ +	char	*p; + +		for (p=rfc1035_buf; *p; p++) +			*p=tl(*p); + +		for (p=rfc1035_buf; *p; p++) +			if (ISSPACE(*p))	break; +		if (*p)	*p++=0; + +		if (strcmp(rfc1035_buf, "domain") == 0) +		{ +			while (p && ISSPACE(*p)) +				++p; +			rfc1035_init_defaultdomain(res, p); +			continue; +		} + +		if (strcmp(rfc1035_buf, "nameserver"))	continue; +		while (*p && ISSPACE(*p))	p++; +		if (nns < RFC1035_MAXNS) +		{ +		char	*q; + +			for (q=p; *q && !ISSPACE(*q); q++) +				; +			*q=0; + +			if (rfc1035_aton(p, &ns[nns++]) < 0) +				--nns; +		} +	} +	if (fp) fclose(fp); + +	rfc1035_init_ns(res, ns, nns); +} + +void rfc1035_destroy_resolv(struct rfc1035_res *res) +{ +	if (res->rfc1035_defaultdomain) +	{ +		free(res->rfc1035_defaultdomain); +	} +} + +/************/ + +struct compresslist { +	struct compresslist *next; +	unsigned offset; +	const char *ptr; +	} ; + +static int mkpacketq(void (*)(const char *, unsigned, void *), void *, +		unsigned *, +		const struct rfc1035_query *, +		unsigned, +		const char *, +		struct compresslist *, +		struct rfc1035_res *); + +unsigned rfc1035_next_randid(struct rfc1035_res *res) +{ +	unsigned i; + +	if (res->randptr >= sizeof(res->randbuf)) +	{ +		for (i=0; i<sizeof(res->randseed); i++) +			if ( ++((unsigned char *)res->randseed)[i]) +				break; + +		md5_digest(res->randseed, sizeof(res->randseed), +			   res->randbuf); +		res->randptr=0; +	} + +	i= ((unsigned)((unsigned char *)res->randbuf)[res->randptr] << 8) | +		((unsigned char *)res->randbuf)[res->randptr+1]; +	res->randptr += 2; +	return i; +} + +int rfc1035_mkquery(struct rfc1035_res *res,	/* resolver */ +			unsigned opcode,	/* opcode */ +			const struct rfc1035_query *questions, +			unsigned nquestions, +			void (*func)(const char *, unsigned, void *), void *arg) +{ +struct { +	unsigned char idhi, idlo; +	unsigned char infohi, infolo; +	unsigned char qhi, qlo; +	unsigned char ahi, alo; +	unsigned char nhi, nlo; +	unsigned char auhi, aulo; +	} header; +unsigned cnt; + +	unsigned id=rfc1035_next_randid(res); + +	header.idhi= id >> 8; +	header.idlo= id; + +	header.infohi= (opcode << 3) & 0x78; + +	if (!res->norecursive) +		header.infohi |= 1; /* Want a recursive query */ +	header.infolo=0; +	header.qhi=nquestions >> 8; +	header.qlo=nquestions; +	header.ahi=0; +	header.alo=0; +	header.nhi=0; +	header.nlo=0; +	header.auhi=0; +	header.aulo=0; + +	if (res->dnssec_payload_size) +		header.aulo=1; + +	(*func)( (const char *)&header, sizeof(header), arg); +	cnt=sizeof(header); +	if (nquestions) +		if (mkpacketq(func, arg, &cnt, questions, nquestions, +			questions->name, 0, res))	return (-1); + +	if (res->dnssec_payload_size) +	{ +		/* RFC 2671, section 4.3 */ + +		struct { +			char opt_root_domain_name; +			char opt_type_hi; +			char opt_type_lo; +			char opt_class_hi; +			char opt_class_lo; +			char opt_extendedrcode; +			char opt_version; +			char opt_ttl_zhi; +			char opt_ttl_zlo; +			char opt_rdlen_hi; +			char opt_rdlen_lo; +		} rfc2671_43; + +		memset(&rfc2671_43, 0, sizeof(rfc2671_43)); + +		rfc2671_43.opt_type_lo=RFC1035_TYPE_OPT; + +		rfc2671_43.opt_class_hi= res->dnssec_payload_size >> 8; +		rfc2671_43.opt_class_lo= res->dnssec_payload_size; +		rfc2671_43.opt_ttl_zhi |= 0x80; /* RFC 3225 */ + +		(*func)((char *)&rfc2671_43, sizeof(rfc2671_43), arg); +	} + +	return (0); +} + +int rfc1035_hostnamecmp(const char *p, const char *q) +{ +	while (*p || *q) +	{ +		if (*p == '.' || *q == '.' ) +		{ +			if ( (*p && *p != '.') || (*q && *q != '.')) +				return (1); +			while (*p == '.')	++p; +			while (*q == '.')	++q; +			continue; +		} +		if (!*p || !*q)	return (1); +		if ( tl(*p) != tl(*q))	return (1); +		++p; +		++q; +	} +	return (0); +} + +static struct compresslist *search(struct compresslist *cp, const char *name) +{ +	for ( ; cp; cp=cp->next) +	{ +		if (rfc1035_hostnamecmp(name, cp->ptr) == 0 && +			(cp->offset & 0x3FFF) == cp->offset) +			return (cp); +			/* Packet compression uses the two high bits */ +	} +	return (0); +} + +static int mkpacketq_full(void (*)(const char *, unsigned, void *), +		void *, +		unsigned *, +		const struct rfc1035_query *, +		unsigned, +		const char *, +		struct compresslist *, struct rfc1035_res *); + +static int mkpacketq(void (*func)(const char *, unsigned, void *), void *arg, +		unsigned *cnt, +		const struct rfc1035_query *qp, +		unsigned nqp, +		const char *nameptr, +		struct compresslist *comp_list, +		struct rfc1035_res *res) +{ +char	*buf; +int	rc; + + +	if (!res->rfc1035_defaultdomain || strchr(nameptr, '.')) +		return (mkpacketq_full(func, arg, cnt, qp, nqp, nameptr, +			comp_list, res)); + +	/* Append default domain */ + +	if ((buf=malloc(strlen(nameptr)+ +		strlen(res->rfc1035_defaultdomain)+2)) == 0) +		return (-1); + +	strcat(strcat(strcpy(buf, nameptr), "."), +		res->rfc1035_defaultdomain); + +	rc=mkpacketq_full(func, arg, cnt, qp, nqp, buf, comp_list, res); +	free(buf); +	return (rc); +} + +static int mkpacketq_full(void (*func)(const char *, unsigned, void *), +		void *arg, +		unsigned *cnt, +		const struct rfc1035_query *qp, +		unsigned nqp, +		const char *nameptr, +		struct compresslist *comp_list, +		struct rfc1035_res *res) +{ +unsigned llen; +struct	compresslist *cp; + +	while (nameptr && *nameptr == '.') +		++nameptr; + +	if (!nameptr || !*nameptr) +	{ +	struct { +		unsigned char padtail; +		unsigned char qtypehi, qtypelo; +		unsigned char qclasshi, qclasslo; +		} qtail; + +		qtail.padtail=0; +		qtail.qtypehi=qp->qtype >> 8; +		qtail.qtypelo=qp->qtype; +		qtail.qclasshi=qp->qclass >> 8; +		qtail.qclasslo=qp->qclass; + +		(*func)((const char *)&qtail, sizeof(qtail), arg); +		++qp; +		--nqp; +		*cnt += sizeof(qtail); +		if (nqp) +			return (mkpacketq(func, arg, cnt, +				qp, nqp, qp->name, comp_list, res)); +		return (0); +	} + +	for (llen=0; nameptr[llen] && nameptr[llen] != '.'; llen++) +		; +	cp=search(comp_list, nameptr); +	if (cp) +	{ +	struct { +		unsigned char ptrhi, ptrlo; +		unsigned char qtypehi, qtypelo; +		unsigned char qclasshi, qclasslo; +		} qtail; + +		qtail.ptrhi= (cp->offset >> 8) | 0xC0; +		qtail.ptrlo= cp->offset; +		qtail.qtypehi=qp->qtype >> 8; +		qtail.qtypelo=qp->qtype; +		qtail.qclasshi=qp->qclass >> 8; +		qtail.qclasslo=qp->qclass; + +		(*func)( (const char *)&qtail, sizeof(qtail), arg); +		++qp; +		--nqp; +		*cnt += sizeof(qtail); + +		if (nqp) +			return (mkpacketq(func, arg, cnt, +				qp, nqp, qp->name, comp_list, res)); +	} +	else +	{ +	unsigned n=llen; +	unsigned char c; +	struct compresslist newc; + +		if (n > 63)	return (-1); + +		newc.next=comp_list; +		newc.offset= *cnt; +		newc.ptr=nameptr; + +		c=(unsigned char)n; +		(*func)((const char *) &c, 1, arg); +		(*func)( nameptr, c, arg); +		*cnt += 1+c; +		return (mkpacketq_full(func, arg, cnt, +				qp, nqp, nameptr+llen, &newc, res)); +	} +	return (0); +} + +/*******************************************************/ + +int rfc1035_wait_reply(int fd, unsigned nsecs) +{ +fd_set	fds; +struct	timeval tv; +int	n; + +	FD_ZERO(&fds); +	FD_SET(fd, &fds); +	tv.tv_sec=nsecs; +	tv.tv_usec=0; +	while ((n=sox_select(fd+1, &fds, 0, 0, &tv)) < 0) +	{ +		if (errno != EINTR) +			break; +	} + +	if (n > 0 && FD_ISSET(fd, &fds)) +		return (0); +	errno=ETIMEDOUT; +	return (-1); +} + +int rfc1035_wait_query(int fd, unsigned nsecs) +{ +fd_set	fds; +struct	timeval tv; +int	n; + +	FD_ZERO(&fds); +	FD_SET(fd, &fds); +	tv.tv_sec=nsecs; +	tv.tv_usec=0; +	while ((n=sox_select(fd+1, 0, &fds, 0, &tv)) < 0) +	{ +		if (errno != EINTR) +			break; +	} + +	if (n > 0 && FD_ISSET(fd, &fds)) +		return (0); +	errno=ETIMEDOUT; +	return (-1); +} diff --git a/rfc1035/rfc1035.h b/rfc1035/rfc1035.h new file mode 100644 index 0000000..1d6eb31 --- /dev/null +++ b/rfc1035/rfc1035.h @@ -0,0 +1,630 @@ +/* +** Copyright 1998 - 2011 Double Precision, Inc. +** See COPYING for distribution information. +*/ + +#ifndef	rfc1035_h +#define	rfc1035_h + + + +#if	HAVE_CONFIG_H +#include "rfc1035/config.h" +#endif + +#include	"random128/random128.h" +#include	"md5/md5.h" + +#include	<stdio.h> +#include	<sys/types.h> +#include	<sys/socket.h> +#if HAVE_NETINET_IN_H +#include	<netinet/in.h> +#endif + +#ifdef  __cplusplus +extern "C" { +#endif + +#if RFC1035_IPV6 +typedef struct in6_addr	RFC1035_ADDR; +typedef struct sockaddr_in6 RFC1035_SOCKADDR; +typedef struct sockaddr_storage RFC1035_NETADDR; +#define RFC1035_ADDRANY		in6addr_any +#else +typedef	struct in_addr	RFC1035_ADDR; +typedef struct sockaddr_in	RFC1035_SOCKADDR; +typedef struct sockaddr		RFC1035_NETADDR; + +extern struct in_addr rfc1035_addr_any; +#define RFC1035_ADDRANY		rfc1035_addr_any +#endif + +#define RFC1035_TYPE_A		1 +#define RFC1035_TYPE_NS		2 +#define RFC1035_TYPE_MD		3 +#define RFC1035_TYPE_MF		4 +#define RFC1035_TYPE_CNAME	5 +#define RFC1035_TYPE_SOA	6 +#define RFC1035_TYPE_MB		7 +#define RFC1035_TYPE_MG		8 +#define RFC1035_TYPE_MR		9 +#define RFC1035_TYPE_NULL	10 +#define RFC1035_TYPE_WKS	11 +#define RFC1035_TYPE_PTR	12 +#define RFC1035_TYPE_HINFO	13 +#define RFC1035_TYPE_MINFO	14 +#define RFC1035_TYPE_MX		15 +#define RFC1035_TYPE_TXT	16 + +#define	RFC1035_TYPE_AAAA	28	/* RFC 1886. Even if we don't have +					IPv6 */ + +#define RFC1035_TYPE_OPT	41 +#define RFC1035_TYPE_RRSIG	46 + +#define	RFC1035_TYPE_AXFR	252 +#define	RFC1035_TYPE_MAILB	253 +#define	RFC1035_TYPE_MAILA	254 +#define	RFC1035_TYPE_ANY	255 + +void rfc1035_type_itostr(int, void (*)(const char *, void *), void *); +int rfc1035_type_strtoi(const char *); + +#define	RFC1035_CLASS_IN	1 +#define	RFC1035_CLASS_CSNET	2 +#define	RFC1035_CLASS_CHAOS	3 +#define	RFC1035_CLASS_HESIOD	4 +#define	RFC1035_CLASS_ANY	255 + +const char *rfc1035_class_itostr(int); +int rfc1035_class_strtoi(const char *); + +#define	RFC1035_OPCODE_QUERY	0 +#define	RFC1035_OPCODE_IQUERY	1 +#define	RFC1035_OPCODE_STATUS	2 + +const char *rfc1035_opcode_itostr(int); +int rfc1035_opcode_strtoi(const char *); + +#define RFC1035_RCODE_NOERROR	0 +#define	RFC1035_RCODE_FORMAT	1 +#define	RFC1035_RCODE_SERVFAIL	2 +#define	RFC1035_RCODE_NXDOMAIN	3 +#define	RFC1035_RCODE_UNIMPLEMENTED	4 +#define	RFC1035_RCODE_REFUSED	5 + +const char *rfc1035_rcode_itostr(int); +int rfc1035_rcode_strtoi(const char *); + +struct rfc1035_query { +	const char *name; +	unsigned qtype, qclass; +	} ; + +struct rfc1035_reply;	/* Defined below */ +struct rfc1035_rr;	/* Defined below */ + +/* +** The init family of functions perform various initializations.  Calling them +** is optional, as librfc1035.a will use defaults if not specified. +*/ + +#define	RFC1035_MAXNS	10 +#define	RFC1035_DEFAULT_INITIAL_TIMEOUT	5 +#define	RFC1035_DEFAULT_MAXIMUM_BACKOFF	3 + +		/* Resolver state */ +struct rfc1035_res { + +	RFC1035_ADDR nameservers[RFC1035_MAXNS]; +	int rfc1035_nnameservers; + +	char *rfc1035_defaultdomain; +	int norecursive; /* Do not set the recursive flag, for specialized apps */ +	int dnssec_payload_size; /* Enable dnssec requests */ + +	unsigned rfc1035_good_ns; +	unsigned rfc1035_timeout_initial;	/* Initial timeout */ +	unsigned rfc1035_timeout_backoff;	/* Maximum exponential backoff */ + +	random128binbuf randseed; +	MD5_DIGEST	randbuf; +	unsigned randptr; +	} ; + +extern struct rfc1035_res rfc1035_default_resolver; + +void rfc1035_init_timeout(struct rfc1035_res *, unsigned, unsigned); +					/* +					** Specify timeout in seconds, +					** and maximum exponential backoff. +					*/ +void rfc1035_init_ns(struct rfc1035_res *, const RFC1035_ADDR *, unsigned); +		/* Specify nameservers to query (max 10) */ + +void rfc1035_init_norecursive(struct rfc1035_res *, int); +	/* Set the no-recursive flag, if you don't want the NS to do recursive queries on your behalf */ + +void rfc1035_init_dnssec_enable(struct rfc1035_res *, int); +	/* Enable/disable dnssec/edns0 */ + +void rfc1035_init_edns_payload(struct rfc1035_res *, int); +	/* Set edns0 payload size */ + +void rfc1035_init_resolv(struct rfc1035_res *); +	/* Read /etc/resolv.conf for nameservers */ + +void rfc1035_destroy_resolv(struct rfc1035_res *); +	/* Destroy the resolver object */ + +	/* +	** Most people will only need to call rfc1035_resolve or +	** rfc1035_resolve_cname.  The return value from _resolve functions +	** should be interpreted as follows: +	**     NULL - internal failure (treat it as a soft DNS error) +	**     ptr->rcode = RFC1035_RCODE_NOERROR - success +	**     ptr->rcode = RFC1035_RCODE_NXDOMAIN - hard DNS error +	**     ptr->rcode = RFC1035_RCODE_TEMPFAIL - soft DNS error +	**     ptr->rcode = any other value - log abnormal result, +	**                  handle as soft DNS error +	*/ + +struct rfc1035_reply *rfc1035_resolve( +	struct rfc1035_res *,	/* Pointer to a resolver structure */ +	int,		/* Opcode, see above. */ +	const char *,	/* Query name */ +	unsigned,	/* Query type, see above. */ +	unsigned);	/* Query class, see above. */ + +	/* +	** Multiple queries.  Most servers don't support this. +	*/ + +struct rfc1035_reply *rfc1035_resolve_multiple( +	struct rfc1035_res *,	/* Pointer to a resolver structure */ +	int,	/* opcode */ +	const struct rfc1035_query *,	/* Array of queries */ +	unsigned);			/* Array size */ + +/* +** rfc1035_resolve_cname is like _resolve, but starts with the default +** servers, and automatically reissues the query if the received response +** is a CNAME.  If successful, it returns an INDEX into the allrrs array +** containing the first answer.  To get the next answers, call +** rfc1035_replysearch_all with return value+1.  If not succesfull, -1 +** is returned. +** +** Note - if the returned index points to a CNAME, this is because a CNAME +** pointed to another CNAME -- it's BAD! +** +** It takes a POINTER to the 'id' counter, which is incremented if a +** second query needs to be issued. +** +** If takes a POINTER to the rfc1035_reply structure, which will be either +** null on exit, or point to the reply received.  The pointer MAY be not null +** even if the return value is -1. +** +** Suggested logic: +**    Return value >= 0, succesfull query. +**    Return value <= -1, ptr is not null, and rcode = RFC1035_RCODE_NXDOMAIN, +**        hard DNS error. +**    Return value <= -1, ptr is not null, and rcode = RFC1035_RCODE_TEMPFAIL, +**        soft DNS error. +**    Other situations: log the abnormal result, handle as soft DNS error. +*/ + +#define	RFC1035_ERR_CNAME_RECURSIVE	-2 +	/* Specific error code for a recursive CNAME record - prohibited */ + +int rfc1035_resolve_cname( +		struct rfc1035_res *,	/* Pointer to a resolver structure */ +		char *,			/* RFC1035_MAXNAMESIZE buffer with +					** the name to query */ +		unsigned,		/* Query type */ +		unsigned,		/* Query class */ +		struct rfc1035_reply **, /* Ptr set to reply received */ +		int); /* Extended flags: */ + +#define RFC1035_X_RANDOMIZE 1    /* Randomize query results */ + +	/* +	** Always call replyfree when done. +	*/ + +void rfc1035_replyfree(struct rfc1035_reply *); + +	/* +	** !!!ALL!!! the const char * pointers in rfc1035_reply are NOT +	** standard C strings, but DNS-compressed strings.  Call +	** replyhostname to translate those to C strings. +	*/ + +const char *rfc1035_replyhostname( +	const struct rfc1035_reply *,	/* The reply */ +	const char *,	/* The const char ptr */ + +	char *);	/* Buffer where to put hostname.  All strings are +			** guaranteed to fit into RFC1035_MAXNAMESIZE+1 byte +			** buffer. +			** replyhostname returns this pointer. +			*/ + +/* Some value added code, look up A and PTR records. */ + +int rfc1035_a(struct rfc1035_res *, +	const char *,		/* Host name */ +	RFC1035_ADDR **,		/* We allocate array of IP addresses */ +	unsigned *);			/* We return # of IP addresses here */ + +int rfc1035_ptr(struct rfc1035_res *, +	const RFC1035_ADDR *,	/* Query PTR for this address */ +	char *);		/* Result - RFC1035_MAXNAMESIZE+1 buf */ + +int rfc1035_ptr_x(struct rfc1035_res *res, const RFC1035_ADDR *addr, +		  void (*cb_func)(const char *, void *), +		  void *cb_arg); /* Invoke a callback function instead +				 ** (multiple callbacks possible) +				 */ + +/* ---------------------- */ + +	/* Replyuncompress is a lower-level function taking a pointer to +	** the const char *.  When it returns, the const char * is advanced +	** past the end of the compressed string in the DNS data. +	** replyuncompress returns its third argument, or NULL if there was +	** an error. */ + +const char *rfc1035_replyuncompress(const char **, +	const struct rfc1035_reply *, char *); + +#define	RFC1035_MAXNAMESIZE	255 + + +	/* +	** Compare two hostnames.  Return 0 if they match, non-zero if they +	** don't. +	*/ + +int rfc1035_hostnamecmp(const char *, const char *); + +	/* +	** After we receive a reply, search for the answer there.  Returns +	** an index in the respective section, or -1 if not found. +	** If we find a CNAME, we return a pointer to it instead, so make +	** sure to check for that! +	*/ + +int rfc1035_replysearch_an( +	const struct rfc1035_res *,	/* The resolver */ +	const struct rfc1035_reply *,	/* The reply */ +	const char *, 			/* Hostname to search */ +	unsigned,			/* Type */ +	unsigned,			/* Class */ +	int);				/* Starting position, 1st time use 0 */ + +int rfc1035_replysearch_ns( +	const struct rfc1035_res *,	/* The resolver */ +	const struct rfc1035_reply *,	/* The reply */ +	const char *, 			/* Hostname to search */ +	unsigned,			/* Type */ +	unsigned,			/* Class */ +	int);				/* Starting position, 1st time use 0 */ + +int rfc1035_replysearch_all( +	const struct rfc1035_res *,	/* The resolver */ +	const struct rfc1035_reply *,	/* The reply */ +	const char *, 			/* Hostname to search */ +	unsigned,			/* Type */ +	unsigned,			/* Class */ +	int);				/* Starting position, 1st time use 0 */ + +/* +** Low level functions follow. +*/ + +	/* +	** rfc1035_mkquery() constructs a query to be sent.  The query is +	** composed by REPEATEDLY running the caller-provided function, +	** which will be called REPEATEDLY to build the query, part by part. +	*/ + +int rfc1035_mkquery(struct rfc1035_res *,	/* resolver structure */ +			unsigned,	/* opcode */ + +#define	RFC1035_RESOLVE_RECURSIVE 1	/* Ask nameserver to do the recursion */ + +			const struct rfc1035_query *,	/* questions */ +			unsigned,	/* Number of questions */ +			void (*)(const char *, unsigned, void *), +					/* Function - called repetitively +					** to build the query */ +			void *);	/* Third arg to function */ + +/**************************************************************************/ +/* Low level input/output functions.  Most people won't need to use these */ +/**************************************************************************/ + +int rfc1035_open_udp(int *af);		/* Create a UDP socket */ + +int rfc1035_send_udp(int,	/* File descriptor from rfc1035_open */ +		const struct sockaddr *, int, /* Send to this name server */ +		const char *,	/* The query */ +		unsigned);	/* Query length */ +	/* +	** Returns 0, or non-zero if failed. +	*/ + +int rfc1035_wait_reply(int,	/* File descriptor from rfc1035_open */ +	unsigned);	/* Number of seconds to wait, use 0 for default */ +	/* Returns 0 when reply is waiting, non-0 if timeout expired */ + +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 */ +	const char *,		/* query */ +	unsigned,		/* query length */ +	int *,			/* # of bytes received */ +	unsigned);		/* # of seconds to wait for response */ + +	/* +	** Jumbo function: sends the indicated query via UDP, waits for +	** a validated reply.  Returns pointer to dynamically allocated +	** memory with the reply.  Returns NULL if there was an error. +	** errno will be set to EAGAIN if the response timed out +	** (if the UDP stack returns an error, we fake an EAGAIN). +	** After getting EAGAIN, attempt number should be incremented, +	** and we should try again. +	** Fake ETIMEDOUT is returned if no more attempts are possible. +	*/ + +int rfc1035_open_tcp(struct rfc1035_res *, const RFC1035_ADDR *); +				/* +				** Create a TCP socket for this attempt #, +				** returns negative for a failure, and sets +				** errno. +				*/ + +	/* +	** Attempt to transmit the indicated query on this TCP socket. +	** Return 0 for successfull transmission. +	*/ + +int rfc1035_send_tcp(int,	/* file descriptor */ +		const char *,	/* query */ +		unsigned);	/* query length */ + +	/* +	** Attempt to receive a reply on this TCP socket. +	** Returns pointer to dynamically malloced memory, or null if error. +	*/ + +char *rfc1035_recv_tcp(struct rfc1035_res *, +		int,	/* file descriptor */ +		int *,		/* * initialized to contain msg length */ +		unsigned);	/* # of seconds to wait for a response */ + +char *rfc1035_query_tcp(struct rfc1035_res *, +	int,	/* file descriptor */ +	const char *,		/* query */ +	unsigned,		/* query length */ +	int *,			/* * initialized to contain msg length */ +	unsigned);		/* # of seconds to wait for response */ + +/*************************************************/ +/* Parse a raw response into a useful structure. */ +/*************************************************/ + +struct rfc1035_rr { +	const char *rrname;  /* NOT a null term str, a ptr into the raw resp */ +	unsigned rrtype, rrclass; +	RFC1035_UINT32  ttl; +	unsigned rdlength; +	const char    *rdata;	/* Raw data, parsed record follows: */ + +	union { +		struct { +			const char *hinfo_str; +			const char *os_str; +			} hinfo; + +		struct in_addr inaddr;		/* A */ +#if RFC1035_IPV6 +		struct in6_addr in6addr;	/* AAAA */ +#endif + +		const char *domainname; +				/* CNAME, MB, MD, MF, MG, MR, NS, PTR */ + +		struct { +			const char *rmailbx_label; +			const char *emailbx_label; +			} minfo; + +		struct { +			unsigned preference; +			const char *mx_label; +			} mx; + +		struct { +			const char *mname_label; +			const char *rname_label; +			RFC1035_UINT32 serial; +			RFC1035_UINT32 refresh; +			RFC1035_UINT32 retry; +			RFC1035_UINT32 expire; +			RFC1035_UINT32 minimum; +			} soa; + +		struct { +			RFC1035_UINT16 type_covered; +			unsigned char algorithm; +			unsigned char labels; +			RFC1035_UINT16 original_ttl; +			RFC1035_UINT32 signature_expiration; +			RFC1035_UINT32 signature_inception; +			RFC1035_UINT16 key_tag; +			const char *signer_name; +			const char *signature; +			RFC1035_UINT16 signature_len; +		} rrsig; + +		/* As are just represented by rdata/rdlength */ +		/* TXTs are parsed directly from rdata/rdlength */ +		/* WKS are parsed directly */ + +		} rr; +	} ; + +struct rfc1035_reply { +	struct	rfc1035_reply	*next;	/* AXFRs have a linked list here */ + +	const	char	*reply;	/* The raw reply, for convenience's sake */ +	unsigned	replylen;	/* The length of the reply */ +	char		*mallocedbuf;	/* If not NULL, dynamically allocated +					** memory that holds the reply. +					*/ + +	RFC1035_NETADDR server_addr;	/* Replying server */ + +	unsigned char qr; +	unsigned char opcode; +	unsigned char aa; +	unsigned char tc; +	unsigned char rd; +	unsigned char ra; +	unsigned char ad; +	unsigned char cd; +	unsigned char rcode; +	unsigned qdcount; +	unsigned ancount; +	unsigned nscount; +	unsigned arcount; + +	struct rfc1035_query *qdptr;	/* sizeof qdcount */ +	struct rfc1035_rr *anptr; +	struct rfc1035_rr *nsptr; +	struct rfc1035_rr *arptr; + +	struct rfc1035_rr **allrrs;	/* Pointers to all RR records, +					** add ancount+nscount+arcount for +					** the size of the array */ +	} ; + +struct rfc1035_reply *rfc1035_replyparse(const char *, unsigned); +void rfc1035_rr_rand_an(struct rfc1035_reply *rr); +void rfc1035_rr_rand_ns(struct rfc1035_reply *rr); +void rfc1035_rr_rand_ar(struct rfc1035_reply *rr); +void rfc1035_rr_rand(struct rfc1035_reply *rr); + +void rfc1035_dump(struct rfc1035_reply *, FILE *); + +const char *rfc1035_fmttime(unsigned long, char *); +#define	RFC1035_MAXTIMEBUFSIZE	40 + +char *rfc1035_dumprrdata(struct rfc1035_reply *, struct rfc1035_rr *); + +int rfc1035_rr_gettxt(struct rfc1035_rr *, +	int, +	char buf[256]); + +/* +** I ignore any possible bugs in the resolver functions, and roll my own, +** for IPv4. +*/ + +void rfc1035_ntoa_ipv4(const struct in_addr *in, char *buf); +int rfc1035_aton_ipv4(const char *p, struct in_addr *in4); + +#if RFC1035_IPV6 +void rfc1035_ipv6to4(struct in_addr *, const struct in6_addr *); +void rfc1035_ipv4to6(struct in6_addr *, const struct in_addr *); +#endif + +/* +** Extract network address from a socket address. +*/ + +int rfc1035_sockaddrip(const RFC1035_NETADDR *,	/* Socket address buffer */ +		int,				/* Length of address */ +		RFC1035_ADDR *);		/* Address saved here */ + +int rfc1035_sockaddrport(const RFC1035_NETADDR *, /* Socket address buffer */ +		int,				/* Length of address */ +		int *);				/* Port saved here */ + +#if RFC1035_IPV6 +#define	RFC1035_NTOABUFSIZE	INET6_ADDRSTRLEN +#else +#define	RFC1035_NTOABUFSIZE	16 +#endif + +void rfc1035_ntoa(const RFC1035_ADDR *, char *); +int rfc1035_aton(const char *, RFC1035_ADDR *); + +/* +** New function that compares two addresses -- handles both IPv4 and IPv6: +*/ + +int rfc1035_same_ip(const void *, int, const void *, int); + +int     rfc1035_bindsource(int sockfd,	/* Socket fd */ +	const struct sockaddr *addr,	/* Buffer to socket address */ +	int addrlen);			/* Size of socket address */ + +/* +** First try to create an IPv6 socket, then, if we fail, an IPv4 socket. +*/ + +int	rfc1035_mksocket(int sock_type,		/* socket type to create */ +			int sock_protocol,	/* socket protocol to create */ +			int *af);	/* If succeed, address family created */ + +/* +** Take the destination address, and create a socket address structure +** suitable for connecting to this address. +*/ + +int	rfc1035_mkaddress(int af,		/* AF_INET or AF_INET6 */ + +	/* buf is initiailized to the sender's ip address. */ + +	RFC1035_NETADDR *buf,		/* Buffer for the created address */ +	const RFC1035_ADDR *addr,	/* Network address */ +	int port,			/* Network port (network byte order) */ +	const struct sockaddr **ptr,	/* Will point to buf */ +	int *len);			/* Will be size of socket address */ + +/* +** A convenient interface to the SIOCGIFCONF ioctl. +*/ + +struct rfc1035_ifconf { +	struct rfc1035_ifconf *next; +	char *ifname; +	RFC1035_ADDR ifaddr; +}; + +struct rfc1035_ifconf *rfc1035_ifconf(int *errflag); +void rfc1035_ifconf_free(struct rfc1035_ifconf *ifconf_list); + +#ifdef  __cplusplus +} +#endif + +#endif diff --git a/rfc1035/rfc1035an.c b/rfc1035/rfc1035an.c new file mode 100644 index 0000000..7459530 --- /dev/null +++ b/rfc1035/rfc1035an.c @@ -0,0 +1,141 @@ +/* +** Copyright 1998 - 2000 Double Precision, Inc. +** See COPYING for distribution information. +*/ +#include	"config.h" +#include	"rfc1035.h" +#include	<sys/types.h> +#include	<sys/socket.h> +#include	<arpa/inet.h> +#include	<errno.h> +#include	<string.h> + + +void rfc1035_ntoa_ipv4(const struct in_addr *in, char *buf) +{ +union	{ +	unsigned char addr[4]; +	RFC1035_UINT32 n; +	} u; + +int	i; +char	*p; +char	pbuf[4]; + +	u.n=in->s_addr; +	for (i=0; i<4; i++) +	{ +		if (i)	*buf++='.'; +		p=pbuf+3; +		*p=0; +		do +		{ +			*--p = '0' + (u.addr[i] % 10); +		} while ( (u.addr[i] /= 10) != 0); +		while (*p) +			*buf++ = *p++; +	} +	*buf=0; +} + +void rfc1035_ntoa(const RFC1035_ADDR *in, char *buf) +{ +#if	RFC1035_IPV6 +	inet_ntop(AF_INET6, in, buf, RFC1035_MAXNAMESIZE+1); +#else +	rfc1035_ntoa_ipv4(in, buf); +#endif +} + +static RFC1035_UINT32 doaton(const char *p, int *err) +{ +RFC1035_UINT32 octets[4]; +int	i; +int	base; + +	*err=1; +	for (i=0; i<4; i++) +	{ +		if (i > 0) +		{ +			if (*p == '\0')	break; +			if (*p != '.')	return ( -1 ); +			++p; +		} +		if (*p < '0' || *p > '9')	return (-1); +		octets[i]=0; +		base=10; +		if (*p == '0')	base=8; + +		while (*p >= '0' && *p < '0'+base) +			octets[i] = octets[i] * base + (*p++ - '0'); +	} +	if (*p)	return ( -1 ); +	*err=0; +	if (i == 1)	return (octets[0]); +	*err=1; +	if (octets[0] > 255)	return ( -1 ); +	octets[0] <<= 24; + +	if (i == 2) +	{ +		if (octets[1] > 0x00FFFFFF)	return ( -1 ); +		*err=0; +		return (octets[0] | octets[1]); +	} + +	if (octets[1] > 255)	return ( -1 ); +	octets[1] <<= 16; +	if (i == 3) +	{ +		if (octets[2] > 0x0000FFFF)	return ( -1 ); +		*err=0; +		return (octets[0] | octets[1] | octets[2]); +	} +	if (octets[2] > 255 || octets[3] > 255)	return ( -1 ); +	*err=0; +	return (octets[0] | octets[1] | (octets[2] << 8) | octets[3]); +} + +int rfc1035_aton_ipv4(const char *p, struct in_addr *in4) +{ +int	dummy; +RFC1035_UINT32 n=doaton(p, &dummy); +union	{ +	unsigned char addr[4]; +	struct in_addr n; +	} u; + +	u.addr[3]=n; +	u.addr[2]=n >> 8; +	u.addr[1]=n >> 16; +	u.addr[0]=n >> 24; + +	if (dummy)	errno=EINVAL; +	*in4=u.n; +	return (dummy); +} + +#if	RFC1035_IPV6 + +int rfc1035_aton(const char *p, RFC1035_ADDR *in6) +{ +struct in_addr	in4; + +	if (rfc1035_aton_ipv4(p, &in4) == 0) +	{ +		rfc1035_ipv4to6(in6, &in4); +		return (0); +	} +	if (inet_pton(AF_INET6, p, in6) <= 0) +		return (-1); +	return (0); +} + +#else + +int rfc1035_aton(const char *p, RFC1035_ADDR *in4) +{ +	return (rfc1035_aton_ipv4(p, in4)); +} +#endif diff --git a/rfc1035/rfc1035bindsource.c b/rfc1035/rfc1035bindsource.c new file mode 100644 index 0000000..0f59ada --- /dev/null +++ b/rfc1035/rfc1035bindsource.c @@ -0,0 +1,21 @@ +/* +** Copyright 1998 - 2000 Double Precision, Inc. +** See COPYING for distribution information. +*/ +#include	"config.h" +#include	"rfc1035.h" +#include	<sys/types.h> +#include	<sys/socket.h> +#include	<arpa/inet.h> +#include	<errno.h> + + +/* +**	Bind a socket to a local IP. This is used to control the source IP +**	address when making TCP connection. +*/ + +int	rfc1035_bindsource(int sockfd, const struct sockaddr *addr, int addrlen) +{ +	return bind(sockfd, addr, addrlen); +} diff --git a/rfc1035/rfc1035dump.c b/rfc1035/rfc1035dump.c new file mode 100644 index 0000000..6b62a5e --- /dev/null +++ b/rfc1035/rfc1035dump.c @@ -0,0 +1,170 @@ +/* +** Copyright 1998 - 2000 Double Precision, Inc. +** See COPYING for distribution information. +*/ + +#include	"rfc1035.h" +#include	<stdlib.h> +#include	<arpa/inet.h> + + +static void print_hostname(FILE *f, const char *p) +{ +	for ( ; *p; p++) +	{ +		if (*p < ' ' || *p >= 127) +		{ +			fprintf(f, "\\%03o", (int)(unsigned char)*p); +			continue; +		} + +		if (*p == '\\') +		{ +			fprintf(f, "\\\\"); +			continue; +		} +		putc(*p, f); +	} +} + +static void tostr_callback(const char *ptr, void *vp) +{ +	FILE *fp=*(FILE **)vp; + +	fprintf(fp, "%s", ptr); +} + +void rfc1035_dump(struct rfc1035_reply *r, FILE *f) +{ +unsigned n; +char namebuf[RFC1035_MAXNAMESIZE+1]; +char timebuf[RFC1035_MAXTIMEBUFSIZE+1]; +char	ipbuf[RFC1035_NTOABUFSIZE]; +struct	rfc1035_reply *qr; + +	fprintf(f, ";;HEADER"); + +	rfc1035_ntoa((const RFC1035_ADDR *)&r->server_addr, ipbuf); + +	fprintf(f, " (server %s)", ipbuf); + +	fprintf(f, ":\n;;  Bytes: %ld\n",  +		(long)r->replylen); + +	fprintf(f, ";;  Opcode: %s\n",  +		rfc1035_opcode_itostr(r->opcode)); +	fprintf(f, ";;  Flags:"); +	if (r->qr) +		fprintf(f, " qr"); +	if (r->aa) +		fprintf(f, " aa"); +	if (r->tc) +		fprintf(f, " tc"); +	if (r->rd) +		fprintf(f, " rd"); +	if (r->ra) +		fprintf(f, " ra"); +	if (r->ad) +		fprintf(f, " ad"); +	if (r->cd) +		fprintf(f, " cd"); +	fprintf(f, "\n;;  Status: %s\n", rfc1035_rcode_itostr(r->rcode)); +	fprintf(f, ";;  # questions: %u\n", r->qdcount); +	fprintf(f, ";;  # answers: %u\n", r->ancount); +	fprintf(f, ";;  # authoritative: %u\n", r->nscount); +	fprintf(f, ";;  # additional: %u\n", r->arcount); + +	fprintf(f, ";;\n;;QUESTIONS:\n"); +	for (n=0; n<r->qdcount; n++) +	{ +		fprintf(f, ";;  "); +		print_hostname(f, rfc1035_replyhostname(r, r->qdptr[n].name, +							namebuf)); +		fprintf(f,".\t%s ", +			rfc1035_class_itostr(r->qdptr[n].qclass)); + +		rfc1035_type_itostr(r->qdptr[n].qtype, tostr_callback, &f); + +		fprintf(f, "\n"); +	} + +	fprintf(f, "\n;;ANSWERS:\n"); +	for (qr=r; qr; qr=qr->next) +	{ +		for (n=0; n<qr->ancount; n++) +		{ +			char	*c; + +			fprintf(f, " "); +			print_hostname(f, +				       rfc1035_replyhostname(qr, +							     qr->anptr[n] +							     .rrname, +							     namebuf)); +			fprintf(f, ".\t%s\t%s ", +				rfc1035_fmttime(qr->anptr[n].ttl, timebuf), +				rfc1035_class_itostr(qr->anptr[n].rrclass)); + +			rfc1035_type_itostr(qr->anptr[n].rrtype, tostr_callback, +					    &f); + +			c=rfc1035_dumprrdata(qr, qr->anptr+n); +			if (c) +			{ +				fprintf(f, "\t%s", c); +				free(c); +			} +			fprintf(f, "\n"); +		} +	} +	fprintf(f, "\n;;AUTHORITATIVE:\n"); +	for (n=0; n<r->nscount; n++) +	{ +		char	*c; + +		fprintf(f, " "); +		print_hostname(f, rfc1035_replyhostname(r, +							r->nsptr[n].rrname, +							namebuf)); +		fprintf(f, ".\t%s\t%s ", +			rfc1035_fmttime(r->nsptr[n].ttl, timebuf), +			rfc1035_class_itostr(r->nsptr[n].rrclass)); + +		rfc1035_type_itostr(r->nsptr[n].rrtype, tostr_callback, &f); + +		c=rfc1035_dumprrdata(r, r->nsptr+n); +		if (c) +		{ +			fprintf(f, "\t%s", c); +			free(c); +		} +		fprintf(f, "\n"); +	} + +	fprintf(f, "\n;;ADDITIONAL:\n"); +	for (n=0; n<r->arcount; n++) +	{ +		char	*c; + +		if (r->arptr[n].rrtype == RFC1035_TYPE_OPT) +			continue; + +		fprintf(f, " "); +		print_hostname(f, rfc1035_replyhostname(r, +							r->arptr[n].rrname, +							namebuf)); +		fprintf(f, ".\t%s\t%s ", +			rfc1035_fmttime(r->arptr[n].ttl, timebuf), +			rfc1035_class_itostr(r->arptr[n].rrclass)); + +		rfc1035_type_itostr(r->arptr[n].rrtype, tostr_callback, &f); + +		c=rfc1035_dumprrdata(r, r->arptr+n); +		if (c) +		{ +			fprintf(f, "\t%s", c); +			free(c); +		} +		fprintf(f, "\n"); +	} +} diff --git a/rfc1035/rfc1035dumprrdata.c b/rfc1035/rfc1035dumprrdata.c new file mode 100644 index 0000000..0e964c7 --- /dev/null +++ b/rfc1035/rfc1035dumprrdata.c @@ -0,0 +1,373 @@ +/* +** Copyright 1998 - 2004 Double Precision, Inc. +** See COPYING for distribution information. +*/ + +#include	"rfc1035.h" +#include	"rfc822/encode.h" +#include	<string.h> +#include	<stdlib.h> +#include	<sys/types.h> +#include	<arpa/inet.h> +#if TIME_WITH_SYS_TIME +#include	<sys/time.h> +#include	<time.h> +#else +#if HAVE_SYS_TIME_H +#include	<sys/time.h> +#else +#include	<time.h> +#endif +#endif + + +static char *dumpsoa(struct rfc1035_reply *r, struct rfc1035_rr *rr) +{ +char	name1[RFC1035_MAXNAMESIZE+1], name2[RFC1035_MAXNAMESIZE+1]; +char	*p; +char	timebuf[RFC1035_MAXTIMEBUFSIZE+1]; + +	rfc1035_replyhostname(r, rr->rr.soa.rname_label, name1); +	rfc1035_replyhostname(r, rr->rr.soa.mname_label, name2); + +	p=(char *)malloc(strlen(name1)+strlen(name2)+500); +	if (!p)	return (0); + +	strcat(strcat(strcat(strcpy(p, name2), ". "), name1), ". (\n\t\t\t\t"); +	sprintf(p+strlen(p), "%lu ; serial\n",  +			(unsigned long)rr->rr.soa.serial); +	sprintf(p+strlen(p), "\t\t\t\t%s  ; refresh\n",  +			rfc1035_fmttime( rr->rr.soa.refresh, timebuf)); +	sprintf(p+strlen(p), "\t\t\t\t%s  ; retry\n",  +			rfc1035_fmttime( rr->rr.soa.retry, timebuf)); +	sprintf(p+strlen(p), "\t\t\t\t%s  ; expire\n",  +			rfc1035_fmttime( rr->rr.soa.expire, timebuf)); +	sprintf(p+strlen(p), "\t\t\t\t%s) ; minimum",  +			rfc1035_fmttime( rr->rr.soa.minimum, timebuf)); +	return (p); +} + +static void str_cat(char *p, const char **q) +{ +int	l=(int)(unsigned char)*(*q)++; + +	while (*p)	p++; +	memcpy(p, *q, l); +	p[l]=0; +	*q += l; +} + +static char *dumptxt(struct rfc1035_reply *r, struct rfc1035_rr *rr) +{ +int	len=1; +char	*p=0; +int	pass; +const char *cp; + +	for (pass=0; pass<2; pass++) +	{ +		if (pass && (p=(char *)malloc(len)) == 0)	return (0); +		if (pass)	*p=0; + +		cp=rr->rdata; +		while (cp < rr->rdata+rr->rdlength) +		{ +		int l=(int)(unsigned char)*cp; + +			if (l >= rr->rdata+rr->rdlength-cp)	return (0); +			if (pass == 0) +				cp += l+1; +			len += l+4; +			if (pass && *p) +				strcat(p, "\n\t\t\t"); +			if (pass) +				str_cat(p, &cp); +		} +	} +	return (p); +} + +struct fmt_rrsig_info { + +	char *buf; +	size_t n; +}; + + +static void append_str(struct fmt_rrsig_info *info, const char *str, size_t l) +{ +	if (info->buf) +	{ +		memcpy(info->buf, str, l); + +		info->buf += l; +	} + +	info->n += l; +} + +static void tostr_callback(const char *str, void *vp) +{ +	append_str( (struct fmt_rrsig_info *)vp, str, strlen(str)); +} + +static void append_int32(struct fmt_rrsig_info *info, RFC1035_UINT32 val) +{ +	char bufp[30]; +	char *q=bufp+sizeof(bufp); + +	do +	{ +		*--q = '0' + (val % 10); + +		val /= 10; +	} while (val); + +	append_str(info, q, bufp+sizeof(bufp)-q); +} + +static void append_time_t(struct fmt_rrsig_info *info, time_t timeval) +{ +	char bufp[30]; +	struct tm result; + +	gmtime_r(&timeval, &result); + +	strftime(bufp, sizeof(bufp), "%Y%m%d%H%M%S", &result); + +	append_str(info, bufp, strlen(bufp)); +} + +static int encode_callback_func(const char *p, size_t n, +				void *vp) +{ +	struct fmt_rrsig_info *info=(struct fmt_rrsig_info *)vp; + +	while (n) +	{ +		size_t i; + +		if (*p == '\n') +		{ +			append_str(info, " ", 1); +			++p; +			--n; +			continue; +		} + +		for (i=0; i<n; ++i) +			if (p[i] == '\n') +				break; + +		append_str(info, p, i); + +		p += i; +		n -= i; +	} +	return 0; + +} + +static size_t fmt_rrsig(struct rfc1035_reply *r, +			struct rfc1035_rr *rr, time_t now, char *buf) +{ +	char	p[RFC1035_MAXNAMESIZE+1]; + +	char	timebuf[RFC1035_MAXTIMEBUFSIZE+1]; + +	time_t signature_inception, signature_expiration; + +	struct libmail_encode_info lei; + +	struct fmt_rrsig_info fri; + +	fri.buf=buf; +	fri.n=0; + +	rfc1035_type_itostr(rr->rr.rrsig.type_covered, tostr_callback, &fri); + +	append_str(&fri, " ", 1); + +	append_int32(&fri, rr->rr.rrsig.algorithm); + +	append_str(&fri, " ", 1); + +	append_int32(&fri, rr->rr.rrsig.labels); + +	append_str(&fri, " ", 1); + +	rfc1035_fmttime(rr->rr.rrsig.original_ttl, timebuf); + +	append_str(&fri, timebuf, strlen(timebuf)); + +	append_str(&fri, " ", 1); + +	if (sizeof(time_t) == 4) +	{ +		signature_inception=rr->rr.rrsig.signature_inception; +		signature_expiration=rr->rr.rrsig.signature_expiration; +	} +	else +	{ +		time_t now_epoch=(now & ~0xFFFFFFFFLL); + +		time_t cur_epoch=now_epoch | rr->rr.rrsig.signature_inception; + +		time_t prev_epoch=cur_epoch - 0xFFFFFFFF-1; +		time_t next_epoch=cur_epoch + 0xFFFFFFFF+1; + + +#define time2diff(a,b) ((a) < (b) ? (b)-(a):(a)-(b)) + +#define closest2now(now,time1,time2)					\ +		(time2diff((now), (time1)) < time2diff((now), (time2))	\ +		 ? (time1):(time2)) + +		signature_inception = +			closest2now(now, closest2now(now, +						     prev_epoch, +						     cur_epoch), +				    next_epoch); + +		signature_expiration = +			signature_inception + +			((rr->rr.rrsig.signature_expiration - +			  rr->rr.rrsig.signature_inception) +			 & 0x7FFFFFFF); +	} + +	append_time_t(&fri, signature_inception); + +	append_str(&fri, " ", 1); + +	append_time_t(&fri, signature_expiration); + +	append_str(&fri, " ", 1); + +	append_int32(&fri, rr->rr.rrsig.key_tag); + +	append_str(&fri, " ", 1); + +	rfc1035_replyhostname(r, rr->rr.rrsig.signer_name, p); + +	append_str(&fri, p, strlen(p)); + +	append_str(&fri, ". ", 2); + +	libmail_encode_start(&lei, "base64", encode_callback_func, &fri); + +	libmail_encode(&lei, rr->rr.rrsig.signature, +		       rr->rr.rrsig.signature_len); +	libmail_encode_end(&lei); + +	return fri.n; +} + +char *rfc1035_dumprrdata(struct rfc1035_reply *r, struct rfc1035_rr *rr) +{ +	if (rr->rrclass != RFC1035_CLASS_IN)	return (0); +	switch (rr->rrtype)	{ +	case RFC1035_TYPE_A: +		{ +		char	ipbuf[RFC1035_NTOABUFSIZE]; + +			rfc1035_ntoa_ipv4(&rr->rr.inaddr, ipbuf); +			return (strdup(ipbuf)); +		} +#if	RFC1035_IPV6 +	case RFC1035_TYPE_AAAA: +		{ +		char	ipbuf[INET6_ADDRSTRLEN]; + +			if (inet_ntop(AF_INET6, &rr->rr.in6addr, +				ipbuf, sizeof(ipbuf)) == 0) +				ipbuf[0]=0; +			return (strdup(ipbuf)); +		} +#endif +	case RFC1035_TYPE_TXT: +		return (dumptxt(r, rr)); +	case RFC1035_TYPE_CNAME: +	case RFC1035_TYPE_MB: +	case RFC1035_TYPE_MG: +	case RFC1035_TYPE_MR: +	case RFC1035_TYPE_MD: +	case RFC1035_TYPE_MF: +	case RFC1035_TYPE_NS: +	case RFC1035_TYPE_PTR: +		{ +		char	p[RFC1035_MAXNAMESIZE+1], *q; + +			rfc1035_replyhostname(r, rr->rr.domainname, p); + +			if ((q=(char *)malloc(strlen(p)+2)) != 0) +				strcat(strcpy(q, p), "."); +			return (q); +		} + +	case RFC1035_TYPE_SOA: +		return (dumpsoa(r, rr)); +		break; +	case RFC1035_TYPE_MX: + +		{ +		char	p[RFC1035_MAXNAMESIZE+1], *q; + +			rfc1035_replyhostname(r, rr->rr.mx.mx_label, p); + +			if ((q=(char *)malloc(strlen(p)+40)) != 0) +			{ +				sprintf(q, "%d %s.", +					(int)rr->rr.mx.preference, p); +			} +			return (q); +		} + +	case RFC1035_TYPE_HINFO: +		{ +		char *p=malloc( (int)(unsigned char)*rr->rr.hinfo.hinfo_str+ +				(int)(unsigned char)*rr->rr.hinfo.os_str+10); + +			if (p) +			{ +			const char *q=rr->rr.hinfo.hinfo_str; + +				*p=0; +				str_cat(p, &q); +				strcat(p, ", "); +				q=rr->rr.hinfo.os_str; +				str_cat(p, &q); +			} +			return (p); +		} +	case RFC1035_TYPE_MINFO: +		{ +		char	p[RFC1035_MAXNAMESIZE+1], t[RFC1035_MAXNAMESIZE+1], *q; + +			rfc1035_replyhostname(r, rr->rr.minfo.rmailbx_label, p); +			rfc1035_replyhostname(r, rr->rr.minfo.emailbx_label, t); + +			if ((q=(char *)malloc(strlen(p)+strlen(t)+4)) == 0) +				return (0); +			strcat(strcat(strcat(strcpy(q, p), ". "), t), "."); +			return (q); +		} + +	case RFC1035_TYPE_RRSIG: +		{ +			time_t now=time(NULL); + +			size_t n=fmt_rrsig(r, rr, now, NULL); +			char *p; + +			if ((p=malloc(n+1)) == 0) +				return (0); + +			fmt_rrsig(r, rr, now, p); + +			p[n]=0; +			return p; +		} +	} +	return (0); +} diff --git a/rfc1035/rfc1035fmttime.c b/rfc1035/rfc1035fmttime.c new file mode 100644 index 0000000..8c9461a --- /dev/null +++ b/rfc1035/rfc1035fmttime.c @@ -0,0 +1,24 @@ +/* +** Copyright 1998 - 2000 Double Precision, Inc. +** See COPYING for distribution information. +*/ + +#include	"rfc1035.h" +#include	<string.h> + + +const char *rfc1035_fmttime(unsigned long n, char *buf) +{ +unsigned long s,m,h; + +	s = n % 60; n=n/60; +	m = n % 60; n=n/60; +	h = n % 24; n=n/24; + +	buf[0]='\0'; +	if (n)	sprintf(buf,"%lud",n); +	if (n || h)	sprintf(buf+strlen(buf), "%luh", h); +	if (n || h || m)	sprintf(buf+strlen(buf), "%lum", m); +	sprintf(buf+strlen(buf), "%lus", s); +	return (buf); +} diff --git a/rfc1035/rfc1035gettxt.c b/rfc1035/rfc1035gettxt.c new file mode 100644 index 0000000..4af5506 --- /dev/null +++ b/rfc1035/rfc1035gettxt.c @@ -0,0 +1,30 @@ +/* +** Copyright 1998 - 2000 Double Precision, Inc. +** See COPYING for distribution information. +*/ + +#include	"rfc1035.h" +#include	<string.h> + + +int rfc1035_rr_gettxt(struct rfc1035_rr *p, int startpos, char buf[256]) +{ +unsigned l; + +	if (startpos < 0 || (unsigned)startpos >= p->rdlength || +		p->rdlength - (unsigned)startpos <= +			(l=(unsigned)(unsigned char)p->rdata[startpos])) +	{ +		buf[0]=0; +		return (-1); +	} + +	++startpos; + +	memcpy(buf, p->rdata + startpos, l); +	buf[l]=0; +	startpos += l; +	if (startpos >= p->rdlength) +		startpos= -1; +	return (startpos); +} diff --git a/rfc1035/rfc1035ifconf.c b/rfc1035/rfc1035ifconf.c new file mode 100644 index 0000000..47b24b2 --- /dev/null +++ b/rfc1035/rfc1035ifconf.c @@ -0,0 +1,183 @@ +/* +** Copyright 2002 Double Precision, Inc. +** See COPYING for distribution information. +*/ + +#include "rfc1035.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#if HAVE_UNISTD_H +#include <unistd.h> +#endif + + +#if HAVE_SIOCGIFCONF + +#if HAVE_SYS_TYPES_H +#include <sys/types.h> +#endif + +#include <sys/ioctl.h> +#include <net/if.h> + +static int getifconf(int fd, struct rfc1035_ifconf **ifconf_ptr) +{ +	struct ifreq ifreq_buf[64]; +	struct ifconf ifc; +	int i; +	const struct sockaddr_in *sin; +	RFC1035_ADDR addr; +	char ipaddr[RFC1035_NTOABUFSIZE]; +	struct rfc1035_ifconf *ifcptr; +	struct rfc1035_ifconf **ifconf; + +	ifc.ifc_len=sizeof(ifreq_buf); +	ifc.ifc_req=ifreq_buf; + +	if (ioctl(fd, SIOCGIFCONF, &ifc) < 0) +		return (0); + +	for (i=0; i * sizeof(struct ifreq) < ifc.ifc_len; i++) +	{ +		sin=(const struct sockaddr_in *)&ifreq_buf[i].ifr_addr; + +#if RFC1035_IPV6 +		if (sin->sin_family == AF_INET6) +		{ +			struct sockaddr_in6 sin6; + +			memcpy(&sin6, sin, sizeof(sin6)); +			addr=sin6.sin6_addr; +		} +		else if (sin->sin_family == AF_INET) +			rfc1035_ipv4to6(&addr, &sin->sin_addr); +		else +			continue; +#else +		if (sin->sin_family == AF_INET) +			addr=sin->sin_addr; +		else +			continue; +#endif + +		rfc1035_ntoa(&addr, ipaddr); + +		/* +		** Eliminate any dupes. +		*/ + +		ifconf=ifconf_ptr; + +		while ( *ifconf ) +		{ +			char ipaddr2[RFC1035_NTOABUFSIZE]; + +			rfc1035_ntoa(&(*ifconf)->ifaddr, ipaddr2); + +			if (strcmp(ipaddr, ipaddr2) == 0) +				break; + +			ifconf= &(*ifconf)->next; +		} + +		if ( *ifconf ) +			continue;	/* Already have this IP addr */ + +		if ( (ifcptr=malloc(sizeof(struct rfc1035_ifconf))) == NULL || +		     (ifcptr->ifname=strdup(ifreq_buf[i].ifr_name)) == NULL) +		{ +			if (ifcptr) +				free(ifcptr); +			return (-1); +		} + +		ifcptr->ifaddr=addr; +		ifcptr->next=NULL; + +		*ifconf= ifcptr; +	} +	return (0); +} + +/* +** On systems that support IPv6, issue an SIOCGIFCONF on both an IPv4 and +** an IPv6 socket, for good measure. +*/ + +static int doifconf(struct rfc1035_ifconf **ifconf_list) +{ +	int fd; + +	fd=socket(PF_INET, SOCK_STREAM, 0); + +	if (fd >= 0) +	{ +		if (getifconf(fd, ifconf_list)) +		{ +			close(fd); +			return (-1); +		} +		close(fd); +	} + +#if RFC1035_IPV6 + +	fd=socket(PF_INET6, SOCK_STREAM, 0); + +	if (fd >= 0) +	{ +		if (getifconf(fd, ifconf_list)) +		{ +			close(fd); +			return (-1); +		} +		close(fd); +	} +#endif +	return (0); +} + +struct rfc1035_ifconf *rfc1035_ifconf(int *errflag) +{ +	struct rfc1035_ifconf *ifconf_list=NULL; +	int dummy; + +	if (!errflag) +		errflag= &dummy; + +	*errflag= -1; +	if (doifconf(&ifconf_list)) +	{ +		rfc1035_ifconf_free(ifconf_list); +		return (NULL); +	} + +	*errflag=0; +	return ifconf_list; +} + +#else + +struct rfc1035_ifconf *rfc1035_ifconf(int *errflag) +{ +	if (errflag) +		*errflag=0; +	return NULL; +} +#endif + +void rfc1035_ifconf_free(struct rfc1035_ifconf *ifconf_list) +{ +	while (ifconf_list) +	{ +		struct rfc1035_ifconf *p=ifconf_list->next; + +		free(ifconf_list->ifname); +		free(ifconf_list); +		ifconf_list=p; +	} +} + diff --git a/rfc1035/rfc1035ipv6to4.c b/rfc1035/rfc1035ipv6to4.c new file mode 100644 index 0000000..d63e9e4 --- /dev/null +++ b/rfc1035/rfc1035ipv6to4.c @@ -0,0 +1,51 @@ +/* +** Copyright 2000-2003 Double Precision, Inc. +** See COPYING for distribution information. +*/ + +#include	"config.h" +#include	"rfc1035.h" +#include	<stdio.h> +#include	<string.h> +#include	<stdlib.h> +#include	<arpa/inet.h> + + +#if RFC1035_FREEBSD40 +#define	s6_addr16	__u6_addr.__u6_addr16 +#define s6_addr32	__u6_addr.__u6_addr32 +#endif + +#if RFC1035_SOLARIS8 +#define s6_addr32	_S6_un._S6_u32 +#endif + +#if RFC1035_IPV6 + +void rfc1035_ipv6to4(struct in_addr *ip4, const struct in6_addr *ip6) +{ +	ip4->s_addr=ip6->s6_addr32[3]; +} + +void rfc1035_ipv4to6(struct in6_addr *ip6, const struct in_addr *ip4) +{ +	memset(ip6, 0, sizeof(*ip6)); + +#if RFC1035_SOLARIS8 + +	/* No 16-bit union <grumble>... */ + +	ip6->_S6_un._S6_u8[10]= ~0; +	ip6->_S6_un._S6_u8[11]= ~0; + +#else +	ip6->s6_addr16[5]= ~0; +#endif + +	ip6->s6_addr32[3]= ip4->s_addr; + +	if (ip4->s_addr == INADDR_ANY) +		*ip6= in6addr_any; +} +#endif + diff --git a/rfc1035/rfc1035mkaddress.c b/rfc1035/rfc1035mkaddress.c new file mode 100644 index 0000000..38f56b1 --- /dev/null +++ b/rfc1035/rfc1035mkaddress.c @@ -0,0 +1,69 @@ +/* +** Copyright 1998 - 2000 Double Precision, Inc. +** See COPYING for distribution information. +*/ +#include	"config.h" +#include	"rfc1035.h" +#include	<sys/types.h> +#include	<sys/socket.h> +#include	<arpa/inet.h> +#include	<errno.h> +#include	<string.h> + + +/* +**	Ok, take an address, and a port, and come back with a socket address +**	in the specified address family. +*/ + +int	rfc1035_mkaddress(int af, +			RFC1035_NETADDR *buf, +			const RFC1035_ADDR *addr, +			int port, +			const struct sockaddr **ptr, +			int *len) +{ +struct sockaddr_in sin; + +#if	RFC1035_IPV6 + +	if (af == AF_INET6) +	{ +	struct sockaddr_in6 sin6; + +		memset(&sin6, 0, sizeof(sin6)); +		sin6.sin6_family=af; +		sin6.sin6_addr= *addr; +		sin6.sin6_port=port; +		memcpy(buf, &sin6, sizeof(sin6)); +		*ptr=(struct sockaddr *)buf; +		*len=sizeof(sin6); +		return (0); +	} + +	if (af != AF_INET || (!IN6_IS_ADDR_V4MAPPED(addr) && memcmp(addr, &in6addr_any, sizeof(*addr)))) +	{ +		errno=EAFNOSUPPORT; +		return (-1); +	} + +	memset(&sin, 0, sizeof(sin)); +	sin.sin_family=af; +	rfc1035_ipv6to4(&sin.sin_addr, addr); +	sin.sin_port=port; +#else +	if (af != AF_INET) +	{ +		errno=EINVAL; +		return (-1); +	} +	memset(&sin, 0, sizeof(sin)); +	sin.sin_family=af; +	sin.sin_addr= *addr; +	sin.sin_port=port; +#endif +	memcpy(buf, &sin, sizeof(sin)); +	*ptr=(struct sockaddr *)buf; +	*len=sizeof(sin); +	return (0); +} diff --git a/rfc1035/rfc1035mksocket.c b/rfc1035/rfc1035mksocket.c new file mode 100644 index 0000000..45bb66a --- /dev/null +++ b/rfc1035/rfc1035mksocket.c @@ -0,0 +1,39 @@ +/* +** Copyright 1998 - 2011 Double Precision, Inc. +** See COPYING for distribution information. +*/ +#include	"config.h" +#include	"rfc1035.h" +#include	<sys/types.h> +#include	<sys/socket.h> +#include	<arpa/inet.h> +#include	<errno.h> + + +/* +**	Create a socket.  Duh.  If we've compiled IPv6 support, but we can't +**	create an IPv6 socket, create an IPv4 socket.  This can happen, say, +**	on Linux with IPv6 runtime libraries, but without IPv6 in the kernel. +*/ + +int	rfc1035_mksocket(int sock_type, int sock_protocol, int *af) +{ +#if	RFC1035_IPV6 +	int	s; +	int	on=0; + +	*af=AF_INET6; +	if ( (s=socket(PF_INET6, sock_type, sock_protocol)) >= 0) +	{ +#ifdef IPV6_V6ONLY + +		setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, +			   (char *)&on, sizeof(on)); +#endif + +		return (s); +	} +#endif +	*af=AF_INET; +	return (socket(PF_INET, sock_type, sock_protocol)); +} diff --git a/rfc1035/rfc1035mxlist.c b/rfc1035/rfc1035mxlist.c new file mode 100644 index 0000000..557210f --- /dev/null +++ b/rfc1035/rfc1035mxlist.c @@ -0,0 +1,448 @@ +/* +** Copyright 1998 - 2011 Double Precision, Inc. +** See COPYING for distribution information. +*/ + +#include	"config.h" +#include	<stdio.h> +#include	<stdlib.h> +#include	<string.h> +#include	"rfc1035.h" +#include	"rfc1035mxlist.h" + +void rfc1035_mxlist_free(struct rfc1035_mxlist *p) +{ +struct rfc1035_mxlist *q; + +	while (p) +	{ +		q=p->next; +		if (p->hostname)	free(p->hostname); +		free(p); +		p=q; +	} +} + +static int addrecord(struct rfc1035_mxlist **list, const char *mxname, +		     int mxpreference, + +#if	RFC1035_IPV6 +		     struct in6_addr *in, +#else +		     struct in_addr *in, +#endif +		     int ad, +		     int port) +{ +#if	RFC1035_IPV6 +struct sockaddr_in6 sin; +#else +struct sockaddr_in sin; +#endif +struct rfc1035_mxlist *p; + +	if ((p=(struct rfc1035_mxlist *)malloc(sizeof(struct rfc1035_mxlist))) +		== 0 || (p->hostname=malloc(strlen(mxname)+1)) == 0) +	{ +		if (p)	free ( (char *)p); +		return (-1); +	} + +	memset(&sin, 0, sizeof(sin)); + +#if	RFC1035_IPV6 +	sin.sin6_family=AF_INET6; +	sin.sin6_addr= *in; +	sin.sin6_port=htons(port); +	p->protocol=PF_INET6; +#else +	sin.sin_family=AF_INET; +	sin.sin_addr= *in; +	sin.sin_port=htons(port); +	p->protocol=PF_INET; +#endif + +	while ( *list && (*list)->priority < mxpreference ) +		list= &(*list)->next; + +	p->next=*list; +	*list=p; +	p->ad=ad; +	p->priority=mxpreference; +	strcpy(p->hostname, mxname); +	memcpy(&p->address, &sin, sizeof(sin)); +	return (0); +} + +static int harvest_records(struct rfc1035_res *res, +	struct rfc1035_mxlist **list, +	struct rfc1035_reply *mxreply, +	int mxpreference, +	char *mxname, +	int q_type, int *found, int autoquery, int port); + +#define	HARVEST_AUTOQUERY	1 +#define	HARVEST_NODUPE		2 + +static int add_arecords(struct rfc1035_res *res, struct rfc1035_mxlist **list, +	struct rfc1035_reply *mxreply, +	int mxpreference, +	char *mxname, int port) +{ +#if	RFC1035_IPV6 +struct in6_addr	in; +#else +struct in_addr	in; +#endif +int	found=0; +int	rc; + +	if (rfc1035_aton(mxname, &in) == 0) +	{	/* Broken MX record */ +	char	buf[RFC1035_NTOABUFSIZE]; + +		rfc1035_ntoa(&in, buf); + +		if (addrecord(list, buf, mxpreference, &in, 0, port)) +			return (RFC1035_MX_INTERNAL); + +		return (RFC1035_MX_OK); +	} + +#if	RFC1035_IPV6 + +/* +	Here's the IPv6 strategy: + +If we have an existing MX record to work with, try to harvest +both A and AAAA addresses from it.  If we find either an A or an AAAA +record, stop. + +If we don't have an existing MX record, or we didn't find A or AAAA +records, then query for A records.  Query for AAAA records only if A +records weren't found. + +*/ +	if (mxreply) +	{ +		if ((rc=harvest_records(res, list, mxreply, mxpreference, +			mxname, RFC1035_TYPE_AAAA, &found, 0, port)) +				!= RFC1035_MX_OK) +			return (rc); + +		if ((rc=harvest_records(res, list, mxreply, mxpreference, +			mxname, RFC1035_TYPE_A, &found, HARVEST_NODUPE, +				port)) +						!= RFC1035_MX_OK) +			return (rc); +		if (found)	return (RFC1035_MX_OK); +	} + +	if ((rc=harvest_records(res, list, mxreply, mxpreference, mxname, +		RFC1035_TYPE_A, &found, HARVEST_AUTOQUERY|HARVEST_NODUPE, port)) +						!= RFC1035_MX_OK) +		return (rc); +	if (found)	return (RFC1035_MX_OK); + +	if ((rc=harvest_records(res, list, mxreply, mxpreference, mxname, +		RFC1035_TYPE_AAAA, &found, HARVEST_AUTOQUERY, port)) +			!= RFC1035_MX_OK) +		return (rc); + +#else +	if ((rc=harvest_records(res, list, mxreply, mxpreference, mxname, +		RFC1035_TYPE_A, &found, HARVEST_AUTOQUERY, port)) +			!= RFC1035_MX_OK) +		return (rc); +#endif + +	if (!found)	return (RFC1035_MX_HARDERR); +	return (RFC1035_MX_OK); +} + +static int harvest_records(struct rfc1035_res *res, +	struct rfc1035_mxlist **list, +	struct rfc1035_reply *mxreply, +	int mxpreference, +	char *mxname, +	int q_type, int *found, +	int flags, int port) +{ +struct rfc1035_reply *areply=0; +int index; +#if	RFC1035_IPV6 +struct in6_addr in; +#else +struct in_addr in; +#endif + +	index= -1; + +	if (!mxreply || ( +		((index=rfc1035_replysearch_all( res, mxreply, mxname, +					q_type, +					RFC1035_CLASS_IN, +					0)) < 0 || +			mxreply->allrrs[index]->rrtype != q_type) +		&& (flags & HARVEST_AUTOQUERY)) +		) +	{ +		index=rfc1035_resolve_cname(res, mxname, +			q_type, +			RFC1035_CLASS_IN, &areply, RFC1035_X_RANDOMIZE); +		if (index < 0) +		{ +			if (!areply) +			{ +				if (index == RFC1035_ERR_CNAME_RECURSIVE) +					return (RFC1035_MX_BADDNS); +				return (RFC1035_MX_INTERNAL); +			} + +			if (areply->rcode == RFC1035_RCODE_NXDOMAIN || +				areply->rcode == RFC1035_RCODE_NOERROR) +			{ +				rfc1035_replyfree(areply); +				return (RFC1035_MX_OK); +			} +			rfc1035_replyfree(areply); +			return (RFC1035_MX_SOFTERR); +		} +		mxreply=areply; +	} + +	for ( ; index >= 0 ; +			index=rfc1035_replysearch_all( res, mxreply, mxname, +					q_type, +					RFC1035_CLASS_IN, +					index+1)) +	{ +		if (mxreply->allrrs[index]->rrtype != q_type) +			continue; + +#if RFC1035_IPV6 +		if (q_type == RFC1035_TYPE_A) +		{ +		struct rfc1035_mxlist *q; + +			/* Map it to an IPv4 address */ + +			rfc1035_ipv4to6(&in, +				&mxreply->allrrs[index]->rr.inaddr); + +			/* See if it's already here */ + +			for (q= *list; q; q=q->next) +			{ +			struct sockaddr_in6 sin6; + +				if (q->protocol != PF_INET6) +					continue; +				memcpy(&sin6, &q->address, sizeof(sin6)); + +				if (memcmp(&sin6.sin6_addr, &in, sizeof(in)) +					== 0 && q->priority == mxpreference) +					break; +			} +			if ((flags & HARVEST_NODUPE) && q)	continue; +		} +		else +			in=mxreply->allrrs[index]->rr.in6addr; +#else +		in.s_addr=mxreply->allrrs[index]->rr.inaddr.s_addr; +#endif +		*found=1; +		if (addrecord(list, mxname, mxpreference, &in, mxreply->ad, +			      port)) +		{ +			if (areply) +				rfc1035_replyfree(areply); +			return (RFC1035_MX_INTERNAL); +		} +	} +	if (areply) +		rfc1035_replyfree(areply); + +	return (RFC1035_MX_OK); +} + +static int domxlistcreate(struct rfc1035_res *res, +			  const char *q_name, int opts, +			  struct rfc1035_mxlist **list, int port) +{ +char	namebuf[RFC1035_MAXNAMESIZE+1]; +struct	rfc1035_reply *replyp; +int	index; +RFC1035_ADDR	in; +int seen_softerr=0; +int seen_good=0; + +	*list=0; +	if (rfc1035_aton(q_name, &in) == 0) +		return (RFC1035_MX_HARDERR);	/* Don't gimme an IP address */ + +	namebuf[0]=0; +	strncat(namebuf, q_name, RFC1035_MAXNAMESIZE); +	if (namebuf[0] == '[') +	{ +	char	*q=strchr(namebuf, ']'); + +		if (!q || q[1])	return (RFC1035_MX_HARDERR);	/* Bad addr */ +		*q=0; +		if (rfc1035_aton(namebuf+1, &in)) +			return (RFC1035_MX_HARDERR); + +		if (addrecord(list, q_name, -1, &in, 0, port)) +			return (RFC1035_MX_INTERNAL); +		return (RFC1035_MX_OK); +	} + +	index=rfc1035_resolve_cname(res, namebuf, +		RFC1035_TYPE_MX, +		RFC1035_CLASS_IN, &replyp, RFC1035_X_RANDOMIZE); + +	if (index < 0) +	{ +		if (!replyp) +		{ +			if (index == RFC1035_ERR_CNAME_RECURSIVE) +				return (RFC1035_MX_BADDNS); +			return (RFC1035_MX_INTERNAL); +		} + +		if (replyp->rcode == RFC1035_RCODE_NXDOMAIN || +			replyp->rcode == RFC1035_RCODE_NOERROR) +		{ +			rfc1035_replyfree(replyp); +			strcpy(namebuf, q_name); + +			if (opts & RFC1035_MX_AFALLBACK) +				return (add_arecords(res, list, 0, -1, +						     namebuf, port)); +			return RFC1035_MX_HARDERR; +		} + +		rfc1035_replyfree(replyp); +		return (RFC1035_MX_SOFTERR); +	} + +	for ( ; index >= 0; +			index=rfc1035_replysearch_all( res, replyp, namebuf, +					RFC1035_TYPE_MX, +					RFC1035_CLASS_IN, +					index+1)) +	{ +	char	mxname[RFC1035_MAXNAMESIZE+1]; + +		if (replyp->allrrs[index]->rrtype != RFC1035_TYPE_MX) +			continue; + +		if (rfc1035_replyhostname(replyp, +			replyp->allrrs[index]->rr.mx.mx_label, mxname) == 0) +			continue; + +		switch (add_arecords(res, list, replyp, +			replyp->allrrs[index]->rr.mx.preference, mxname, +			port)) { +		case	RFC1035_MX_SOFTERR: +			seen_softerr=1; +			continue; +		case	RFC1035_MX_INTERNAL: +			rfc1035_replyfree(replyp); +			return (RFC1035_MX_INTERNAL); +		case	RFC1035_MX_BADDNS: +			rfc1035_replyfree(replyp); +			return (RFC1035_MX_BADDNS); +		default: +			seen_good=1; +			continue; +		} +	} + +	rfc1035_replyfree(replyp); + +	if (seen_good && (opts & RFC1035_MX_IGNORESOFTERR)) +		seen_softerr=0; +		/* At least some A records were probably fetched */ + +	if (seen_softerr) +		return (RFC1035_MX_SOFTERR); + +	if (*list)	return (RFC1035_MX_OK); +	return (RFC1035_MX_HARDERR); +} + +static int domxlistcreate2(struct rfc1035_res *res, +			   const char *q_name, +			   int opts, +			   struct rfc1035_mxlist **list, +			   int port) +{ +char	*buf; +int	rc; + +	if (strchr(q_name, '.') || strchr(q_name, ':') || +		!res->rfc1035_defaultdomain) +		return (domxlistcreate(res, q_name, opts, list, port)); + +	if ((buf=malloc(strlen(q_name)+ +		strlen(res->rfc1035_defaultdomain)+2)) == 0) +		return (-1); + +	strcat(strcat(strcpy(buf, q_name), "."), res->rfc1035_defaultdomain); + +	rc=domxlistcreate(res, buf, opts, list, port); + +	free(buf); +	return (rc); +} + +static int domxlistcreate3(struct rfc1035_res *res, +			   const char *q_name, +			   int opts, +			   struct rfc1035_mxlist **list) +{ +char	*buf; +int	rc; +const char *p; + +	p=strchr(q_name, ','); + +	if (p == 0)	return (domxlistcreate2(res, q_name, opts, list, 25)); + +	if ((buf=malloc(p-q_name+1)) == 0) +		return (-1); + +	memcpy(buf, q_name, p-q_name); +	buf[p-q_name]=0; + +	rc=domxlistcreate2(res, buf, opts, list, atoi(p+1)); + +	free(buf); +	return (rc); +} + +int rfc1035_mxlist_create_x(struct rfc1035_res *res, +			    const char *q_name, +			    int opts, +			    struct rfc1035_mxlist **list) +{ +int	rc=domxlistcreate3(res, q_name, opts, list); + +	if (rc != RFC1035_MX_OK) +	{ +		rfc1035_mxlist_free(*list); +		*list=0; +	} +	return (rc); +} + +int rfc1035_mxlist_create(struct rfc1035_res *res, +			  const char *q_name, +			  struct rfc1035_mxlist **list) +{ +	return rfc1035_mxlist_create_x(res, q_name, +				       RFC1035_MX_AFALLBACK | +				       RFC1035_MX_IGNORESOFTERR, +				       list); +} diff --git a/rfc1035/rfc1035mxlist.h b/rfc1035/rfc1035mxlist.h new file mode 100644 index 0000000..e4349b8 --- /dev/null +++ b/rfc1035/rfc1035mxlist.h @@ -0,0 +1,57 @@ +#ifndef	rfc1035_mx_h +#define	rfc1035_mx_h + +/* +** Copyright 1998 - 1999 Double Precision, Inc. +** See COPYING for distribution information. +*/ + +#if	HAVE_CONFIG_H +#include "config.h" +#endif + +#include	<sys/types.h> +#include	<netinet/in.h> +#include	<arpa/inet.h> + + +#ifdef  __cplusplus +extern "C" { +#endif + +#define	RFC1035_MX_OK		0	/* Ok, records follow */ +#define	RFC1035_MX_SOFTERR	1	/* Soft DNS error */ +#define	RFC1035_MX_HARDERR	2	/* Hard DNS error */ +#define	RFC1035_MX_INTERNAL	3	/* Internal library error */ +#define	RFC1035_MX_BADDNS	4	/* Bad DNS records */ + +struct rfc1035_mxlist { +	struct rfc1035_mxlist *next; +	int	protocol; +#if	RFC1035_IPV6 +	struct sockaddr_storage address; +#else +	struct sockaddr address; +#endif +	int priority;	/* -1 for plain old A records */ +	int ad; +	char *hostname; +	} ; + +struct rfc1035_res; + +int rfc1035_mxlist_create(struct rfc1035_res *, +	const char *, struct rfc1035_mxlist **); +void rfc1035_mxlist_free(struct rfc1035_mxlist *); + +int rfc1035_mxlist_create_x(struct rfc1035_res *, +			    const char *, int, +			    struct rfc1035_mxlist **); +#define RFC1035_MX_AFALLBACK 1 +#define RFC1035_MX_IGNORESOFTERR 2 + +#ifdef  __cplusplus +} +#endif + +#endif diff --git a/rfc1035/rfc1035qa.c b/rfc1035/rfc1035qa.c new file mode 100644 index 0000000..7335624 --- /dev/null +++ b/rfc1035/rfc1035qa.c @@ -0,0 +1,238 @@ +/* +** Copyright 1998 - 2011 Double Precision, Inc. +** See COPYING for distribution information. +*/ + +#include	"rfc1035.h" +#include	<errno.h> +#include	<string.h> +#include	<stdlib.h> +#include	<arpa/inet.h> + +/* Convenient function to do forward IP lookup */ + +#if	RFC1035_IPV6 + +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) +#endif +{ +struct	rfc1035_reply *reply; +int	n, o; +char	namebuf[RFC1035_MAXNAMESIZE+1]; + +	namebuf[0]=0; +	strncat(namebuf, name, RFC1035_MAXNAMESIZE); + +	*iasize=0; +	if (rfc1035_resolve_cname(res, namebuf, +		RFC1035_TYPE_A, RFC1035_CLASS_IN, &reply, 0) < 0 || +		reply == 0 || +		(n=rfc1035_replysearch_an( res, reply, namebuf, RFC1035_TYPE_A, +			RFC1035_CLASS_IN, 0)) < 0) +	{ +		if (reply && reply->rcode != RFC1035_RCODE_NXDOMAIN && +			reply->rcode != RFC1035_RCODE_NOERROR) +		{ +			errno=EAGAIN; +			rfc1035_replyfree(reply); +			return (1);	/* soft error */ +		} + +		if (reply) rfc1035_replyfree(reply); +		errno=ENOENT; +		return (-1); /* hard error */ +	} + +	for (o=n; o >= 0; o=rfc1035_replysearch_an(res, reply, namebuf, +			RFC1035_TYPE_A, RFC1035_CLASS_IN, o+1)) +		++*iasize; + +	if ( *iasize == 0 ) +	{ +		errno=EAGAIN; +		rfc1035_replyfree(reply); +		return (-1); +	} + +	if ( (*iaptr=(struct in_addr *)malloc(sizeof(**iaptr)* *iasize)) == 0) +	{ +		rfc1035_replyfree(reply); +		return (-1); +	} + +	for (*iasize=0; n >= 0; n=rfc1035_replysearch_an(res, reply, namebuf, +			RFC1035_TYPE_A, RFC1035_CLASS_IN, n+1)) +	{ +		(*iaptr)[*iasize]= reply->allrrs[n]->rr.inaddr; +		++*iasize; +	} + +	rfc1035_replyfree(reply); +	return (0); +} + +#if	RFC1035_IPV6 + +/* +**	The IPv6 version issues two queries - for both A and AAAA records, +**	then maps any A record to IPv6. +** +**	If we get back both an AAAA for the IPv4-mapped address, and the +**	A record itself, ignore the dupe. +*/ + +static int we_have_that_ipv4(struct in6_addr in6, +	const struct	in_addr	*ia4ptr, +	unsigned	ia4len) +{ +char	buf[INET6_ADDRSTRLEN]; +const char *p; +struct in_addr in4; +unsigned i; + +	if (!IN6_IS_ADDR_V4MAPPED((&in6))) return (0);	/* Not an IPv4 addy */ + +	if (inet_ntop(AF_INET6, &in6, buf, sizeof(buf)) == 0) +		return (0);	/* WTF??? */ + +	if ((p=strrchr(buf, ':')) != 0) +		++p; +	else +		p=buf; + +	rfc1035_aton_ipv4(p, &in4); + +	for (i=0; i<ia4len; i++) +		if (ia4ptr[i].s_addr == in4.s_addr)	return (1); +	return (0); +} + +int rfc1035_a(struct rfc1035_res *res, +	const char *name, struct in6_addr **iaptr, unsigned *iasize) +{ +struct	rfc1035_reply *reply; +int	n, o; +char	namebuf[RFC1035_MAXNAMESIZE+1]; +int	enotfound=0; + +struct	in_addr	*ia4ptr; +unsigned	ia4len; +unsigned k; + +	n=rfc1035_a_ipv4(res, name, &ia4ptr, &ia4len); + +	if (n > 0) return (n); +	if (n < 0) +	{ +		if (errno != ENOENT)	return (n); +		ia4len=0; +		ia4ptr=0; +		enotfound=1; +	} + +	namebuf[0]=0; +	strncat(namebuf, name, RFC1035_MAXNAMESIZE); + +	*iasize=ia4len; +	reply=0; + +	/* +	** Resist the temptation to stick in "ia4len > 0 &&", below.  Why? +	** A) We get a connection from an IPv6 address. +	** B) Spam check: the IP address must resolve backwards and forwards. +	** C) There are IPv4 records for the same hostname as well. +	** D) This sux. +	*/ + +	if (rfc1035_resolve_cname(res, namebuf, +		RFC1035_TYPE_AAAA, RFC1035_CLASS_IN, &reply, 0) < 0 || +		reply == 0 || +		(n=rfc1035_replysearch_an( res, reply, namebuf, +					   RFC1035_TYPE_AAAA, +					   RFC1035_CLASS_IN, 0)) < 0) +	{ +		if (reply && reply->rcode != RFC1035_RCODE_NXDOMAIN && +			reply->rcode != RFC1035_RCODE_NOERROR && +			*iasize == 0) +			/* Unfortunately this is necessary.  Some swervers +			** return a TEMPFAIL for AAAA queries. +			*/ +		{ +			errno=EAGAIN; +			rfc1035_replyfree(reply); +			if (ia4len) +				free(ia4ptr); +			return (1);	/* soft error */ +		} + +		if (reply) rfc1035_replyfree(reply); +		if (enotfound) +			return (-1); +		enotfound=1; +		reply=0; +		n= -1; +	} +	else +	{ +		for (o=n; o >= 0; o=rfc1035_replysearch_an(res, reply, namebuf, +				RFC1035_TYPE_AAAA, RFC1035_CLASS_IN, o+1)) +		{ +			if (we_have_that_ipv4(reply->allrrs[n]->rr.in6addr, +				ia4ptr, ia4len)) +				continue; + +			++*iasize; +		} +	} + +	if ( *iasize == 0 && enotfound) +	{ +		if (ia4len) +			free(ia4ptr); +		errno=ENOENT; +		return (-1); +	} + +	if ( *iasize == 0 ) +	{ +		errno=EAGAIN; +		rfc1035_replyfree(reply); +		return (-1); +	} + +	if ( (*iaptr=(struct in6_addr *)malloc(sizeof(**iaptr)* *iasize)) == 0) +	{ +		rfc1035_replyfree(reply); +		return (-1); +	} + +	for (*iasize=0; n >= 0; n=rfc1035_replysearch_an(res, reply, namebuf, +			RFC1035_TYPE_AAAA, RFC1035_CLASS_IN, n+1)) +	{ +		if (we_have_that_ipv4(reply->allrrs[n]->rr.in6addr, +			ia4ptr, ia4len)) +			continue; +		(*iaptr)[*iasize]= reply->allrrs[n]->rr.in6addr; +		++*iasize; +	} + +	for (k=0; k<ia4len; k++) +	{ +	char	buf[INET6_ADDRSTRLEN]; + +		strcpy(buf, "::ffff:"); +		rfc1035_ntoa_ipv4( &ia4ptr[k], buf+sizeof("::ffff:")-1); +		if (inet_pton( AF_INET6, buf, (*iaptr)+ *iasize) <= 0) +			memset( (*iaptr)+ *iasize, 0, sizeof(*iaptr)); +		++*iasize; +	} +	if (ia4len) +		free (ia4ptr); +	rfc1035_replyfree(reply); +	return (0); +} +#endif diff --git a/rfc1035/rfc1035qptr.c b/rfc1035/rfc1035qptr.c new file mode 100644 index 0000000..704d849 --- /dev/null +++ b/rfc1035/rfc1035qptr.c @@ -0,0 +1,149 @@ +/* +** Copyright 1998 - 2011 Double Precision, Inc. +** See COPYING for distribution information. +*/ + +#include	"rfc1035.h" +#include	<string.h> +#include	<errno.h> + + +static int ptr(struct rfc1035_res *, const void *, int, +	       void (*)(const char *, void *), +	       void *); + +static void save_name(const char *name, void *void_arg) +{ +	strcpy((char *)void_arg, name); +} + +int rfc1035_ptr(struct rfc1035_res *res, const RFC1035_ADDR *addr, +		  char *name) +{ +	return rfc1035_ptr_x(res, addr, save_name, name); +} + +/* Convenient function to do reverse IP lookup */ +#if	RFC1035_IPV6 + +int rfc1035_ptr_x(struct rfc1035_res *res, const RFC1035_ADDR *addr, +		  void (*cb_func)(const char *, void *), +		  void *cb_arg) +{ +struct	in_addr	in4; + +	if (IN6_IS_ADDR_V4MAPPED(addr)) +	{ +		rfc1035_ipv6to4(&in4, addr); +		if (ptr(res, &in4, AF_INET, cb_func, cb_arg) == 0) return (0); +		return (-1); +	} +	return (ptr(res, addr, AF_INET6, cb_func,cb_arg)); +} + +#else +int rfc1035_ptr_x(struct rfc1035_res *res, const RFC1035_ADDR *a, +		  void (*cb_func)(const char *, void *), +		  void *cb_arg) +{ +	return (ptr(res, a, AF_INET, cb_func, cb_arg)); +} +#endif + +static int ptr(struct rfc1035_res *res, const void *addr, int af, +	       void (*cb_func)(const char *, void *), +	       void *cb_arg) +{ +struct	rfc1035_reply *reply; +int	n; +char	name[256], ptrbuf[256]; + +#if	RFC1035_IPV6 + +	if (af == AF_INET6) +	{ +	const char *sin6=(const char *)addr; +	unsigned i; + +		*name=0; +		for (i=sizeof(struct in6_addr); i; ) +		{ +		char	buf[10]; + +			--i; +			sprintf(buf, "%x.%x.", +				(int)(unsigned char)(sin6[i] & 0x0F), +				(int)(unsigned char)((sin6[i] >> 4) & 0x0F)); +			strcat(name, buf); +		} +		strcat(name, "ip6.arpa"); +	} +	else +#endif +	if (af != AF_INET) +	{ +		errno=ENOENT; +		return (-1); /* hard error */ +	} +	else +	{ +	const char *p; +	unsigned char a=0,b=0,c=0,d=0; +	struct	in_addr	ia; + +		memcpy(&ia, addr, sizeof(ia)); +		rfc1035_ntoa_ipv4(&ia, name); +		p=name; + +		while (*p >= '0' && *p <= '9') +			a= (int)a * 10 + (*p++ - '0'); +		if (*p)	p++; +		while (*p >= '0' && *p <= '9') +			b= (int)b * 10 + (*p++ - '0'); +		if (*p)	p++; +		while (*p >= '0' && *p <= '9') +			c= (int)c * 10 + (*p++ - '0'); +		if (*p)	p++; +		while (*p >= '0' && *p <= '9') +			d= (int)d * 10 + (*p++ - '0'); + +		sprintf(name, "%d.%d.%d.%d.in-addr.arpa", +			(int)d, (int)c, (int)b, (int)a); +	} + +	if (rfc1035_resolve_cname(res, name, +		RFC1035_TYPE_PTR, RFC1035_CLASS_IN, &reply, 0) < 0 || +		reply == 0 || +		(n=rfc1035_replysearch_an( res, reply, name, RFC1035_TYPE_PTR, +			RFC1035_CLASS_IN, 0)) < 0 || +		rfc1035_replyhostname(reply, reply->allrrs[n]->rr.domainname, +			ptrbuf) == 0) +	{ +		if (reply && reply->rcode != RFC1035_RCODE_NXDOMAIN && +			reply->rcode != RFC1035_RCODE_NOERROR) +		{ +			rfc1035_replyfree(reply); +			errno=EAGAIN; +			return (-1); +		} + +		if (reply) rfc1035_replyfree(reply); +		errno=ENOENT; +		return (-1); /* hard error */ +	} + +	(*cb_func)(ptrbuf, cb_arg); + +	while ((n=rfc1035_replysearch_an(res, reply, name, +					 RFC1035_TYPE_PTR, +					 RFC1035_CLASS_IN, n+1)) >= 0) +	{ +		if (rfc1035_replyhostname(reply, +					  reply->allrrs[n]->rr.domainname, +					  ptrbuf)) +			(*cb_func)(ptrbuf, cb_arg); +	} + +	rfc1035_replyfree(reply); +	return (0); +} diff --git a/rfc1035/rfc1035reply.c b/rfc1035/rfc1035reply.c new file mode 100644 index 0000000..98c3766 --- /dev/null +++ b/rfc1035/rfc1035reply.c @@ -0,0 +1,479 @@ +/* +** Copyright 1998 - 1999 Double Precision, Inc. +** See COPYING for distribution information. +*/ + +#include	"rfc1035.h" +#include	<stdlib.h> +#include	<string.h> +#include	<errno.h> + + +void rfc1035_replyfree(struct rfc1035_reply *p) +{ +struct	rfc1035_reply	*q; + +	while (p) +	{ +		q=p->next; +		if (p->mallocedbuf)	free(p->mallocedbuf); +		if (p->qdptr)	free(p->qdptr); +		if (p->anptr)	free(p->anptr); +		if (p->nsptr)	free(p->nsptr); +		if (p->arptr)	free(p->arptr); +		if (p->allrrs)	free(p->allrrs); +		free(p); +		p=q; +	} +} + +const char *rfc1035_replyuncompress(const char **ptr, +	const struct rfc1035_reply *reply, char *namebuf) +{ +unsigned i=0; +unsigned l; +const char *end=reply->reply+reply->replylen; +const char *newptr=0; +int	cnt=0; + +	for (;;) +	{ +		if ( (*ptr) == end)	return (0); +		l=(unsigned char)*(*ptr)++; +		if (l > 63) +		{ +			if ((l & 0xC0) != 0xC0)	return (0); +			if ( (*ptr) == end)	return (0); +			l= ((l & 63) << 8) + (unsigned char)*(*ptr)++; +			if (l >= reply->replylen)	return (0); +			ptr= &newptr; +			newptr=reply->reply+l; +			if ( ++cnt > 100)	return (0); +						/* Detect loops */ + +			continue; +		} +		if (l == 0)	break; +		if (i) +		{ +			if (i >= RFC1035_MAXNAMESIZE)	return (0); +			if (namebuf) namebuf[i]='.'; +			++i; +		} +		if (RFC1035_MAXNAMESIZE-i < l)	return (0); + +		if (reply->reply + reply->replylen - (*ptr) < l) +			return (0); + +		if (namebuf) +			memcpy(namebuf+i, *ptr, l); +		i += l; +		*ptr += l; +	} + +	if (namebuf)	namebuf[i]=0; +	return (namebuf ? namebuf:""); +} + +const char *rfc1035_replyhostname( const struct rfc1035_reply *r, const char *n, +	char *namebuf) +{ +const char *p=rfc1035_replyuncompress(&n, r, namebuf); + +	if (!p)	strcpy(namebuf, "error"); +	return (p); +} + +static void doparse(struct rfc1035_reply *, struct rfc1035_rr *); + +struct rfc1035_reply *rfc1035_replyparse(const char *p, unsigned l) +{ +struct rfc1035_reply *r=(struct rfc1035_reply *) +	malloc(sizeof(struct rfc1035_reply)); +char	c; +unsigned n; +int	pass; +unsigned *lenptrs[3]; +struct rfc1035_rr *rrptrs[3]; +const char *end=p+l; +unsigned allcnt; + +	if (!r)	return (0); +	r->next=0; +	r->reply=p; +	r->replylen=l; +	r->mallocedbuf=0; +	memset(&r->server_addr, 0, sizeof(r->server_addr)); +	r->qdptr=0; +	r->anptr=0; +	r->nsptr=0; +	r->arptr=0; +	r->allrrs=0; + +	if (l < 12) +	{ +		errno=EINVAL; +		rfc1035_replyfree(r); +		return (0); +	} + +	c=p[2]; +	r->rd= c & 1; +	c >>= 1; +	r->tc= c & 1; +	c >>= 1; +	r->aa= c & 1; +	c >>= 1; +	r->opcode= c & 15; +	c >>= 4; +	r->qr= c & 1; + +	r->ra=(p[3] >> 7) & 1; +	r->ad=(p[3] >> 5) & 1; +	r->cd=(p[3] >> 4) & 1; +	r->rcode=p[3] & 15; + +	r->qdcount= ((unsigned)(unsigned char)p[4] << 8) | (unsigned char)p[5]; +	r->ancount= ((unsigned)(unsigned char)p[6] << 8) | (unsigned char)p[7]; +	r->nscount= ((unsigned)(unsigned char)p[8] << 8) | (unsigned char)p[9]; +	r->arcount= ((unsigned)(unsigned char)p[10]<< 8) | (unsigned char)p[11]; + +	if (r->qdcount >= l || r->ancount >= l || r->nscount >= l || +		r->arcount >= l || +		(n=r->qdcount + r->ancount) >= l || +		(n += r->nscount) >= l || +		n + r->arcount >= l) +	{ +		errno=EINVAL; +		rfc1035_replyfree(r); +		return (0); +	} + +	if ((r->qdcount && (r->qdptr=malloc(sizeof(struct rfc1035_query) +				* r->qdcount)) == 0) || +		(r->ancount && (r->anptr=malloc(sizeof(struct rfc1035_rr) +				* r->ancount)) == 0) || +		(r->nscount && (r->nsptr=malloc(sizeof(struct rfc1035_rr) +				* r->nscount)) == 0) || +		(r->arcount && (r->arptr=malloc(sizeof(struct rfc1035_rr) +				* r->arcount)) == 0) || +		(r->ancount + r->nscount + r->arcount && +			(r->allrrs=malloc(sizeof(*r->allrrs)*( +				r->ancount + r->nscount + r->arcount))) == 0) +		) +	{ +		rfc1035_replyfree(r); +		return (0); +	} + +	p += 12; +	l -= 12; +	for (n=0; n<r->qdcount; n++) +	{ +		r->qdptr[n].name=p; +		if (rfc1035_replyuncompress( &p, r, 0 ) == 0) +		{ +			errno=EINVAL; +			rfc1035_replyfree(r); +			return (0); +		} + +		if (p > end-4)	return (0); + +		r->qdptr[n].qtype=((unsigned)(unsigned char)p[0] << 8) +				| (unsigned char)p[1]; +		p += 2; +		r->qdptr[n].qclass=((unsigned)(unsigned char)p[0] << 8) +				| (unsigned char)p[1]; +		p += 2; +	} + +	lenptrs[0]= &r->ancount; +	lenptrs[1]= &r->nscount; +	lenptrs[2]= &r->arcount; +	rrptrs[0]= r->anptr; +	rrptrs[1]= r->nsptr; +	rrptrs[2]= r->arptr; + +	allcnt=0; + +	for (pass=0; pass<3; pass++) +	{ +	struct rfc1035_rr *rrp= rrptrs[pass]; + +		for (n=0; n< *lenptrs[pass]; n++) +		{ +			r->allrrs[allcnt++]=rrp; +			rrp->rrname=p; +			if (rfc1035_replyuncompress( &p, r, 0 ) +				== 0) +			{ +				errno=EINVAL; +				rfc1035_replyfree(r); +				return (0); +			} + +			if (p > end-10)	return (0); +			rrp->rrtype=((unsigned)(unsigned char)p[0] << 8) +				| (unsigned char)p[1]; +			p += 2; +			rrp->rrclass=((unsigned)(unsigned char)p[0] << 8) +				| (unsigned char)p[1]; +			p += 2; +			rrp->ttl=((RFC1035_UINT32)(unsigned char)p[0] << 24) | +				((RFC1035_UINT32)(unsigned char)p[1] << 16) | +				((RFC1035_UINT32)(unsigned char)p[2] << 8) | +				(unsigned char)p[3]; +			p += 4; + +			rrp->rdlength=((unsigned)(unsigned char)p[0] << 8) +				| (unsigned char)p[1]; +			p += 2; +			rrp->rdata= rrp->rdlength ? p:0; +			if (end - p < rrp->rdlength) +			{ +				errno=EINVAL; +				rfc1035_replyfree(r); +				return (0); +			} +			p += rrp->rdlength; +			doparse(r, rrp); +			++rrp; +		} +	} +	return (r); +} + +static void doparse(struct rfc1035_reply *r, struct rfc1035_rr *rr) +{ +const char *p; +static const char error[]="\005error"; + +	memset(&rr->rr, '\0', sizeof(rr->rr)); +	if (rr->rrclass != RFC1035_CLASS_IN)	return; + +	switch (rr->rrtype)	{ +	case RFC1035_TYPE_A: + +		if (rr->rdlength < 4) +			rr->rr.inaddr.s_addr=0; +		else +			memcpy(&rr->rr.inaddr.s_addr, +				rr->rdata, 4); +		break; +#if	RFC1035_IPV6 +	case RFC1035_TYPE_AAAA: +		memset(&rr->rr.in6addr, 0, sizeof(rr->rr.in6addr)); +		if (rr->rdlength >= sizeof(struct in6_addr)) +			memcpy(&rr->rr.in6addr, rr->rdata, +				sizeof(rr->rr.in6addr)); +		break; +#endif +	case RFC1035_TYPE_CNAME: +	case RFC1035_TYPE_MB: +	case RFC1035_TYPE_MG: +	case RFC1035_TYPE_MR: +	case RFC1035_TYPE_MD: +	case RFC1035_TYPE_MF: +	case RFC1035_TYPE_NS: +	case RFC1035_TYPE_PTR: +		rr->rr.domainname=p=rr->rdata; +		if (rfc1035_replyuncompress(&p, r, 0) == 0 || +			p > rr->rdata + rr->rdlength) +			rr->rr.domainname=error; +		break; + +	case RFC1035_TYPE_SOA: +		rr->rr.soa.mname_label=p=rr->rdata; + +		if (rr->rdlength < 20 || +			rfc1035_replyuncompress(&p, r, 0) == 0 || +				p > rr->rdata + rr->rdlength || +			(rr->rr.soa.rname_label=p, rfc1035_replyuncompress(&p, +				r, 0)) == 0 || +				p > rr->rdata + rr->rdlength - 20) +		{ +			rr->rr.soa.mname_label=error; +			rr->rr.soa.rname_label=error; +			rr->rr.soa.serial=0; +			rr->rr.soa.refresh=0; +			rr->rr.soa.retry=0; +			rr->rr.soa.expire=0; +			rr->rr.soa.minimum=0; +		} +		else +		{ +			rr->rr.soa.serial= +				((RFC1035_UINT32)(unsigned char)p[0] << 24) | +				((RFC1035_UINT32)(unsigned char)p[1] << 16) | +				((RFC1035_UINT32)(unsigned char)p[2] << 8) | +				(unsigned char)p[3]; +			p += 4; +			rr->rr.soa.refresh= +				((RFC1035_UINT32)(unsigned char)p[0] << 24) | +				((RFC1035_UINT32)(unsigned char)p[1] << 16) | +				((RFC1035_UINT32)(unsigned char)p[2] << 8) | +				(unsigned char)p[3]; +			p += 4; +			rr->rr.soa.retry= +				((RFC1035_UINT32)(unsigned char)p[0] << 24) | +				((RFC1035_UINT32)(unsigned char)p[1] << 16) | +				((RFC1035_UINT32)(unsigned char)p[2] << 8) | +				(unsigned char)p[3]; +			p += 4; +			rr->rr.soa.expire= +				((RFC1035_UINT32)(unsigned char)p[0] << 24) | +				((RFC1035_UINT32)(unsigned char)p[1] << 16) | +				((RFC1035_UINT32)(unsigned char)p[2] << 8) | +				(unsigned char)p[3]; +			p += 4; +			rr->rr.soa.minimum= +				((RFC1035_UINT32)(unsigned char)p[0] << 24) | +				((RFC1035_UINT32)(unsigned char)p[1] << 16) | +				((RFC1035_UINT32)(unsigned char)p[2] << 8) | +				(unsigned char)p[3]; +		} +		break; +	case RFC1035_TYPE_MX: +		p=rr->rdata; +		if (rr->rdlength < 2) +		{ +			rr->rr.mx.preference=0; +			rr->rr.mx.mx_label=error; +		} +		else +		{ +			rr->rr.mx.preference= +				((unsigned)(unsigned char)p[0] << 8) | +				(unsigned char)p[1]; +			p += 2; +			rr->rr.mx.mx_label=p; +			if (rfc1035_replyuncompress(&p, r, 0) == 0 +				|| p > rr->rdata + rr->rdlength) +			{ +				rr->rr.mx.preference=0; +				rr->rr.mx.mx_label=error; +			} +		} +		break; + +	case RFC1035_TYPE_HINFO: +		p=rr->rdata; +		rr->rr.hinfo.hinfo_str=error; +		rr->rr.hinfo.os_str=error; +		if (rr->rdlength && (unsigned char)*p < rr->rdlength) +		{ +		const char *q=p+1+(unsigned char)*p; + +			if (q < rr->rdata + rr->rdlength && +				q+(unsigned char)*q < rr->rdata + rr->rdlength) +			{ +				rr->rr.hinfo.hinfo_str=p; +				rr->rr.hinfo.os_str=q; +			} +		} +		break; + +	case RFC1035_TYPE_MINFO: +		rr->rr.minfo.rmailbx_label=p=rr->rdata; +		if (rfc1035_replyuncompress(&p, r, 0) == 0 || +			p > rr->rdata + rr->rdlength || +			(rr->rr.minfo.emailbx_label=p, +				rfc1035_replyuncompress(&p, r, 0)) == 0 || +				p > rr->rdata + rr->rdlength) +		{ +			rr->rr.minfo.rmailbx_label=error; +			rr->rr.minfo.emailbx_label=error; +		} +		break; +	case RFC1035_TYPE_RRSIG: + +		p=rr->rdata; + +		if (rr->rdlength < 18 || + +		    ((rr->rr.rrsig.type_covered= +		      ((unsigned)(unsigned char)p[0] << 8) +		      | (unsigned char)p[1]), + +		     (rr->rr.rrsig.algorithm=p[2]), +		     (rr->rr.rrsig.labels=p[3]), +		     (rr->rr.rrsig.original_ttl= +		      ((RFC1035_UINT32)(unsigned char)p[4] << 24) | +		      ((RFC1035_UINT32)(unsigned char)p[5] << 16) | +		      ((RFC1035_UINT32)(unsigned char)p[6] << 8) | +		      (unsigned char)p[7]), +		     (rr->rr.rrsig.signature_expiration= +		      ((RFC1035_UINT32)(unsigned char)p[8] << 24) | +		      ((RFC1035_UINT32)(unsigned char)p[9] << 16) | +		      ((RFC1035_UINT32)(unsigned char)p[10] << 8) | +		      (unsigned char)p[11]), +		     (rr->rr.rrsig.signature_inception= +		      ((RFC1035_UINT32)(unsigned char)p[12] << 24) | +		      ((RFC1035_UINT32)(unsigned char)p[13] << 16) | +		      ((RFC1035_UINT32)(unsigned char)p[14] << 8) | +		      (unsigned char)p[15]), + 		     (rr->rr.rrsig.key_tag= +		      ((RFC1035_UINT16)(unsigned char)p[16] << 8) | +		      (unsigned char)p[17]), +		     (rr->rr.rrsig.signer_name=(p += 18)), + +		     rfc1035_replyuncompress(&p, r, 0) == 0) || +		    p > rr->rdata + rr->rdlength) +		{ +			memset(&rr->rr.rrsig, 0, sizeof(rr->rr.rrsig)); +			rr->rr.rrsig.signer_name=error; +			break; +		} + +		rr->rr.rrsig.signature=p; +		rr->rr.rrsig.signature_len=rr->rdata + rr->rdlength - p; +		break; + +	default: +		break; +	} +} + +/* +** Randomize order of resource records. +*/ + +static void rr_random(struct rfc1035_rr *p, unsigned n) +{ +	unsigned i; + +	for (i=0; i<n; i++) +	{ +		size_t j=rand() % n; + +		{ +			struct rfc1035_rr buf=p[j]; + +			p[j]=p[i]; +			p[i]=buf; +		} +	} +} + +void rfc1035_rr_rand_an(struct rfc1035_reply *rr) +{ +	rr_random(rr->anptr, rr->ancount); +} + +void rfc1035_rr_rand_ns(struct rfc1035_reply *rr) +{ +	rr_random(rr->nsptr, rr->nscount); +} + +void rfc1035_rr_rand_ar(struct rfc1035_reply *rr) +{ +	rr_random(rr->arptr, rr->arcount); +} + +void rfc1035_rr_rand(struct rfc1035_reply *rr) +{ +	rfc1035_rr_rand_an(rr); +	rfc1035_rr_rand_ns(rr); +	rfc1035_rr_rand_ar(rr); +} diff --git a/rfc1035/rfc1035resolve.c b/rfc1035/rfc1035resolve.c new file mode 100644 index 0000000..d772eb0 --- /dev/null +++ b/rfc1035/rfc1035resolve.c @@ -0,0 +1,236 @@ +/* +** Copyright 1998 - 2000 Double Precision, Inc. +** See COPYING for distribution information. +*/ + +#include	"config.h" +#include	<stdio.h> +#include	"soxwrap/soxwrap.h" +#include	"rfc1035.h" +#include	<errno.h> +#if	HAVE_UNISTD_H +#include	<unistd.h> +#endif +#include	<stdlib.h> +#include	<string.h> + + +struct querybuf { +	char qbuf[512]; +	unsigned qbuflen; +	} ; + +static void putqbuf(const char *p, unsigned l, void *q) +{ +struct querybuf *qp=(struct querybuf *)q; + +	if (qp->qbuflen < sizeof(qp->qbuf) && +			sizeof(qp->qbuf) - qp->qbuflen >= l) +		memcpy(qp->qbuf+qp->qbuflen, p, l); +	qp->qbuflen += l; +} + +struct rfc1035_reply *rfc1035_resolve_multiple( +			struct rfc1035_res *res, +			int opcode, +			const struct rfc1035_query *queries, +			unsigned nqueries) +{ +struct	querybuf qbuf; +int	udpfd; +int	attempt; +const RFC1035_ADDR *ns; +unsigned nscount; +unsigned current_timeout, timeout_backoff; +unsigned nbackoff, backoff_num; +int	af; +static const char fakereply[]={0, 0, 0, RFC1035_RCODE_SERVFAIL, +		0, 0, +		0, 0, +		0, 0, +		0, 0}; + +	nscount=res->rfc1035_nnameservers; +	ns=res->nameservers; + +	if (res->rfc1035_good_ns >= nscount) +		res->rfc1035_good_ns=0; + +	qbuf.qbuflen=0; +	if ( rfc1035_mkquery(res, +		opcode, queries, nqueries, &putqbuf, &qbuf)) +	{ +		errno=EINVAL; +		return (0); +	} + +	/* Prepare the UDP socket */ + +	if ((udpfd=rfc1035_open_udp(&af)) < 0)	return (0); + +	/* Keep trying until we get an answer from a nameserver */ + +	current_timeout=res->rfc1035_timeout_initial; +	nbackoff=res->rfc1035_timeout_backoff; +	if (!current_timeout)	current_timeout=RFC1035_DEFAULT_INITIAL_TIMEOUT; +	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) +	{ +	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) +			{ +				dotcp=1; +				isaxfr=1; +				break; +			} + +		if (isaxfr && nqueries > 1) +			return (rfc1035_replyparse(fakereply, +				sizeof(fakereply))); + +		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 ((reply=rfc1035_query_udp(res, udpfd, addrptr, +				addrptrlen, qbuf.qbuf, qbuf.qbuflen, &nbytes, +					current_timeout)) == 0) +				continue; + +			res->rfc1035_good_ns= (res->rfc1035_good_ns + attempt) % +					nscount; + +		/* Parse the reply */ + +			rfcreply=rfc1035_replyparse(reply, nbytes); +			if (!rfcreply) +			{ +				free(reply); +				if (errno == ENOMEM)	break; +				continue; +			/* Bad response from the server, try the next one. */ +			} +			rfcreply->mallocedbuf=reply; +		/* +		** If the reply came back with the truncated bit set, +		** retry the query via TCP. +		*/ + +			if (rfcreply->tc) +			{ +				dotcp=1; +				rfc1035_replyfree(rfcreply); +			} +		} + +		if (dotcp) +		{ +		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 (!reply) +			{ +				sox_close(tcpfd); +				continue; +			} + +			res->rfc1035_good_ns= (res->rfc1035_good_ns +					+ attempt) % nscount; + +			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) +					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; +			} +			sox_close(tcpfd); +			if (!firstreply) +				return (0); +			rfcreply=firstreply; +		} +		memcpy(&rfcreply->server_addr, sin, sin_len); +		sox_close(udpfd); +		return (rfcreply); +	} + +	/* +	** Return a fake server failure reply, when we couldn't contact +	** any name server. +	*/ +	sox_close(udpfd); +	return (rfc1035_replyparse(fakereply, sizeof(fakereply))); +} + +struct rfc1035_reply *rfc1035_resolve( +	struct rfc1035_res *res, +	int opcode, +	const char *name, +	unsigned qtype, +	unsigned qclass) +{ +struct rfc1035_query q; + +	q.name=name; +	q.qtype=qtype; +	q.qclass=qclass; +	return (rfc1035_resolve_multiple(res, opcode, &q, 1)); +} diff --git a/rfc1035/rfc1035sameip.c b/rfc1035/rfc1035sameip.c new file mode 100644 index 0000000..01a7e03 --- /dev/null +++ b/rfc1035/rfc1035sameip.c @@ -0,0 +1,55 @@ +/* +** Copyright 1998 - 2000 Double Precision, Inc. +** See COPYING for distribution information. +*/ +#include	"config.h" +#include	"rfc1035.h" +#include	<sys/types.h> +#include	<sys/socket.h> +#include	<arpa/inet.h> +#include	<errno.h> +#include	<string.h> + + +/* +**	We have two socket addresses.  Did they come from the same IP +**	address? +*/ + +#if RFC1035_IPV6 + +int rfc1035_same_ip(const void *a, int al, const void *b, int bl) +{ +char	bufa[INET6_ADDRSTRLEN]; +char	bufb[INET6_ADDRSTRLEN]; +struct	in6_addr in6a; +struct	in6_addr in6b; +const char *as, *bs; + +	if (rfc1035_sockaddrip(a, al, &in6a) || +		rfc1035_sockaddrip(b, bl, &in6b) || +		(as=inet_ntop(AF_INET6, &in6a, bufa, sizeof(bufa))) == 0 || +		(bs=inet_ntop(AF_INET6, &in6b, bufb, sizeof(bufb))) == 0 || +		strcmp(as, bs)) +		return (0); +	return (1); +} + +#else + +int rfc1035_same_ip(const void *a, int al, const void *b, int bl) +{ +	if ( ((const struct sockaddr_in *)a)->sin_family != AF_INET || +		((const struct sockaddr_in *)b)->sin_family != AF_INET || +		al < sizeof(struct sockaddr_in) || +		bl < sizeof(struct sockaddr_in)) +	{ +		return (0); +	} + +	return ( ((const struct sockaddr_in *)a)->sin_addr.s_addr == +		((const struct sockaddr_in *)b)->sin_addr.s_addr); +} + +#endif + diff --git a/rfc1035/rfc1035search.c b/rfc1035/rfc1035search.c new file mode 100644 index 0000000..8e69ad4 --- /dev/null +++ b/rfc1035/rfc1035search.c @@ -0,0 +1,214 @@ +/* +** Copyright 1998 - 2011 Double Precision, Inc. +** See COPYING for distribution information. +*/ + +#include	"rfc1035.h" +#include	<string.h> +#include	<stdlib.h> + + +static int found(const struct rfc1035_reply *r, +	const struct rfc1035_rr *rrp, const char *h, +	unsigned qclass, unsigned qtype) +{ +char	namebuf[RFC1035_MAXNAMESIZE+1]; + +	if ((rrp->rrtype != qtype && +		rrp->rrtype != RFC1035_TYPE_CNAME) || +			rrp->rrclass != qclass)	return (0); + +	if (rfc1035_replyhostname(r, rrp->rrname, namebuf) == 0) +		return (0); + +	if (rfc1035_hostnamecmp(namebuf, h) == 0)	return (1); +	return (0); +} + +int rfc1035_replysearch_an(const struct rfc1035_res *res, +			   const struct rfc1035_reply *r, const char *h, +			   unsigned qtype, unsigned qclass, int pos) +{ +	if (res->rfc1035_defaultdomain && strchr(h, '.') == NULL) +	{ +		char *buf; +		int rc; + +		/* Append default domain */ + +		if ((buf=malloc(strlen(h)+ +				strlen(res->rfc1035_defaultdomain)+2)) == 0) +			return (-1); + +		strcat(strcat(strcpy(buf, h), "."), +		       res->rfc1035_defaultdomain); + +		rc=rfc1035_replysearch_an(res, r, buf, qtype, qclass, pos); +		free(buf); +		return (rc); +	} + +	for ( ; pos >= 0 && pos < r->ancount; pos++) +		if (found(r, r->anptr+pos, h, qclass, qtype))	return (pos); +	return (-1); +} + +int rfc1035_replysearch_ns(const struct rfc1035_res *res, +			   const struct rfc1035_reply *r, const char *h, +			   unsigned qtype, unsigned qclass, int pos) +{ +	if (res->rfc1035_defaultdomain && strchr(h, '.') == NULL) +	{ +		char *buf; +		int rc; + +		/* Append default domain */ + +		if ((buf=malloc(strlen(h)+ +				strlen(res->rfc1035_defaultdomain)+2)) == 0) +			return (-1); + +		strcat(strcat(strcpy(buf, h), "."), +		       res->rfc1035_defaultdomain); + +		rc=rfc1035_replysearch_ns(res, r, buf, qtype, qclass, pos); +		free(buf); +		return (rc); +	} + +	for ( ; pos >= 0 && pos < r->nscount; pos++) +		if (found(r, r->nsptr+pos, h, qclass, qtype))	return (pos); +	return (-1); +} + +int rfc1035_replysearch_ar(const struct rfc1035_res *res, +			   const struct rfc1035_reply *r, const char *h, +			   unsigned qtype, unsigned qclass, int pos) +{ +	if (res->rfc1035_defaultdomain && strchr(h, '.') == NULL) +	{ +		char *buf; +		int rc; + +		/* Append default domain */ + +		if ((buf=malloc(strlen(h)+ +				strlen(res->rfc1035_defaultdomain)+2)) == 0) +			return (-1); + +		strcat(strcat(strcpy(buf, h), "."), +		       res->rfc1035_defaultdomain); + +		rc=rfc1035_replysearch_ar(res, r, buf, qtype, qclass, pos); +		free(buf); +		return (rc); +	} + +	for ( ; pos >= 0 && pos < r->arcount; pos++) +		if (found(r, r->arptr+pos, h, qclass, qtype))	return (pos); +	return (-1); +} + +int rfc1035_replysearch_all(const struct rfc1035_res *res, +			   const struct rfc1035_reply *r, const char *h, +			    unsigned qtype, unsigned qclass, int pos) +{ +	unsigned s; + +	if (res->rfc1035_defaultdomain && strchr(h, '.') == NULL) +	{ +		char *buf; +		int rc; + +		/* Append default domain */ + +		if ((buf=malloc(strlen(h)+ +				strlen(res->rfc1035_defaultdomain)+2)) == 0) +			return (-1); + +		strcat(strcat(strcpy(buf, h), "."), +		       res->rfc1035_defaultdomain); + +		rc=rfc1035_replysearch_all(res, r, buf, qtype, qclass, pos); +		free(buf); +		return (rc); +	} + +	s=r->ancount+r->nscount+r->arcount; + +	for ( ; pos >= 0 && pos < s; pos++) +		if (found(r, r->allrrs[pos], h, qclass, qtype))	return (pos); +	return (-1); +} + +int rfc1035_resolve_cname(struct rfc1035_res *res, char *namebuf, +			  unsigned qtype, +			  unsigned qclass, +			  struct rfc1035_reply **ptr, +			  int x_flags) +{ +int	n; +int	recursion_count=10; + +	if ( (*ptr=rfc1035_resolve(res, RFC1035_OPCODE_QUERY, +			namebuf, qtype, qclass)) == 0) +		return (-1); + +	if (x_flags && RFC1035_X_RANDOMIZE && +	    (*ptr)->rcode == RFC1035_RCODE_NOERROR) +		rfc1035_rr_rand(*ptr); + +	if ( (*ptr)->rcode != RFC1035_RCODE_NOERROR || +		(n=rfc1035_replysearch_an( res, +					   *ptr, namebuf, qtype, qclass, 0))<0) +		return (-1); + +	if ( (*ptr)->anptr[n].rrtype == qtype)	return (n); + +	/* Must've gotten back a CNAME when we were looking for something else +	*/ + +	for (;;) +	{ + +		if (rfc1035_replyhostname( *ptr, (*ptr)->anptr[n].rr.domainname, +			namebuf) == 0) +		{ +			rfc1035_replyfree( *ptr ); +			*ptr=0; +			return (-1); +		} + +	/* Let's see if we already have the answer for the canonical name */ + +		if ( (n=rfc1035_replysearch_all( res, *ptr, namebuf, qtype, +				qclass, 0)) >= 0) +		{ +			if ( (*ptr)->anptr[n].rrtype == qtype)	return (n); +			if ( --recursion_count > 0) +				continue; + +			rfc1035_replyfree( *ptr );	/* Recursive CNAME */ +			*ptr=0; +			return (RFC1035_ERR_CNAME_RECURSIVE); +		} + +		rfc1035_replyfree( *ptr ); +		if ( (*ptr=rfc1035_resolve(res, RFC1035_OPCODE_QUERY, +				namebuf, qtype, qclass)) == 0) +			return (-1); + +		if ( (*ptr)->rcode == RFC1035_RCODE_NOERROR && +			(n=rfc1035_replysearch_an( res, *ptr, namebuf, +				qtype, qclass, 0)) >= 0) +		{ +			if ( (*ptr)->anptr[n].rrtype == qtype)	return (n); +			if ( --recursion_count > 0) +				continue; +			rfc1035_replyfree( *ptr );	/* Recursive CNAME */ +			*ptr=0; +			return (RFC1035_ERR_CNAME_RECURSIVE); +		} +		return (-1); +	} +} diff --git a/rfc1035/rfc1035sockaddrip.c b/rfc1035/rfc1035sockaddrip.c new file mode 100644 index 0000000..ab35ce5 --- /dev/null +++ b/rfc1035/rfc1035sockaddrip.c @@ -0,0 +1,45 @@ +/* +** Copyright 1998 - 2000 Double Precision, Inc. +** See COPYING for distribution information. +*/ +#include	"config.h" +#include	"rfc1035.h" +#include	<sys/types.h> +#include	<sys/socket.h> +#include	<arpa/inet.h> +#include	<errno.h> +#include	<string.h> + + +int rfc1035_sockaddrip(const RFC1035_NETADDR *a, int al, RFC1035_ADDR *ip) +{ +int	af=((const struct sockaddr_in *)a)->sin_family; + +	if ( af == AF_INET ) +	{ +		if (al >= sizeof(struct sockaddr_in)) +		{ +#if RFC1035_IPV6 +			rfc1035_ipv4to6(ip, +				&((const struct sockaddr_in *)a)->sin_addr); +#else +			*ip=((const struct sockaddr_in *)a)->sin_addr; +#endif +			return (0); +		} +	} + +#if RFC1035_IPV6 + +	if ( af == AF_INET6 ) +	{ +		if (al >= sizeof(struct sockaddr_in6)) +		{ +			*ip=((const struct sockaddr_in6 *)a)->sin6_addr; +			return (0); +		} +	} +#endif +	return (-1); +} + diff --git a/rfc1035/rfc1035sockaddrport.c b/rfc1035/rfc1035sockaddrport.c new file mode 100644 index 0000000..5e9b608 --- /dev/null +++ b/rfc1035/rfc1035sockaddrport.c @@ -0,0 +1,38 @@ +/* +** Copyright 1998 - 2000 Double Precision, Inc. +** See COPYING for distribution information. +*/ +#include	"config.h" +#include	"rfc1035.h" +#include	<sys/types.h> +#include	<sys/socket.h> +#include	<arpa/inet.h> +#include	<errno.h> + + +int rfc1035_sockaddrport(const RFC1035_NETADDR *a, int al, int *port) +{ +int	af=((const struct sockaddr_in *)a)->sin_family; + +	if ( af == AF_INET ) +	{ +		if (al >= sizeof(struct sockaddr_in)) +		{ +			*port=((const struct sockaddr_in *)a)->sin_port; +			return (0); +		} +	} + +#if RFC1035_IPV6 + +	if ( af == AF_INET6 ) +	{ +		if (al >= sizeof(struct sockaddr_in6)) +		{ +			*port=((const struct sockaddr_in6 *)a)->sin6_port; +			return (0); +		} +	} +#endif +	return (-1); +} diff --git a/rfc1035/rfc1035str.c b/rfc1035/rfc1035str.c new file mode 100644 index 0000000..4335369 --- /dev/null +++ b/rfc1035/rfc1035str.c @@ -0,0 +1,143 @@ +/* +** Copyright 1998 - 1999 Double Precision, Inc. +** See COPYING for distribution information. +*/ + +#include	"rfc1035.h" +#include	<string.h> + + +static struct { const char *name; int num; } typetab[]={ +	{"A",		1}, +	{"NS",		2}, +	{"MD",		3}, +	{"MF",		4}, +	{"CNAME",	5}, +	{"SOA",		6}, +	{"MB",		7}, +	{"MG",		8}, +	{"MR",		9}, +	{"NULL",	10}, +	{"WKS",		11}, +	{"PTR",		12}, +	{"HINFO",	13}, +	{"MINFO",	14}, +	{"MX",		15}, +	{"TXT",		16}, +	{"AAAA",	28}, +	{"RRSIG",	46}, +	{"AXFR",	252}, +	{"MAILB",	253}, +	{"MAILA",	254}, +	{"ANY",		255}}, + +	classtab[]={ + +	{"IN",		1}, +	{"CSNET",	2}, +	{"CHAOS",	3}, +	{"HESIOD",	4}, +	{"ANY",		255}}, + +	opcodetab[]={ +	{"QUERY",	0}, +	{"IQUERY",	1}, +	{"STATUS",	2}}, + +	rcodetab[]={ +	{"NOERROR",	0}, +	{"FORMAT",	1}, +	{"SERVFAIL",	2}, +	{"NXDOMAIN",	3}, +	{"UNIMPLEMENTED", 4}, +	{"REFUSED",	5}}; + +#if HAVE_STRCASECMP +#define	COMPARE(a,b)	strcasecmp((a), (b)) +#else +#define	COMPARE(a,b)	stricmp((a), (b)) +#endif + +void rfc1035_type_itostr(int n, void (*func)(const char *, void *), void *arg) +{ +	unsigned i; +	char buf[30]; + +	for (i=0; i<sizeof(typetab)/sizeof(typetab[0]); i++) +		if (typetab[i].num == n) +		{ +			(*func)(typetab[i].name, arg); +			return; +		} + +	snprintf(buf, sizeof(buf), "TYPE%d", n); + +	buf[sizeof(buf)-1]=0; + +	(*func)(buf, arg); +} + +int rfc1035_type_strtoi(const char *n) +{ +unsigned i; + +	for (i=0; i<sizeof(typetab)/sizeof(typetab[0]); i++) +		if (COMPARE(typetab[i].name, n) == 0) return (typetab[i].num); +	return (-1); +} + +const char *rfc1035_class_itostr(int n) +{ +unsigned i; + +	for (i=0; i<sizeof(classtab)/sizeof(classtab[0]); i++) +		if (classtab[i].num == n) return (classtab[i].name); +	return ("unknown"); +} + +int rfc1035_class_strtoi(const char *n) +{ +unsigned i; + +	for (i=0; i<sizeof(classtab)/sizeof(classtab[0]); i++) +		if (COMPARE(classtab[i].name, n) == 0) return (classtab[i].num); +	return (-1); +} + +const char *rfc1035_opcode_itostr(int n) +{ +unsigned i; + +	for (i=0; i<sizeof(opcodetab)/sizeof(opcodetab[0]); i++) +		if (opcodetab[i].num == n) return (opcodetab[i].name); +	return ("unknown"); +} + +int rfc1035_opcode_strtoi(const char *n) +{ +unsigned i; + +	for (i=0; i<sizeof(opcodetab)/sizeof(opcodetab[0]); i++) +		if (COMPARE(opcodetab[i].name, n) == 0) +				return (opcodetab[i].num); +	return (-1); +} + +const char *rfc1035_rcode_itostr(int n) +{ +unsigned i; + +	for (i=0; i<sizeof(rcodetab)/sizeof(rcodetab[0]); i++) +		if (rcodetab[i].num == n) return (rcodetab[i].name); +	return ("unknown"); +} + +int rfc1035_rcode_strtoi(const char *n) +{ +unsigned i; + +	for (i=0; i<sizeof(rcodetab)/sizeof(rcodetab[0]); i++) +		if (COMPARE(rcodetab[i].name, n) == 0) +				return (rcodetab[i].num); +	return (-1); +} diff --git a/rfc1035/rfc1035tcp.c b/rfc1035/rfc1035tcp.c new file mode 100644 index 0000000..69725da --- /dev/null +++ b/rfc1035/rfc1035tcp.c @@ -0,0 +1,193 @@ +/* +** Copyright 1998 - 2011 Double Precision, Inc. +** See COPYING for distribution information. +*/ + +#include	"config.h" +#include	"rfc1035.h" +#include	<stdio.h> +#include	<string.h> +#include	"soxwrap/soxwrap.h" +#include	<sys/types.h> +#include	<arpa/inet.h> +#if	HAVE_UNISTD_H +#include	<unistd.h> +#endif +#include	<stdlib.h> +#include	<errno.h> +#include	<fcntl.h> +#if TIME_WITH_SYS_TIME +#include	<sys/time.h> +#include	<time.h> +#else +#if HAVE_SYS_TIME_H +#include	<sys/time.h> +#else +#include	<time.h> +#endif +#endif + + +int rfc1035_open_tcp(struct rfc1035_res *res, const RFC1035_ADDR *addr) +{ +RFC1035_NETADDR addrbuf; +int	af; +const struct sockaddr *addrptr; +int	addrptrlen; + +int	fd=rfc1035_mksocket(SOCK_STREAM, 0, &af); + +	if (fd < 0)	return (-1); + +	if (rfc1035_mkaddress(af, &addrbuf, +		addr, htons(53), +		&addrptr, &addrptrlen)) +	{ +		close(fd); +		return (-1); +	} + +	if (fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NDELAY) < 0) +	{ +		sox_close(fd); +		return (-1); +	} + +	if (sox_connect(fd, addrptr, addrptrlen) == 0) +		return (fd); + +	if (errno == EINPROGRESS || errno == EAGAIN) +	{ +	unsigned	w=res->rfc1035_timeout_initial; + +		if (!w)	w=RFC1035_DEFAULT_INITIAL_TIMEOUT; +		if (rfc1035_wait_query(fd, w) == 0 && +			(sox_connect(fd, addrptr, addrptrlen) == 0 +				|| errno == EISCONN)) +		{ +			return (fd); +		} +	} +	sox_close(fd); +	return (-1); +} + +int rfc1035_send_tcp(int fd, const char *query, unsigned query_len) +{ +int	n=query_len+2; +char	*buf=malloc(n); +char	*p; +int	pl; + +	if (!buf)	return (-1); + +	buf[0]=query_len >> 8; +	buf[1]=query_len; + +	memcpy(buf+2, query, query_len); + +	p=buf; +	pl=n; +	while (pl) +	{ +	int	i=sox_write(fd, p, pl); + +		if (i < 0 && errno == EINTR)	continue; +		if (i <= 0)	break; +		p += i; +		pl -= i; +	} +	free(buf); + +	if (pl)	return (-1); +	return (0); +} + +static int doread(int fd, char *buf, int bufsize) +{ +int	len; + +	do +	{ +		len=sox_read(fd, buf, bufsize); +	} while (len < 0 && errno == EINTR); +	return (len); +} + +char *rfc1035_recv_tcp(struct rfc1035_res *res, int fd, int *buflen, unsigned w) +{ +int	len; +unsigned response_len; +char lenbuf[2]; +char	*mallocedbuf=0; +time_t	current_time, finish_time; + +	time(¤t_time); +	finish_time=current_time+w; + +	if (rfc1035_wait_reply(fd, w)) +		return (0); + +	len=doread(fd, lenbuf, 2); +	if (len <= 0) +	{ +		errno=EIO; +		return (0); +	} +	if (len == 1) +	{ +		time(¤t_time); +		if (current_time >= finish_time)	return (0); +		if (rfc1035_wait_reply(fd, finish_time - current_time)) +			return (0); + +		len=doread(fd, lenbuf+1, 1); +		if (len <= 0) +		{ +			errno=EIO; +			return (0); +		} +		++len; +	} + +	response_len= ((unsigned)(unsigned char)lenbuf[0] << 8) +			| (unsigned char)lenbuf[1]; + +	if ((mallocedbuf=malloc(response_len)) == 0) +		return (0); + +	*buflen=0; + +	while ((unsigned)*buflen < response_len) +	{ +		time(¤t_time); +		if (current_time >= finish_time || +			rfc1035_wait_reply(fd, finish_time - current_time)) +		{ +			len=0; +			errno=ETIMEDOUT; +		} +		else +			len=doread(fd, mallocedbuf + *buflen, +						response_len - *buflen); +		if (len <= 0) +		{ +			free(mallocedbuf); +			return (0); +		} +		*buflen += len; +	} +	return (mallocedbuf); +} + +char *rfc1035_query_tcp(struct rfc1035_res *res, +	int fd, const char *query, unsigned query_len, +	int *buflen, unsigned s) +{ +char	*p; + +	if ( rfc1035_send_tcp(fd, query, query_len) < 0 || +		(p=rfc1035_recv_tcp(res, fd, buflen, s)) == 0) +		return (0); +	return (p); +} diff --git a/rfc1035/rfc1035udp.c b/rfc1035/rfc1035udp.c new file mode 100644 index 0000000..29dac33 --- /dev/null +++ b/rfc1035/rfc1035udp.c @@ -0,0 +1,167 @@ +/* +** Copyright 1998 - 2011 Double Precision, Inc. +** See COPYING for distribution information. +*/ + +#include	"config.h" +#include	"rfc1035.h" +#include	<stdio.h> +#include	<string.h> +#include	"soxwrap/soxwrap.h" +#include	<sys/types.h> +#include	<arpa/inet.h> +#include	<errno.h> +#include	<stdlib.h> +#if	HAVE_UNISTD_H +#include	<unistd.h> +#endif +#if TIME_WITH_SYS_TIME +#include	<sys/time.h> +#include	<time.h> +#else +#if HAVE_SYS_TIME_H +#include	<sys/time.h> +#else +#include	<time.h> +#endif +#endif + +int rfc1035_open_udp(int *af) +{ +	return (rfc1035_mksocket(SOCK_DGRAM, 0, af)); +} + +int rfc1035_send_udp(int fd, const struct sockaddr *sin, int sin_len, +	const char *query, unsigned query_len) +{ +	if (sox_sendto(fd, (const char *)query, query_len, 0, +		sin, sin_len) == query_len) +		return (0); +	return (-1); +} + +static int dorecv(int fd, char *bufptr, int buflen, int flags, +		struct sockaddr *addr, socklen_t *addrlen) +{ +socklen_t len; + +	do +	{ +		len=sox_recvfrom(fd, bufptr, buflen, flags, addr, addrlen); +	} while (len < 0 && errno == EINTR); +	return (len); +} + +char *rfc1035_recv_udp(int fd, +	const struct sockaddr *addrshouldfrom, int addrshouldfrom_len, +	int *buflen, const char *query) +{ +int	len; + +#if	RFC1035_IPV6 + +struct sockaddr_storage addrfrom; + +#else + +struct sockaddr_in addrfrom; + +#endif + +socklen_t addrfromlen; +char	rfc1035_buf[512]; +char	*bufptr=rfc1035_buf; +char	*mallocedbuf=0; + +	*buflen=sizeof(rfc1035_buf); + +	while ((len=dorecv(fd, bufptr, *buflen, MSG_PEEK, 0, 0)) >= *buflen ) +	{ +		if (len == *buflen)	len += 511; +		++len; + +		if (mallocedbuf)	free(mallocedbuf); +		mallocedbuf=(char *)malloc(len); +		if (!mallocedbuf)	return (0); +		bufptr= mallocedbuf; +		*buflen=len; +	} + +	addrfromlen=sizeof(addrfrom); +	if (len < 0 || (len=dorecv(fd, bufptr, *buflen, 0, +		(struct sockaddr *)&addrfrom, &addrfromlen)) < 0) +	{ +		if (mallocedbuf) +			free(mallocedbuf); +		errno=EIO; +		return (0); +	} + +	*buflen=len; + +	if ( !rfc1035_same_ip( &addrfrom, addrfromlen, +				addrshouldfrom, addrshouldfrom_len)) +	{ +		if (mallocedbuf) +			free(mallocedbuf); + +		errno=EAGAIN; +		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 (mallocedbuf) +			free(mallocedbuf); +		errno=EAGAIN; +		return (0); +	} +	if (!mallocedbuf) +	{ +		if ((mallocedbuf=malloc( *buflen )) == 0) +			return (0); + +		memcpy(mallocedbuf, bufptr, *buflen); +		bufptr=mallocedbuf; +	} +	return (bufptr); +} + +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) +{ +time_t current_time, final_time; +char	*rc; + +	time(¤t_time); + +	if (rfc1035_send_udp(fd, sin, sin_len, query, query_len)) +		return (0); + +	final_time=current_time+w; + +	while (current_time < final_time) +	{ +		if (rfc1035_wait_reply(fd, final_time-current_time)) +			break; + +		rc=rfc1035_recv_udp(fd, sin, sin_len, buflen, query); +		if (rc)	return (rc); + +		if (errno != EAGAIN)	break; + +		time(¤t_time); +	} +	errno=EAGAIN; +	return (0); +} diff --git a/rfc1035/spf.c b/rfc1035/spf.c new file mode 100644 index 0000000..2f0de3c --- /dev/null +++ b/rfc1035/spf.c @@ -0,0 +1,1466 @@ +/* +** Copyright 2004-2011 Double Precision, Inc. +** See COPYING for distribution information. +*/ + +#include	"config.h" +#include	"spf.h" +#include	"rfc1035mxlist.h" +#include	<stdio.h> +#include	<ctype.h> +#include	<stdlib.h> +#include	<string.h> +#include	<errno.h> +#include	<sys/types.h> +#if TIME_WITH_SYS_TIME +#include	<sys/time.h> +#include	<time.h> +#else +#if HAVE_SYS_TIME_H +#include	<sys/time.h> +#else +#include	<time.h> +#endif +#endif + +struct rfc1035_spf_info { +	const char *mailfrom; +	const char *current_domain; +	const char *tcpremoteip; +	const char *tcpremotehost; +	const char *helodomain; +	const char *mydomain; +	char *errmsg_buf; +	size_t errmsg_buf_size; + +	size_t *lookup_cnt; + +	struct rfc1035_res res; +}; + +static void set_err_msg(char *errmsg_buf, +			size_t errmsg_buf_size, +			const char *errmsg) +{ +	size_t l=strlen(errmsg); + +	if (errmsg_buf_size == 0) +		return; + +	--errmsg_buf_size; + +	if (l >= errmsg_buf_size) +		l=errmsg_buf_size; +	memcpy(errmsg_buf, errmsg, l); +	errmsg_buf[l]=0; +} + +static char lookup(struct rfc1035_spf_info *info); + +char rfc1035_spf_lookup(const char *mailfrom, +			const char *tcpremoteip, +			const char *tcpremotehost, +			const char *helodomain, +			const char *mydomain, +			char *errmsg_buf, +			size_t errmsg_buf_size) +{ +	size_t lookup_cnt=0; +	struct rfc1035_spf_info info; +	char result; + +	if (!tcpremoteip) tcpremoteip=""; +	if (!tcpremotehost) tcpremotehost=""; +	if (!helodomain) helodomain=""; +	if (!mydomain) mydomain=""; + +	if (errmsg_buf && errmsg_buf_size) +		*errmsg_buf=0; + +	/* +	** If the <responsible-sender> has no localpart, clients MUST +	** substitute the string "postmaster" for the localpart. +	*/ +	if (strchr(mailfrom, '@') == NULL) +	{ +		char *buf=malloc(sizeof("postmaster@")+strlen(mailfrom)); +		char err_code; + +		if (buf == NULL) +		{ +			set_err_msg(errmsg_buf, errmsg_buf_size, +				    strerror(errno)); +			return SPF_ERROR; +		} + +		err_code=rfc1035_spf_lookup(strcat(strcpy(buf, "postmaster@"), +						   mailfrom), +					    tcpremoteip, +					    tcpremotehost, +					    helodomain, +					    mydomain, +					    errmsg_buf, +					    errmsg_buf_size); +		free(buf); +		return err_code; +	} + +	memset(&info, 0, sizeof(info)); + +	info.mailfrom=mailfrom; + +	/* +	** The <current-domain> is initially drawn from the +	** <responsible-sender>.  Recursive mechanisms such as +	** Include and Redirect replace the initial +	** <current-domain> with another domain.  However, they +	** do not change the value of the <responsible-sender>. +	*/ +	info.current_domain=strrchr(mailfrom, '@')+1; + +	info.tcpremoteip=tcpremoteip; +	info.tcpremotehost=tcpremotehost; +	info.errmsg_buf=errmsg_buf; +	info.errmsg_buf_size=errmsg_buf_size; +	info.helodomain=helodomain; +	info.mydomain=mydomain; +	info.lookup_cnt=&lookup_cnt; + +	rfc1035_init_resolv(&info.res); + +	result=lookup(&info); + +	if (errmsg_buf[0] == 0) +	{ +		static const char errmsg[]="Address %s the Sender Policy Framework"; +		char *p=malloc(sizeof(errmsg)+strlen(mailfrom)+20); + +		if (p) +			sprintf(p, errmsg, result == SPF_PASS +				? "passes":"does not pass"); + +		set_err_msg(errmsg_buf, errmsg_buf_size, +			    p ? p:strerror(errno)); +		if (p) free(p); +	} +	rfc1035_destroy_resolv(&info.res); +	return result; +} + +static int isspf1(struct rfc1035_reply *reply, int n) +{ +	char	txtbuf[256]; +	const char *p; + +	rfc1035_rr_gettxt(reply->allrrs[n], 0, txtbuf); + +	for (p=txtbuf; *p; p++) +		if (!isspace((int)(unsigned char)*p)) +			break; + +	if (strncasecmp(p, "v=spf1", 6) == 0 && +	    (p[6] == 0 || +	     isspace((int)(unsigned char)p[6]))) +		return 1; + +	return 0; +} + +char rfc1035_spf_gettxt_res(const char *current_domain, +			    char *buf, +			    struct rfc1035_res *res) +{ +	struct	rfc1035_reply *reply; +	char	namebuf[RFC1035_MAXNAMESIZE+1]; +	int n, o; + + +	namebuf[0]=0; +	strncat(namebuf, current_domain, RFC1035_MAXNAMESIZE); + +	if (rfc1035_resolve_cname(res, namebuf, +				  RFC1035_TYPE_TXT, RFC1035_CLASS_IN, +				  &reply, 0) < 0 || +	    reply == 0 || +	    (n=rfc1035_replysearch_an(res, reply, namebuf, RFC1035_TYPE_TXT, +				      RFC1035_CLASS_IN, 0)) < 0) +	{ +		switch (reply ? reply->rcode: +			RFC1035_RCODE_SERVFAIL) { +		case RFC1035_RCODE_NOERROR: +			rfc1035_replyfree(reply); +			return SPF_NONE; +		case RFC1035_RCODE_NXDOMAIN: +			rfc1035_replyfree(reply); +			return SPF_UNKNOWN; +		default: +			break; +		} +		if (reply) +			rfc1035_replyfree(reply); +		return SPF_ERROR; +	} + +	while (n >= 0) +	{ +		if (isspf1(reply, n)) +			break; + +		n=rfc1035_replysearch_an(res, reply, namebuf, +					 RFC1035_TYPE_TXT, RFC1035_CLASS_IN, +					 n+1); +	} + +	if (n >= 0) +	{ +		for (o=n; (o=rfc1035_replysearch_an(res, reply, namebuf, +						    RFC1035_TYPE_TXT, +						    RFC1035_CLASS_IN, +						    o+1)) >= 0; ) +		{ + +			/* +			** +			** A domain MUST NOT return multiple records that +			** begin with the version "v=spf1".  If more than +			** one "v=spf1" record is returned, this constitutes +			** a syntax error and the result is "unknown". +			*/ + +			if (isspf1(reply, o)) +			{ +				rfc1035_replyfree(reply); +				return SPF_UNKNOWN; +			} +		} + +		rfc1035_rr_gettxt(reply->allrrs[n], 0, buf); +		rfc1035_replyfree(reply); +		return SPF_PASS; +	} +	rfc1035_replyfree(reply); +	return SPF_UNKNOWN; +} + +char rfc1035_spf_gettxt(const char *current_domain, +			char *buf) +{ +	struct rfc1035_res res; +	char c; + +	rfc1035_init_resolv(&res); + +	c=rfc1035_spf_gettxt_res(current_domain, buf, &res); + +	rfc1035_destroy_resolv(&res); + +	return c; +} + +/* +** Chop up an SPF record into whitespace-delimited words. +** get_words() is called twice: once with wordptr=NULL - return # of words, +** second time with wordptr!=NULL, parse the words. +*/ + +static unsigned get_words(char *record, char **wordptr) +{ +	unsigned n=0; + +	while (*record) +	{ +		if (isspace((int)(unsigned char)*record)) +		{ +			++record; +			continue; +		} + +		if (wordptr) +			*wordptr++=record; +		++n; + +		while (*record) +		{ +			if (isspace((int)(unsigned char)*record)) +				break; +			++record; +		} + +		if (*record && wordptr) +			*record++=0; +	} +	return n; +} + +static char spf_compute(char **words, +			struct rfc1035_spf_info *info); + +char rfc1035_spf_compute(char *record, +			 struct rfc1035_spf_info *info) +{ +	unsigned n=get_words(record, NULL); +	char **words=malloc((n+1)*sizeof(char *)); +	char rc; + +	if (words == NULL) +	{ +		set_err_msg(info->errmsg_buf, info->errmsg_buf_size, +			    strerror(errno)); +		return SPF_ERROR; +	} + +	get_words(record, words); +	words[n]=0; + +	rc=spf_compute(words, info); +	free(words); +	return rc; +} + +static char mechanism(const char *name, +		      struct rfc1035_spf_info *info); + +static void setexp(const char *name, +		   struct rfc1035_spf_info *info); +static char *expand(const char *str, +		    struct rfc1035_spf_info *info); + + +static char spf_compute(char **words, +			struct rfc1035_spf_info *info) +{ +	size_t i; +	char rc; +	const char *exp=NULL; +	const char *redirect=NULL; + +	for (i=0; words[i]; i++) +		if (strncasecmp(words[i], "exp=", 4) == 0) +			exp=words[i]+4; + +	for (i=0; words[i]; i++) +	{ +		const char *name; +		char prefix; + +		if (strncasecmp(words[i], "redirect=", 9) == 0) +			redirect=words[i]+9; + +		if (strchr(words[i], '=')) +			continue; + +		name=words[i]; + +		switch (*name) { +		case '+': +		case '-': +		case '?': +		case '~': +			prefix=*name++; +			break; +		default: +			prefix='+'; +			break; +		} + +		rc=mechanism(name, info); + +		/* +		** When a mechanism is evaluated, one of three things can +		** happen: it can match, it can not match, or it can throw an +		** exception. +		*/ + +		if (rc == SPF_PASS) +		{ +			/* +			** If it matches, processing ends and the prefix value +			** is returned as the result of that record. +			*/ + +			if (prefix != SPF_PASS && exp) +				setexp(exp, info); +			return prefix; +		} + +		if (rc == SPF_FAIL) +		{ +			/* +			** If it does not match, processing continues with +			** the next +			** mechanism. +			*/ + +			continue; +		} + +		/* +		** If it throws an exception, mechanism processing ends and +		** the exception value is returned (either "error" +		** indicating a temporary failure, usually DNS-related, or +		** "unknown" indicating a syntax error or other permanent +		** failure resulting in incomplete processing.) +		*/ +		return rc; +	} + +	if (redirect) +	{ +		/* +		** If all mechanisms fail to match, and a redirect modifier +		** is present, then processing proceeds as follows. +		** +		** The domain-spec portion of the redirect section is expanded +		** as per the macro rules in section 7.  The resulting string +		** is a new domain that is now queried:  The <current-domain> +		** is set to this new domain, and the new domain's SPF record +		** is fetched and processed.  Note that <responsible-sender> +		** does not change. +		** +		** The result of this new query is then considered the result +		** of original query. +		*/ + + +		char *new_domain; +		struct rfc1035_spf_info newinfo; +		char rc; + +		new_domain=expand(redirect, info); + +		if (!new_domain) +			return SPF_ERROR; + +		newinfo= *info; +		newinfo.current_domain=new_domain; + +		rc=lookup(&newinfo); +		free(new_domain); +		return rc; +	} + +	/* +	** If none of the mechanisms match and there is no redirect modifier, +	** then the result of the SPF query is "neutral". +	*/ + +	if (exp) +		setexp(exp, info); + +	return SPF_NEUTRAL; +} + +static int get_dual_cidr_length(const char *p) +{ +#if RFC1035_IPV6 +	const char *q; + +	for (q=p; *q; q++) +		if (*q == '/' && q[1] == '/') +			return atoi(q+2); + +	return atoi(p+1)+12*8; +#else +	if (p[1] == '/') +		return -1; +	return atoi(p+1); +#endif +} + +static int ip_compare(const RFC1035_ADDR *a, +		      const RFC1035_ADDR *b, +		      int pfix) +{ +	const unsigned char *ca, *cb; +	unsigned i; + +	if (pfix < 0) +		return 0; + +	ca=(const unsigned char *)a; +	cb=(const unsigned char *)b; + +	for (i=0; i<sizeof(RFC1035_ADDR); i++) +	{ +		int bits=pfix>8?8:pfix; +		unsigned char m=(unsigned char )(~0 << (8-bits)); + +		if ((ca[i] & m) != (cb[i] & m)) +			return 0; + +		pfix -= bits; +	} +	return 1; +} + +static void get_domain_pfix(struct rfc1035_spf_info *info, +			    const char *start, +			    char **domain_ptr, +			    int  *pfix_ptr) +{ +	*pfix_ptr=sizeof(RFC1035_ADDR)*8; + +	if (*start == 0 || *start == '/') +	{ +		*domain_ptr=strdup(strrchr(info->mailfrom, '@')+1); + +		if (*start == '/') +			*pfix_ptr=get_dual_cidr_length(start); +	} +	else +	{ +		char *p; + +		*domain_ptr=strdup(*start == ':' ? start+1:start); + +		p=strchr(*domain_ptr, '/'); +		if (p) +		{ +			*pfix_ptr=get_dual_cidr_length(p); +			*p++=0; +		} + +		if (*domain_ptr == 0) +		{ +			free(*domain_ptr); +			*domain_ptr=strdup(strrchr(info->mailfrom, +						   '@')+1); +		} +	} + +	if (!*domain_ptr) +		set_err_msg(info->errmsg_buf, info->errmsg_buf_size, +			    strerror(errno)); +} + +struct ptr_info { +	const char *name; +	struct rfc1035_spf_info *info; +	RFC1035_ADDR addr; +	int found; +	int error; +}; + +static void check_ptr(const char *ptr, void *void_arg) +{ +	struct ptr_info *pinfo=(struct ptr_info *)void_arg; +	RFC1035_ADDR *addr; +	unsigned addr_cnt; +	int rc; +	unsigned i; + +	if (pinfo->found) +		return; /* No need */ + +	rc=rfc1035_a(&pinfo->info->res, ptr, &addr, &addr_cnt); + +	if (rc > 0) +		pinfo->error=1; +	if (rc) +		return; + +	for (i=0; i<addr_cnt; i++) +	{ +		if (memcmp(&addr[i], &pinfo->addr, +			   sizeof(pinfo->addr)) == 0) +			break; +	} + +	if (i < addr_cnt) +	{ +		size_t l1, l2; + +		if (strcasecmp(ptr, pinfo->name) == 0) +			pinfo->found=1; + +		l1=strlen(ptr); +		l2=strlen(pinfo->name); + +		if (l2 < l1) +		{ +			ptr=ptr+l1-l2; + +			if (ptr[-1] == '.' && strcasecmp(ptr, pinfo->name)==0) +				pinfo->found=1; +		} +	} +	free(addr); +} + +static char do_ptr(const char *name, +		   struct rfc1035_spf_info *info) +{ +	struct ptr_info pinfo; + +	if (*name++ == ':') +		pinfo.name=name; +	else +		pinfo.name=strrchr(info->mailfrom, '@')+1; + +	pinfo.info=info; +	pinfo.found=0; +	pinfo.error=0; + +	/* +	** First the <sending-host>'s name is looked up using this +	** procedure: +	** +	** perform a PTR lookup against the <sending-host>'s IP.  For +	** each record returned, validate the host name by looking up +	** its IP address.  If the <sending-host>'s IP is among the +	** returned IP addresses, then that host name is validated. +	** +	** Check all validated hostnames to see if they end in the +	** <target-name> domain.  If any do, this mechanism matches. +	** If no validated hostname can be found, or if none of the +	** validated hostnames end in the <target-name>, this +	** mechanism fails to match. +	*/ + +	if (rfc1035_aton(info->tcpremoteip, &pinfo.addr) < 0) +	{ +		set_err_msg(info->errmsg_buf, info->errmsg_buf_size, +			    "Invalid tcpremoteip.\n"); +		return SPF_FAIL; +	} + +	if (rfc1035_ptr_x(&info->res, &pinfo.addr, +			  check_ptr, &pinfo) < 0) +	{ +		if (errno == ENOENT) +			return SPF_FAIL; + +		return SPF_ERROR; +	} + +	if (pinfo.found) +		return SPF_PASS; +	if (pinfo.error) +	{ +		set_err_msg(info->errmsg_buf, info->errmsg_buf_size, +			    "ptr lookup failed.\n"); +		return SPF_UNKNOWN; +	} +	return SPF_FAIL; +} + +static char do_ipcheck(const char *name, struct rfc1035_spf_info *info, +		       int pfix_add) +{ +	char *addrptr; +	char *p; +	int pfix; +	RFC1035_ADDR addr, addrcmp; + +	/* +	** These mechanisms test if the <sending-host> falls into a +	** given IP network. +	** +	** The <sending-host> is compared to the given network.  If +	** they match, the mechanism matches. +	*/ + +	if (rfc1035_aton(info->tcpremoteip, &addr) < 0) +	{ +		set_err_msg(info->errmsg_buf, info->errmsg_buf_size, +			    "Invalid tcpremoteip.\n"); +		return SPF_FAIL; +	} + +	if ((addrptr=strdup(name+4)) == NULL) +	{ +		set_err_msg(info->errmsg_buf, info->errmsg_buf_size, +			    strerror(errno)); +		return SPF_ERROR; +	} + +	p=strrchr(addrptr, '/'); +	pfix=sizeof(RFC1035_ADDR)*8; + +	if (p) +	{ +		*p++=0; +		pfix=atol(p)+pfix_add; +	} + +	if (rfc1035_aton(addrptr, &addrcmp) < 0) +	{ +		free(addrptr); +		return SPF_FAIL; +	} +	free(addrptr); +	if (ip_compare(&addr, &addrcmp, pfix)) +		return SPF_PASS; +	return SPF_FAIL; +} + +static char mechanism(const char *name, +		      struct rfc1035_spf_info *info) +{ +	if (strcasecmp(name, "all") == 0) +	{ +		/* +		** The "all" mechanism is a test that always matches.  It is +		** used as the rightmost mechanism in an SPF record to +		** provide an explicit default. +		*/ +		return SPF_PASS; +	} + +	if (strncasecmp(name, "include:", 8) == 0) +	{ +		char *new_domain; +		struct rfc1035_spf_info newinfo; +		char rc; + +		/* +		** The "include" mechanism triggers a recursive SPF query.  The +		** domain-spec is expanded as per section 7.  Then a new query +		** is launched using the resulting string as the +		** <current-domain>.  The <responsible-sender> stays the same. +		*/ + +		new_domain=expand(name+8, info); + +		if (!new_domain) +			return SPF_ERROR; + +		newinfo= *info; +		newinfo.current_domain=new_domain; + +		/* +		**      included    include +		**      query       mechanism      SPF +		**      result      result         processing +		**      -------- -- -------------- ------------------------------------- +		**      pass     => match,         return the prefix value for "include" +		**      fail     => no match,      continue processing +		**      softfail => no match,      continue processing +		**      neutral  => no match,      continue processing +		**      error    => throw error,   abort processing, return error +		**      unknown  => throw unknown, abort processing, return unknown +		**      none     => throw unknown, abort processing, return unknown +		*/ + +		rc=lookup(&newinfo); +		free(new_domain); + +		switch (rc) { +		case SPF_PASS: +			return SPF_PASS; +		case SPF_FAIL: +		case SPF_SOFTFAIL: +		case SPF_NEUTRAL: +			return SPF_FAIL; +		case SPF_ERROR: +			return SPF_ERROR; +		default: +			return SPF_UNKNOWN; +		} +	} + +	if (strncasecmp(name, "a", 1) == 0 && +	    (name[1] == 0 || name[1] == ':' || name[1] == '/')) +	{ +		char *domain_spec; +		int pfix; +		RFC1035_ADDR addr; + +		RFC1035_ADDR *iaptr; +		unsigned iasize; +		int rc; +		unsigned ii; + +		/* +		** This mechanism matches if the <sending-host> is one of the +		** <target-name>'s IP addresses. +		** +		** A = "a" [ ":" domain-spec ] [ dual-cidr-length ] +		** +		** The <sending-host> is compared to the IP address(es) of the +		** <target-name>.  If any address matches, the mechanism +		** matches. +		*/ + +		get_domain_pfix(info, name+1, &domain_spec, &pfix); + +		if (!domain_spec) +			return SPF_ERROR; + +		if (rfc1035_aton(info->tcpremoteip, &addr) < 0) +		{ +			free(domain_spec); +			set_err_msg(info->errmsg_buf, info->errmsg_buf_size, +				    "Invalid tcpremoteip.\n"); +			return SPF_FAIL; +		} + +		rc=rfc1035_a(&info->res, +			     domain_spec, +			     &iaptr, +			     &iasize); + +		free(domain_spec); + +		if (rc != 0) +		{ +			set_err_msg(info->errmsg_buf, info->errmsg_buf_size, +				    "IP address lookup failed.\n"); +			return SPF_UNKNOWN; +		} + +		for (ii=0; ii<iasize; ii++) +			if (ip_compare(&addr, iaptr+ii, pfix)) +			{ +				free(iaptr); +				return SPF_PASS; +			} + +		free(iaptr); +		return SPF_FAIL; +	} + + +	if (strncasecmp(name, "mx", 2) == 0 && +	    (name[2] == 0 || name[2] == ':' || name[2] == '/')) +	{ +		char *domain_spec; +		int pfix; +		int rc; +		struct rfc1035_mxlist *mxlist, *mxp; +		RFC1035_ADDR addr; + +		/* +		** This mechanism matches if the <sending-host> is one of the +		** MX hosts for a domain name. +    +		** MX = "mx" [ ":" domain-spec ] [ dual-cidr-length ] +     +		** SPF clients first perform an MX lookup on the <target-name>. +		** SPF clients then perform an A lookup on each MX name +		** returned, in order of MX priority.  The <sending-host> is +		** compared to each returned IP address.  If any address +		** matches, the mechanism matches. +		*/ + +		get_domain_pfix(info, name+2, &domain_spec, &pfix); + +		if (!domain_spec) +			return SPF_ERROR; + +		if (rfc1035_aton(info->tcpremoteip, &addr) < 0) +		{ +			free(domain_spec); +			set_err_msg(info->errmsg_buf, info->errmsg_buf_size, +				    "Invalid tcpremoteip.\n"); +			return SPF_FAIL; +		} + +		rc=rfc1035_mxlist_create_x(&info->res, +					   domain_spec, 0, +					   &mxlist); +		free(domain_spec); +		if (rc) +		{ +			rfc1035_mxlist_free(mxlist); +			set_err_msg(info->errmsg_buf, info->errmsg_buf_size, +				    "DNS MX lookup failed.\n"); +			return SPF_ERROR; +		} + +		for (mxp=mxlist; mxp; mxp=mxp->next) +		{ +			RFC1035_ADDR addrcmp; + +			if (rfc1035_sockaddrip(&mxp->address, +					       sizeof(mxp->address), +					       &addrcmp) < 0) +				continue; + +			if (ip_compare(&addr, &addrcmp, pfix)) +			{ +				rfc1035_mxlist_free(mxlist); +				return SPF_PASS; +			} +		} +		rfc1035_mxlist_free(mxlist); +		return SPF_FAIL; +	} + +	if (strncasecmp(name, "ip4:", 4) == 0) +	{ +		if (strchr(name+4, ':')) +			return SPF_FAIL; /* What does IPv6 addr doing here? */ + +#if RFC1035_IPV6 +		return do_ipcheck(name, info, 12*8); +#else +		return do_ipcheck(name, info, 0); +#endif +	} + +	if (strncasecmp(name, "ip6:", 4) == 0) +	{ +#if RFC1035_IPV6 +		return do_ipcheck(name, info, 0); +#else +		return SPF_FAIL; +#endif +	} + +	if (strncasecmp(name, "ptr", 3) == 0 && +	    (name[3] == 0 || name[3] == ':')) +	{ +		return do_ptr(name+3, info); +	} + +	if (strncasecmp(name, "exists:", 7) == 0) +	{ +		char *domain_spec; +		RFC1035_ADDR *iaptr; +		unsigned iasize; +		int rc; + +		/* +		** This mechanism is used to construct an arbitrary host name +		** that is used for a DNS A record query.  It allows for +		** complicated schemes involving arbitrary parts of the mail +		** envelope to determine what is legal. +		** +		** exists = "exists" ":" domain-spec +		** +		** The domain-spec is expanded as per Section 7.  The +		** resulting domain name is used for a DNS A lookup.  If any +		** A record is returned, this mechanism matches.  The lookup +		** type is 'A' even when the connection type is IPv6. +		*/ + +		domain_spec=expand(name+7, info); +		if (!domain_spec) +			return SPF_ERROR; + +		rc=rfc1035_a(&info->res, +			     domain_spec, +			     &iaptr, +			     &iasize); +		free(domain_spec); + +		if (rc < 0) +			return SPF_FAIL; +		if (rc > 0) +			return SPF_ERROR; +		free(iaptr); +		return SPF_PASS; +	} + +	return SPF_FAIL; +} + +static void setexp(const char *exp, +		   struct rfc1035_spf_info *info) +{ +	struct	rfc1035_reply *reply; +	char	namebuf[RFC1035_MAXNAMESIZE+1]; +	char	txtbuf[256]; +	int n; +	char	*str; + +	/* +	** The argument to the explanation modifier is a domain-spec +	** to be TXT queried.  The result of the TXT query is a +	** macro-string that is macro-expanded.  If SPF processing +	** results in a rejection, the expanded result SHOULD be +	** shown to the sender in the SMTP reject message.  This +	** string allows the publishing domain to communicate further +	** information via the SMTP receiver to legitimate senders in +	** the form of a short message or URL. +	*/ + + +	namebuf[0]=0; +	strncat(namebuf, exp, RFC1035_MAXNAMESIZE); + +	if (rfc1035_resolve_cname(&info->res, namebuf, +				  RFC1035_TYPE_TXT, RFC1035_CLASS_IN, +				  &reply, 0) < 0 || +	    reply == 0 || +	    (n=rfc1035_replysearch_an(&info->res, +				      reply, namebuf, RFC1035_TYPE_TXT, +				      RFC1035_CLASS_IN, 0)) < 0) +	{ +		set_err_msg(info->errmsg_buf, +			    info->errmsg_buf_size, +			    "A DNS lookup error occured while" +			    " fetching the SPF explanation record."); +	} +	else +	{ +		rfc1035_rr_gettxt(reply->allrrs[n], 0, txtbuf); + +		str=expand(txtbuf, info); + +		set_err_msg(info->errmsg_buf, +			    info->errmsg_buf_size, +			    str ? str:strerror(errno)); +		if (str) +			free(str); +	} +	rfc1035_replyfree(reply); +} + +static char lookup(struct rfc1035_spf_info *info) +{ +	char record[256]; +	char c; + +	/* +	**  +	** If a loop is detected, or if more than 20 subqueries are triggered, +	** an SPF client MAY abort the lookup and return the result "unknown". +	*/ + +	if (++*info->lookup_cnt > 20) +	{ +		set_err_msg(info->errmsg_buf, info->errmsg_buf_size, +			    "Maximum of 20 nested SPF queries exceeded."); +		return SPF_UNKNOWN; +	} + +	c=rfc1035_spf_gettxt(info->current_domain, record); + +	if (c != SPF_PASS) +		return c; + +	return rfc1035_spf_compute(record, info); +} + +/* +** +** Certain directives perform macro interpolation on their arguments. +** +** Two passes: count # of chars in the expanded macro, generate the macro. +*/ + +static int do_expand(const char *str, struct rfc1035_spf_info *info, +		     void (*cb_func)(const char *, size_t n, void *), +		     void *void_arg); + +static void do_count(const char *p, size_t n, void *va) +{ +	*(size_t *)va += n; +} + +static void do_save(const char *p, size_t n, void *va) +{ +	char **b=(char **)va; + +	memcpy(*b, p, n); +	*b += n; +} + +static char *expand(const char *str, +		    struct rfc1035_spf_info *info) +{ +	size_t cnt=1; +	char *buf; +	char *p; + +	if (do_expand(str, info, do_count, &cnt) < 0) +		return NULL; + +	buf=malloc(cnt); + +	if (!buf) +	{ +		set_err_msg(info->errmsg_buf, info->errmsg_buf_size, +			    strerror(errno)); +		return NULL; +	} + +	p=buf; +	if (do_expand(str, info, do_save, &p) < 0) +	{ +		free(buf); +		return NULL; +	} + +	*p=0; +	return buf; +} + +static char *get_macro(struct rfc1035_spf_info *info, char name); + +static char *transform(char *macro, +		       unsigned transformer_count, +		       char transformer_reverse, +		       char delimiter_char); + +static int do_expand(const char *str, struct rfc1035_spf_info *info, +		      void (*cb_func)(const char *, size_t, void *), +		      void *void_arg) +{ +	unsigned char alpha, lalpha; +	unsigned transformer_count; +	char transformer_reverse; +	char delimiter_char; +	char *macro; + +	/* +	**     macro-string = *( macro-char / VCHAR ) +	**     macro-char   = ( "%{" ALPHA transformer *delimiter "}" ) +	**                    / "%%" / "%_" / "%-" +	**     transformer  = [ *DIGIT ] [ "r" ] +	**     delimiter    = "." / "-" / "+" / "," / "/" / "_" / "=" +	** +	*/ + +	while (*str) +	{ +		size_t i; + +		for (i=0; str[i]; i++) +			if (str[i] == '%') +				break; + +		if (i) +		{ +			(*cb_func)(str, i, void_arg); +			str += i; +			continue; +		} + +		/* +		**   A literal "%" is expressed by "%%". +		**   %_ expands to a single " " space. +		**   %- expands to a URL-encoded space, viz. "%20". +		*/ + +		switch (str[i+1]) { +		case '{': +			break; +		case '%': +			(*cb_func)("%", 1, void_arg); +			str += 2; +			continue; +		case '_': +			(*cb_func)(" ", 1, void_arg); +			str += 2; +			continue; +		case '-': +			(*cb_func)("%20", 3, void_arg); +			str += 2; +			continue; +		default: +			++str; +			continue; +		} + +		str += 2; + +		if (!*str) +			continue; +		alpha=(unsigned char)*str++; +		transformer_count=0; +		while (*str && isdigit((unsigned char)*str)) +		{ +			transformer_count=transformer_count * 10 + +				(*str++ - '0'); +		} + +		transformer_reverse=0; +		delimiter_char=0; + +		while (*str && *str != '}') +		{ +			switch (*str) { +			case 'r': +			case 'R': +				transformer_reverse='r'; +				break; +			case '.': +			case '-': +			case '+': +			case ',': +			case '/': +			case '_': +			case '=': +				delimiter_char= *str; +				break; +			} +			++str; +		} +		lalpha=tolower(alpha); + +		macro=get_macro(info, lalpha); +		if (macro && (transformer_reverse || transformer_count)) +		{ +			char *new_macro=transform(macro, transformer_count, +						  transformer_reverse, +						  delimiter_char); + +			free(macro); +			macro=new_macro; +		} + +		if (macro && lalpha != alpha) +		{ +			static const char validchars[]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; +			size_t l=1; +			size_t i,j; +			char *p; + +			for (i=0; macro[i]; i++) +			{ +				++l; +				if (strchr(validchars, macro[i]) == NULL) +					l += 2; +			} + +			p=malloc(l); +			for (i=j=0; p && macro[i]; i++) +			{ +				if (strchr(validchars, macro[i])) +				{ +					p[j]=macro[i]; +					++j; +				} +				else +				{ +					sprintf(p+j, "%%%02X", +						(int)(unsigned char)macro[i]); +					j += 3; +				} +			} +			if (p) +				p[j]=0; +			free(macro); +			macro=p; +		} + +		if (macro == NULL) +		{ +			set_err_msg(info->errmsg_buf, +				    info->errmsg_buf_size, +				    strerror(errno)); +			return -1; +		} + +		(*cb_func)(macro, strlen(macro), void_arg); +		free(macro); +		if (*str == '}') +			++str; +	} +	return 0; +} + +static char *expandc(const char *ipaddr); +static char *expandi(const char *ipaddr); + +static char *get_macro(struct rfc1035_spf_info *info, char name) +{ +	char *p; +	const char *cp; + +	switch (name) { +	case 'l': +		/* l = local-part of responsible-sender */ + +		cp=strrchr(info->mailfrom, '@'); +		p=malloc(cp-info->mailfrom+1); +		if (!p) +			return p; +		memcpy(p, info->mailfrom, cp-info->mailfrom); +		p[cp-info->mailfrom]=0; +		return p; +	case 's': +		/* s = responsible-sender */ +		return strdup(info->mailfrom); +	case 'o': +		return strdup(strrchr(info->mailfrom, '@')+1); +		/* o = responsible-domain */ +	case 'd': +		return strdup(info->current_domain); +		/* d = current-domain */ +	case 'c': +		return expandc(info->tcpremoteip); +		/* c = SMTP client IP (easily readable format) */ +	case 'i': +		return expandi(info->tcpremoteip); +		/* i = SMTP client IP (nibble format when an IPv6 address) */ +	case 'p': +		return strdup(info->tcpremotehost); +		/* p = SMTP client domain name */ +	case 'v': +		return (strdup(strchr(info->tcpremoteip, ':') && +			       strncmp(info->tcpremoteip, "::ffff:", 7) +			       ? "ip6":"in-addr")); +		/* v = client IP version string: "in-addr" for ipv4 or "ip6" for ipv6 */ +	case 'h': +	/* h = HELO/EHLO domain */ +		return strdup(info->helodomain); +	case 'r': +	/* r = receiving domain */ +		return strdup(info->mydomain); +	} + +	return strdup(""); +} + +/* +** +**   For IPv4 addresses, both the "i" and "c" macros expand to the +**   standard dotted-quad format. +** +**   For IPv6 addresses, the "i" macro expands to dot-format address; it +**   is intended for use in %{ir}.  The "c" macro may expand to any of +**   the hexadecimal colon-format addresses specified in [RFC3513] section +**   2.2.  It is intended for humans to read. +*/ + +static char *expandc(const char *ipaddr) +{ +	if (strncmp(ipaddr, "::ffff:", 7) == 0) +		return strdup(ipaddr+7); +	return strdup(ipaddr); +} + +static char *expandi(const char *ipaddr) +{ +	if (strchr(ipaddr, ':') && +	    strncmp(ipaddr, "::ffff:", 7)) +	{ +		RFC1035_ADDR addr; + +		if (rfc1035_aton(ipaddr, &addr) == 0) +		{ +			char name[sizeof(addr)*4+1]; +			char *p=name; +			int i; +			unsigned char *q=(unsigned char *)&addr; + +			for (i=0; i<sizeof(addr); i++) +			{ +				sprintf(p, "%s%x.%x", i ? ".":"", +					(int)((q[i] >> 4) & 0x0F), +					(int)(q[i] & 0x0F)); +				p += strlen(p); +			} +			return strdup(name); +		} + +	} +	return expandc(ipaddr); +} + +/* +**   If transformers or delimiters are provided, the macro strings are +**   split into parts.  After performing any reversal operation or +**   removal of left-hand parts, the parts are rejoined using "." and not +**   the original splitting characters. +*/ + +static unsigned tsplit(char *macro, char delimiter, char **wordptr) +{ +	/* Two passes */ +	unsigned cnt=0; + +	if (!delimiter) +		delimiter='.'; + +	while (*macro) +	{ +		++cnt; + +		if (wordptr) +			*wordptr++=macro; + +		while (*macro && *macro != delimiter) +			++macro; + +		if (*macro) +		{ +			if (wordptr) +				*macro=0; +			++macro; +		} + +	} +	return cnt; +}		 + +static char *transform(char *macro, +		       unsigned transformer_count, +		       char transformer_reverse, +		       char delimiter_char) +{ +	char **words; +	unsigned n=tsplit(macro, delimiter_char, NULL); +	unsigned start; +	unsigned i; +	char *buf; +	size_t len; + +	if ((words=malloc(sizeof(char *)*(n+1))) == NULL) +		return NULL; +	tsplit(macro, delimiter_char, words); +	words[n]=NULL; + +	/* +	** The DIGIT transformer indicates the number of right-hand parts to +	** use after optional reversal.  If a DIGIT is specified, it MUST be +	** nonzero.  If no DIGITs are specified, or if the value specifies more +	** parts than are available, all the available parts are used.  If the +	** DIGIT was 5, and only 3 parts were available, the macro interpreter +	** would pretend the DIGIT was 3.  Implementations MAY limit the +	** number, but MUST support at least a value of 9. +	*/ + +	if (transformer_count > n || transformer_count <= 0) +		transformer_count=n; + +	if (transformer_reverse) +	{ +		start=0; +		n=transformer_count; +	} +	else +	{ +		start=n-transformer_count; +	} + +	len=1; + +	for (i=start; i<n; i++) +	{ +		len += strlen(words[i])+1; +	} + +	buf=malloc(len); +	if (!buf) +	{ +		free(words); +		return NULL; +	} + +	*buf=0; +	if (transformer_reverse) +	{ +		for (i=n; i>start; ) +		{ +			if (*buf) +				strcat(buf, "."); +			strcat(buf, words[--i]); +		} +	} +	else +	{ +		for (i=start; i<n; i++) +		{ +			if (*buf) +				strcat(buf, "."); +			strcat(buf, words[i]); +		} +	} +	free(words); +	return buf; +} diff --git a/rfc1035/spf.h b/rfc1035/spf.h new file mode 100644 index 0000000..c4afea8 --- /dev/null +++ b/rfc1035/spf.h @@ -0,0 +1,73 @@ +/* +** Copyright 2004 Double Precision, Inc. +** See COPYING for distribution information. +*/ + +#ifndef	rfc1035_spf_h +#define	rfc1035_spf_h + + +#include "rfc1035/rfc1035.h" + +#ifdef  __cplusplus +extern "C" { +#endif + +/* +   An SPF client evaluates an SPF record and produces one of seven +   results: + +     None: The domain does not publish SPF data. + +     Neutral (?): The SPF client MUST proceed as if a domain did not +     publish SPF data.  This result occurs if the domain explicitly +     specifies a "?" value, or if processing "falls off the end" of +     the SPF record. + +     Pass (+): the message meets the publishing domain's definition of +     legitimacy.  MTAs proceed to apply local policy and MAY accept or +     reject the message accordingly. + +     Fail (-): the message does not meet a domain's definition of +     legitimacy.  MTAs MAY reject the message using a permanent +     failure reply code.  (Code 550 is RECOMMENDED.  See [RFC2821] +     section 7.1.) + +     Softfail (~): the message does not meet a domain's strict +     definition of legitimacy, but the domain cannot confidently state +     that the message is a forgery.  MTAs SHOULD accept the message +     but MAY subject it to a higher transaction cost, deeper scrutiny, +     or an unfavourable score. + +   There are two error conditions, one temporary and one permanent. + +     Error: indicates an error during lookup; an MTA SHOULD reject the +     message using a transient failure code, such as 450. + +     Unknown: indicates incomplete processing: an MTA MUST proceed as +     if a domain did not publish SPF data. +*/ + +#define SPF_NONE 0 +#define SPF_NEUTRAL '?' +#define SPF_PASS '+' +#define SPF_FAIL '-' +#define SPF_SOFTFAIL '~' +#define SPF_ERROR '4' + +	/* Everything else is SPF_UNKNOWN */ +#define SPF_UNKNOWN '5' + +char rfc1035_spf_lookup(const char *mailfrom, +			const char *tcpremoteip, +			const char *tcpremotehost, +			const char *helodomain, +			const char *mydomain, +			char *errmsg_buf, +			size_t errmsg_buf_size); + +#ifdef  __cplusplus +} +#endif + +#endif diff --git a/rfc1035/testlookup.c b/rfc1035/testlookup.c new file mode 100644 index 0000000..6ee7c61 --- /dev/null +++ b/rfc1035/testlookup.c @@ -0,0 +1,266 @@ +/* +** Copyright 1998 - 2011 Double Precision, Inc. +** See COPYING for distribution information. +*/ + +#include	"config.h" +#include	"rfc1035.h" +#include	"spf.h" +#include	<sys/types.h> +#include	<stdio.h> +#include	<stdlib.h> +#include	<string.h> +#include	<errno.h> +#include	<arpa/inet.h> + +#include	"soxwrap/soxwrap.h" + + +static void setns(const char *p, struct rfc1035_res *res) +{ +RFC1035_ADDR ia[4]; +int	i=0; +char	*q=malloc(strlen(p)+1), *r; + +	strcpy(q, p); +	for (r=q; (r=strtok(r, ", ")) != 0; r=0) +		if (i < 4) +		{ +			if (rfc1035_aton(r, &ia[i]) == 0) +			{ +				++i; +			} +			else +			{ +				fprintf(stderr, "%s: invalid IP address\n", +					r); +			} +		} +	rfc1035_init_ns(res, ia, i); +} + +extern char rfc1035_spf_gettxt(const char *current_domain, +			       char *buf); + +static void spflookup(const char *current_domain) +{ +	char buf[256]; + +	switch (rfc1035_spf_gettxt(current_domain, buf)) { +	case SPF_NONE: +		printf("none\n"); +		return; +	case SPF_NEUTRAL: +		printf("neutral\n"); +		return; +	case SPF_PASS: +		printf("pass: %s\n", buf); +		return; +	case SPF_FAIL: +		printf("fail\n"); +		return; +	case SPF_SOFTFAIL: +		printf("softfail\n"); +		return; +	case SPF_ERROR: +		printf("error\n"); +		return; +	default: +		printf("unknown\n"); +	} +} + +int main(int argc, char **argv) +{ +struct  rfc1035_res res; +struct	rfc1035_reply *replyp; +int	argn; +const char *q_name; +int	q_type; +int	q_class; +int	q_xflag=0; +int	q_rflag=0; +char	ptrbuf[RFC1035_MAXNAMESIZE+1]; + +	rfc1035_init_resolv(&res); + +	argn=1; +	while (argn < argc) +	{ +		if (argv[argn][0] == '@') +		{ +			setns(argv[argn]+1, &res); +			++argn; +			continue; +		} + +		if (strcmp(argv[argn], "-x") == 0) +		{ +			q_xflag=1; +			++argn; +			continue; +		} +		if (strcmp(argv[argn], "-r") == 0) +		{ +			q_rflag=1; +			++argn; +			continue; +		} + +		if (strcmp(argv[argn], "-dnssec") == 0) +		{ +			rfc1035_init_dnssec_enable(&res, 1); +			++argn; +			continue; +		} + +		if (strcmp(argv[argn], "-udpsize") == 0) +		{ +			++argn; + +			if (argn < argc) +			{ +				rfc1035_init_edns_payload(&res, +							  atoi(argv[argn])); +				++argn; +			} +			continue; +		} + +		break; +	} + +	if (argn >= argc)	exit(0); + +	q_name=argv[argn++]; + +	if (q_xflag) +	{ +	struct in_addr ia; +#if	RFC1035_IPV6 +	struct in6_addr ia6; + +		if (inet_pton(AF_INET6, q_name, &ia6) > 0) +		{ +		const char *sin6=(const char *)&ia6; +		unsigned i; + +			ptrbuf[0]=0; + +			for (i=sizeof(struct in6_addr); i; ) +			{ +			char    buf[10]; + +				--i; +				sprintf(buf, "%x.%x.", +					(int)(unsigned char)(sin6[i] & 0x0F), +					(int)(unsigned char)((sin6[i] >> 4) +							& 0x0F)); +				strcat(ptrbuf, buf); +			} +			strcat(ptrbuf, "ip6.arpa"); +			q_name=ptrbuf; +		} +		else +#endif +		if ( rfc1035_aton_ipv4(q_name, &ia) == 0) +		{ +		char buf[RFC1035_MAXNAMESIZE]; +		unsigned char a=0,b=0,c=0,d=0; +		const char *p=buf; + +			rfc1035_ntoa_ipv4(&ia, buf); + +			while (*p >= '0' && *p <= '9') +				a= (int)a * 10 + (*p++ - '0'); +			if (*p)	p++; +			while (*p >= '0' && *p <= '9') +				b= (int)b * 10 + (*p++ - '0'); +			if (*p)	p++; +			while (*p >= '0' && *p <= '9') +				c= (int)c * 10 + (*p++ - '0'); +			if (*p)	p++; +			while (*p >= '0' && *p <= '9') +				d= (int)d * 10 + (*p++ - '0'); + +			sprintf(ptrbuf, "%d.%d.%d.%d.in-addr.arpa", +				(int)d, (int)c, (int)b, (int)a); +			q_name=ptrbuf; +		} +	} + +	if (q_rflag) +	{ +	RFC1035_ADDR a; +	int	rc; + +		if (rfc1035_aton(q_name, &a) == 0) +		{ +			rc=rfc1035_ptr(&res, &a,ptrbuf); +			if (rc == 0) +			{ +				printf("%s\n", ptrbuf); +				exit(0); +			} +		} +		else +		{ +		RFC1035_ADDR	*aptr; +		unsigned alen; + +			rc=rfc1035_a(&res, q_name, &aptr, &alen); +			if (rc == 0) +			{ +			unsigned i; + +				for (i=0; i<alen; i++) +				{ +					rfc1035_ntoa(&aptr[i], ptrbuf); +					printf("%s\n", ptrbuf); +				} +				exit(0); +			} +		} +		fprintf(stderr, "%s error.\n", errno == ENOENT ? "Hard":"Soft"); +		exit(1); +	} +	 +	q_type= -1; + +	if (argn < argc) +	{ +		if (strcmp(argv[argn], "spf") == 0) +			q_type= -2; +		else +			q_type=rfc1035_type_strtoi(argv[argn++]); +	} + +	if (q_type == -1) +		q_type=q_xflag ? RFC1035_TYPE_PTR:RFC1035_TYPE_ANY; + +	q_class= -1; +	if (argn < argc) +		q_class=rfc1035_class_strtoi(argv[argn]); +	if (q_class < 0) +		q_class=RFC1035_CLASS_IN; + +	if (q_type == -2) +	{ +		spflookup(q_name); +		exit(0); +	} + +	replyp=rfc1035_resolve(&res, RFC1035_OPCODE_QUERY, +			       q_name, q_type, q_class); + +	if (!replyp) +	{ +		perror(argv[0]); +		exit(1); +	} + +	rfc1035_dump(replyp, stdout); +	rfc1035_replyfree(replyp); +	rfc1035_destroy_resolv(&res); +	return (0); +} diff --git a/rfc1035/testspf.c b/rfc1035/testspf.c new file mode 100644 index 0000000..a28482f --- /dev/null +++ b/rfc1035/testspf.c @@ -0,0 +1,120 @@ +/* +** Copyright 2004 Double Precision, Inc. +** See COPYING for distribution information. +*/ + +#include	"config.h" +#include	"rfc1035.h" +#include	"spf.h" +#include	<stdio.h> +#include	<stdlib.h> +#include	<string.h> + + +static struct testsuite_s { +	const char *mailfrom; +	const char *tcpremoteip; +	const char *tcpremotehost; +	const char *helodomain; +	const char *mydomain; +} testsuite[]={ +	{"spf1.email-scan.com","192.168.1.10","spf1.email-scan.com","spf1","example.com"}, +	{"spf2.email-scan.com","192.168.1.10","spf1.email-scan.com","spf1","example.com"}, +	{"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"}, +	{"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"}, +	{"spf6.email-scan.com","::ffff:192.168.1.0","spf5.email-scan.com","helo","example.com"}, +	{"spf6.email-scan.com","::ffff:192.168.1.1","spf5.email-scan.com","helo","example.com"}, +	{"spf7.email-scan.com","::ffff:192.168.1.1","spf5.email-scan.com","helo","example.com"}, +	{"spf7.email-scan.com","::ffff:192.168.2.1","spf5.email-scan.com","helo","example.com"}, +	{"spf7.email-scan.com","::ffff:192.168.1.255","spf5.email-scan.com","helo","example.com"}, +	{"spf8.email-scan.com","::ffff:192.168.1.1","spf5.email-scan.com","helo","example.com"}, +	{"spf8.email-scan.com","::ffff:192.168.2.1","spf5.email-scan.com","helo","example.com"}, +	{"spf8.email-scan.com","::ffff:192.168.1.255","spf5.email-scan.com","helo","example.com"}, +	{"spf9.email-scan.com","::ffff:192.168.1.130","spf5.email-scan.com","helo","example.com"}, +	{"spf9.email-scan.com","::ffff:192.168.1.129","spf5.email-scan.com","helo","example.com"}, +	{"spf9.email-scan.com","::ffff:192.168.1.10","spf5.email-scan.com","helo","example.com"}, +	{"spf10.email-scan.com","::ffff:192.168.1.10","spf10.email-scan.com","helo","example.com"}, +	{"spf10.email-scan.com","::ffff:192.168.1.50","spf10.email-scan.com","helo","example.com"}, +	{"spf11.email-scan.com","::ffff:192.168.0.1","spf10.email-scan.com","helo","example.com"}, +	{"spf11.email-scan.com","::ffff:192.168.0.2","spf10.email-scan.com","helo","example.com"}, +	{"spf12.email-scan.com","::ffff:192.168.0.1","spf10.email-scan.com","helo","example.com"}, +	{"spf12.email-scan.com","::ffff:192.168.1.1","spf10.email-scan.com","helo","example.com"}, +	{"spf13.email-scan.com","::ffff:192.168.1.1","spf10.email-scan.com","spf1","example.com"}, +	{"spf13.email-scan.com","::ffff:192.168.1.1","spf10.email-scan.com","spf50","example.com"}, +	{"spf14.email-scan.com","::ffff:192.168.2.0","spf10.email-scan.com","helo","example.com"}, +	{"spf14.email-scan.com","::ffff:192.168.2.1","spf10.email-scan.com","helo","example.com"}, +	{"spf14.email-scan.com","::ffff:192.168.2.1","spf10.email-scan.com","spf11","example.com"}, +	{"spf14.email-scan.com","::ffff:192.168.0.1","spf10.email-scan.com","spf11","example.com"}, +	{"spf15.email-scan.com","::ffff:192.168.0.1","spf10.email-scan.com","spf11","example.com"} +}; + +static int testspf(const char *mailfrom, +		   const char *tcpremoteip, +		   const char *tcpremotehost, +		   const char *helodomain, +		   const char *mydomain) +{ +	char buf[256]; + +	switch (rfc1035_spf_lookup(mailfrom, tcpremoteip, tcpremotehost, +				   helodomain, mydomain, buf, sizeof(buf))) { +	case SPF_NONE: +		printf("none\n"); +		break; +	case SPF_NEUTRAL: +		printf("neutral\n"); +		break; +	case SPF_PASS: +		printf("pass\n"); +		break; +	case SPF_FAIL: +		printf("fail: %s\n", buf); +		break; +	case SPF_SOFTFAIL: +		printf("softfail\n"); +		break; +	case SPF_ERROR: +		printf("error\n"); +		break; +	default: +		printf("unknown\n"); +	} +	return 0; +} + +int main(int argc, char **argv) +{ +	if (argc == 2 && strncmp(argv[1], "-test=", 6) == 0) +	{ +		int loop_cnt=atoi(argv[1]+6); +		int i; + +		for (i=0;i<loop_cnt;i++) +		{ +			int j; + +			for (j=0; j<sizeof(testsuite)/sizeof(testsuite[0]); +			     j++) +				testspf(testsuite[j].mailfrom, +					testsuite[j].tcpremoteip, +					testsuite[j].tcpremotehost, +					testsuite[j].helodomain, +					testsuite[j].mydomain); +		} +		return 0; +	} + +	if (argc < 6) +	{ +		printf("Usage: %s mailfrom remoteip remotehost helo me\n", +		       argv[0]); +		exit(1); +	} + +	return testspf(argv[1], argv[2], argv[3], argv[4], argv[5]); +} + diff --git a/rfc1035/testsuite.txt b/rfc1035/testsuite.txt new file mode 100644 index 0000000..aa98c1a --- /dev/null +++ b/rfc1035/testsuite.txt @@ -0,0 +1,32 @@ +pass +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 +pass +pass +fail: Address does not pass the Sender Policy Framework +pass +fail: Address does not pass the Sender Policy Framework +pass +fail: Address does not pass the Sender Policy Framework +pass +pass +fail: Address does not pass the Sender Policy Framework +pass +pass +pass +fail: Address does not pass the Sender Policy Framework +pass +fail: Address does not pass the Sender Policy Framework +pass +fail: Address does not pass the Sender Policy Framework +pass +fail: Address does not pass the Sender Policy Framework +pass +fail: Address does not pass the Sender Policy Framework +pass +unknown +fail: Address does not pass the Sender Policy Framework +pass +unknown | 
