summaryrefslogtreecommitdiffstats
path: root/rfc1035
diff options
context:
space:
mode:
authorSam Varshavchik2013-08-19 16:39:41 -0400
committerSam Varshavchik2013-08-25 14:43:51 -0400
commit9c45d9ad13fdf439d44d7443ae75da15ea0223ed (patch)
tree7a81a04cb51efb078ee350859a64be2ebc6b8813 /rfc1035
parenta9520698b770168d1f33d6301463bb70a19655ec (diff)
downloadcourier-libs-9c45d9ad13fdf439d44d7443ae75da15ea0223ed.tar.bz2
Initial checkin
Imported from subversion report, converted to git. Updated all paths in scripts and makefiles, reflecting the new directory hierarchy.
Diffstat (limited to 'rfc1035')
-rw-r--r--rfc1035/.gitignore2
-rw-r--r--rfc1035/Makefile.am42
-rw-r--r--rfc1035/configure.in390
-rw-r--r--rfc1035/rfc1035.c496
-rw-r--r--rfc1035/rfc1035.h630
-rw-r--r--rfc1035/rfc1035an.c141
-rw-r--r--rfc1035/rfc1035bindsource.c21
-rw-r--r--rfc1035/rfc1035dump.c170
-rw-r--r--rfc1035/rfc1035dumprrdata.c373
-rw-r--r--rfc1035/rfc1035fmttime.c24
-rw-r--r--rfc1035/rfc1035gettxt.c30
-rw-r--r--rfc1035/rfc1035ifconf.c183
-rw-r--r--rfc1035/rfc1035ipv6to4.c51
-rw-r--r--rfc1035/rfc1035mkaddress.c69
-rw-r--r--rfc1035/rfc1035mksocket.c39
-rw-r--r--rfc1035/rfc1035mxlist.c448
-rw-r--r--rfc1035/rfc1035mxlist.h57
-rw-r--r--rfc1035/rfc1035qa.c238
-rw-r--r--rfc1035/rfc1035qptr.c149
-rw-r--r--rfc1035/rfc1035reply.c479
-rw-r--r--rfc1035/rfc1035resolve.c236
-rw-r--r--rfc1035/rfc1035sameip.c55
-rw-r--r--rfc1035/rfc1035search.c214
-rw-r--r--rfc1035/rfc1035sockaddrip.c45
-rw-r--r--rfc1035/rfc1035sockaddrport.c38
-rw-r--r--rfc1035/rfc1035str.c143
-rw-r--r--rfc1035/rfc1035tcp.c193
-rw-r--r--rfc1035/rfc1035udp.c167
-rw-r--r--rfc1035/spf.c1466
-rw-r--r--rfc1035/spf.h73
-rw-r--r--rfc1035/testlookup.c266
-rw-r--r--rfc1035/testspf.c120
-rw-r--r--rfc1035/testsuite.txt32
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(&current_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(&current_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(&current_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(&current_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(&current_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