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