diff --git a/Makefile b/Makefile index 7141f0b..3cf3dfb 100644 --- a/Makefile +++ b/Makefile @@ -8,18 +8,20 @@ include $(TOPDIR)/rules.mk PKG_NAME:=shadowsocks-libev -PKG_VERSION:=2.6.2 +PKG_VERSION:=2.6.3 PKG_RELEASE:=1 -PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz -PKG_SOURCE_URL:=https://github.com/shadowsocks/openwrt-shadowsocks/releases/download/v$(PKG_VERSION) -PKG_MD5SUM:=6afee8b6ebcef21e8e921d198b93c362 +PKG_SOURCE_PROTO:=git +PKG_SOURCE_URL:=https://github.com/shadowsocks/shadowsocks-libev.git +PKG_SOURCE_SUBDIR:=$(PKG_NAME)-$(PKG_VERSION)-$(PKG_RELEASE) +PKG_SOURCE_VERSION:=38a3084b1f29ca0ccb5f929ae149fb64a0477fa5 +PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION)-$(PKG_SOURCE_VERSION).tar.xz PKG_LICENSE:=GPLv3 PKG_LICENSE_FILES:=LICENSE -PKG_MAINTAINER:=Max Lv +PKG_MAINTAINER:=Jian Chang -PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)/$(BUILD_VARIANT)/$(PKG_NAME)-$(PKG_VERSION) +PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)/$(BUILD_VARIANT)/$(PKG_NAME)-$(PKG_VERSION)-$(PKG_RELEASE) PKG_INSTALL:=1 PKG_FIXUP:=autoreconf @@ -34,7 +36,7 @@ define Package/shadowsocks-libev/Default TITLE:=Lightweight Secured Socks5 Proxy $(2) URL:=https://github.com/shadowsocks/shadowsocks-libev VARIANT:=$(1) - DEPENDS:=$(3) +libpcre +libpthread + DEPENDS:=$(3) +libev +libpcre +libpthread +libsodium endef Package/shadowsocks-libev = $(call Package/shadowsocks-libev/Default,openssl,(OpenSSL),+libopenssl +zlib) diff --git a/patches/0001-embed-libudns.patch b/patches/0001-embed-libudns.patch new file mode 100644 index 0000000..6252d0f --- /dev/null +++ b/patches/0001-embed-libudns.patch @@ -0,0 +1,7903 @@ +From 893f38557705769c11439b29ba49dfce325622ed Mon Sep 17 00:00:00 2001 +From: Jian Chang +Date: Wed, 25 Jan 2017 17:30:43 +0800 +Subject: [PATCH] embed libudns + +--- + Makefile.am | 2 +- + configure.ac | 12 +- + libudns/Makefile.am | 8 + + libudns/dnsget.1 | 195 +++++++ + libudns/dnsget.c | 759 ++++++++++++++++++++++++++ + libudns/ex-rdns.c | 114 ++++ + libudns/getopt.c | 165 ++++++ + libudns/inet_XtoX.c | 321 +++++++++++ + libudns/rblcheck.1 | 151 ++++++ + libudns/rblcheck.c | 378 +++++++++++++ + libudns/udns.3 | 1352 +++++++++++++++++++++++++++++++++++++++++++++++ + libudns/udns.h | 778 +++++++++++++++++++++++++++ + libudns/udns_XtoX.c | 57 ++ + libudns/udns_bl.c | 160 ++++++ + libudns/udns_codes.c | 199 +++++++ + libudns/udns_dn.c | 379 +++++++++++++ + libudns/udns_dntosp.c | 30 ++ + libudns/udns_init.c | 233 ++++++++ + libudns/udns_jran.c | 52 ++ + libudns/udns_misc.c | 67 +++ + libudns/udns_parse.c | 169 ++++++ + libudns/udns_resolver.c | 1325 ++++++++++++++++++++++++++++++++++++++++++++++ + libudns/udns_rr_a.c | 123 +++++ + libudns/udns_rr_mx.c | 91 ++++ + libudns/udns_rr_naptr.c | 128 +++++ + libudns/udns_rr_ptr.c | 109 ++++ + libudns/udns_rr_srv.c | 155 ++++++ + libudns/udns_rr_txt.c | 98 ++++ + src/Makefile.am | 11 +- + 29 files changed, 7604 insertions(+), 17 deletions(-) + create mode 100644 libudns/Makefile.am + create mode 100644 libudns/dnsget.1 + create mode 100644 libudns/dnsget.c + create mode 100644 libudns/ex-rdns.c + create mode 100644 libudns/getopt.c + create mode 100644 libudns/inet_XtoX.c + create mode 100644 libudns/rblcheck.1 + create mode 100644 libudns/rblcheck.c + create mode 100644 libudns/udns.3 + create mode 100644 libudns/udns.h + create mode 100644 libudns/udns_XtoX.c + create mode 100644 libudns/udns_bl.c + create mode 100644 libudns/udns_codes.c + create mode 100644 libudns/udns_dn.c + create mode 100644 libudns/udns_dntosp.c + create mode 100644 libudns/udns_init.c + create mode 100644 libudns/udns_jran.c + create mode 100644 libudns/udns_misc.c + create mode 100644 libudns/udns_parse.c + create mode 100644 libudns/udns_resolver.c + create mode 100644 libudns/udns_rr_a.c + create mode 100644 libudns/udns_rr_mx.c + create mode 100644 libudns/udns_rr_naptr.c + create mode 100644 libudns/udns_rr_ptr.c + create mode 100644 libudns/udns_rr_srv.c + create mode 100644 libudns/udns_rr_txt.c + +diff --git a/Makefile.am b/Makefile.am +index 4a25c0f..06457eb 100644 +--- a/Makefile.am ++++ b/Makefile.am +@@ -1,4 +1,4 @@ +-SUBDIRS = libcork libipset src ++SUBDIRS = libcork libipset libudns src + + if ENABLE_DOCUMENTATION + SUBDIRS += doc +@@ -8,8 +8,3 @@ ACLOCAL_AMFLAGS = -I m4 + + pkgconfiglibdir = $(libdir)/pkgconfig + pkgconfiglib_DATA = shadowsocks-libev.pc +- +-EXTRA_DIST = acl Changes completions debian docker rpm README.md +-EXTRA_DIST += libcork/include libipset/include +-EXTRA_DIST += libipset/src/libipset/map/inspection-template.c.in +-EXTRA_DIST += libipset/src/libipset/set/inspection-template.c.in +diff --git a/configure.ac b/configure.ac +index 0669f97..d27a830 100755 +--- a/configure.ac ++++ b/configure.ac +@@ -255,17 +255,6 @@ dnl This is an option defined in the origin configure script + AC_DEFINE([HAVE_IPv6], [1], [Enable IPv6 support in libudns]) + + # Checks for libraries. +-PKG_CHECK_MODULES([LIBUDNS], [libudns], HAVE_LIBUDNS=yes; AC_DEFINE(HAVE_LIBUDNS, 1), +-[AC_LIB_HAVE_LINKFLAGS(udns,, [#include ], [dns_init(0, 0);]) +- if test x$ac_cv_libudns = xyes; then +- LIBS="$LIBUDNS $LIBS" +- else +- AC_MSG_ERROR([[*** +-*** Couldn't find libudns. Try installing libudns-dev or udns-devel. +-***]]) +- fi +-]) +- + PKG_CHECK_MODULES([LIBEV], [libev], HAVE_LIBEV=yes; AC_DEFINE(HAVE_LIBEV, 1), + [AC_LIB_HAVE_LINKFLAGS(ev,, [#include ], [ev_run(0,0);]) + if test x$ac_cv_libev = xyes; then +@@ -285,6 +274,7 @@ AC_CONFIG_FILES([shadowsocks-libev.pc + Makefile + libcork/Makefile + libipset/Makefile ++ libudns/Makefile + src/Makefile]) + + AC_OUTPUT +diff --git a/libudns/Makefile.am b/libudns/Makefile.am +new file mode 100644 +index 0000000..4812e22 +--- /dev/null ++++ b/libudns/Makefile.am +@@ -0,0 +1,8 @@ ++SRCS = udns_dn.c udns_dntosp.c udns_parse.c udns_resolver.c udns_init.c \ ++ udns_misc.c udns_XtoX.c \ ++ udns_rr_a.c udns_rr_ptr.c udns_rr_mx.c udns_rr_txt.c udns_bl.c \ ++ udns_rr_srv.c udns_rr_naptr.c udns_codes.c udns_jran.c ++ ++noinst_LTLIBRARIES=libudns.la ++libudns_la_SOURCES= ${SRCS} ++libudns_la_LDFLAGS= -static +diff --git a/libudns/dnsget.1 b/libudns/dnsget.1 +new file mode 100644 +index 0000000..200557f +--- /dev/null ++++ b/libudns/dnsget.1 +@@ -0,0 +1,195 @@ ++.\" dnsget.1: dnsget manpage ++.\" ++.\" Copyright (C) 2005-2014 Michael Tokarev ++.\" This file is part of UDNS library, an async DNS stub resolver. ++.\" ++.\" This library is free software; you can redistribute it and/or ++.\" modify it under the terms of the GNU Lesser General Public ++.\" License as published by the Free Software Foundation; either ++.\" version 2.1 of the License, or (at your option) any later version. ++.\" ++.\" This library is distributed in the hope that it will be useful, ++.\" but WITHOUT ANY WARRANTY; without even the implied warranty of ++.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++.\" Lesser General Public License for more details. ++.\" ++.\" You should have received a copy of the GNU Lesser General Public ++.\" License along with this library, in file named COPYING.LGPL; if not, ++.\" write to the Free Software Foundation, Inc., 59 Temple Place, ++.\" Suite 330, Boston, MA 02111-1307 USA ++ ++.TH dnsget 1 "Jan 2014" "User Utilities" ++ ++.SH NAME ++dnsget \- DNS lookup utility ++ ++.SH SYNOPSYS ++.B dnsget ++.RB [\| \-v \||\| \-q \|] ++.RB [\| \-c ++.IR class \|] ++.RB [\| \-t ++.IR type \|] ++.RB [\| \-o ++.IR opt , opt ,...] ++.IR name \|.\|.\|. ++ ++.SH DESCRIPTION ++.B dnsget ++is a simple command-line to perform DNS lookups, similar to ++.BR host (1) ++and ++.BR dig (1). ++It is useable for both interactive/debugging scenarious and ++in scripts. ++The program is implemented using ++.BR udns (3) ++library. ++ ++.PP ++By default, ++.B dnsget ++produces a human-readable output, similar to ++.RS ++.nf ++alias.example.com. CNAME www.example.com. ++www.example.com. A 192.168.1.1 ++www.example.com. MX 10 mx.example.com. ++.fi ++.RE ++which is just sufficient to see how a given name resolves. ++Output format is controllable with ++.B \-v ++and ++.B \-q ++options -- the former increases verbosity level up to printing ++the whole DNS contents of all packets sent and received, which ++is suitable for debugging DNS problems, while the latter reduces ++the level, making output more quiet, up to bare result with no ++error messages, which is good for scripts. ++ ++.SH OPTIONS ++ ++The following options are recognized by ++.BR dnsget : ++ ++.TP ++.B \-v ++produce more detailed output. More ++.BR \-v 's ++means more details will be produced. With single ++.BR \-v , dnsget ++will print contents of all received DNS packets (in a readable format), ++while with ++.BR \-vv , ++it will output all outgoing DNS packets too. ++ ++.TP ++.B \-q ++the opposite for \fB\-v\fR -- produce less detailed output. ++With single ++.BR \-q , dnsget ++will only show (decoded) data from final DNS resource records (RR), ++while ++.B \-qq ++also suppresses error messages. ++ ++.TP ++\fB\-t \fItype\fR ++request record(s) of the given type \fItype\fR. By default, ++.B dnsget ++will ask for IPv4 address (A) record, or for PTR record if the ++argument in question is an IPv4 or IPv6 address. Recognized ++types include A, AAAA, MX, TXT, CNAME, PTR, NS, SOA, ANY and ++others. ++ ++.TP ++\fB\-c \fIclass\fR ++request DNS record(s) of the given class \fIclass\fR. By ++default ++.B dnsget ++uses IN class. Valid classes include IN, CH, HS, ANY. ++ ++.TP ++.B \-a ++(compatibility option). Equivalent to setting query type to ++.B ANY ++and increasing verbosity level ++.RB ( \-v ). ++ ++.TP ++.B \-C ++(planned) ++ ++.TP ++.B \-x ++(planned) ++ ++.TP ++\fB\-o \fIopt\fR,\fIopt\fR,... ++(may be specified several times). ++Set resolver options (in a form \fIoption\fR:\fIvalue\fR) as if they ++were set in ++.RB $ RES_OPTIONS ++environment variable, or set query flags: ++.RS ++.TP ++\fBtimeout\fR:\fIsec\fR ++Set initial query timeout to \fIsec\fR. ++.TP ++\fBattempts\fR:\fInum\fR ++(re)try every query \fInum\fR times before failing. ++.TP ++\fBudpbuf\fR:\fIbytes\fR ++set DNS UDP buffer size to \fIbytes\fR bytes. Valid values ++are from 512 to 65535. If \fIbytes\fR is greather than 512, ++EDNS0 (RFC 2671) extensions will be used. ++.TP ++\fBport\fR:\fInum\fR ++Use given UDP port number \fInum\fR instead of the default port 53 (domain). ++.TP ++\fBaa\fR ++set AA (auth only) query bit. ++.TP ++\fBnord\fR ++do not set RD (recursion desired) query bit (set by default). ++.TP ++\fBdnssec\fR or \fBdo\fR ++set DNSSEC OK (DO) query flag (\fBdnsget\fR does not verify DNSSEC signatures, ++only displays them; this is set in EDNS RR). ++.TP ++\fBcd\fR ++set CD (checking disabled) query bit. ++.RE ++ ++.TP ++\fB\-n \fInameserver\fR ++Use the given nameserver(s) (may be specified more than once) ++instead of the default. Using this option has the same same effect as ++.RB $ NSCACHEIP ++or ++.RB $ NAMESERVERS ++environment variables, with the only difference that only IPv4 addresses ++are recognized for now, and it is possible to specify names (which will ++be resolved using default settings) instead of IP addresses. ++ ++.TP ++.B \-h ++print short help and exit. ++ ++.SH "RETURN VALUE" ++When all names where resovled successefully, ++.B dnsget ++exits with zero exit status. If at least one name was not found, ++.B dnsget ++will exit with return code 100. If some other error occured during ++name resolution, it will exit with code 99. In case of usage or ++initialization error, ++.B dnsget ++will return 1. ++ ++.SH "SEE ALSO" ++.BR host (1) ++.BR dig (1) ++.BR resolv.conf (5) ++.BR udns (3). +diff --git a/libudns/dnsget.c b/libudns/dnsget.c +new file mode 100644 +index 0000000..3b2f3e8 +--- /dev/null ++++ b/libudns/dnsget.c +@@ -0,0 +1,759 @@ ++/* dnsget.c ++ simple host/dig-like application using UDNS library ++ ++ Copyright (C) 2005 Michael Tokarev ++ This file is part of UDNS library, an async DNS stub resolver. ++ ++ This library is free software; you can redistribute it and/or ++ modify it under the terms of the GNU Lesser General Public ++ License as published by the Free Software Foundation; either ++ version 2.1 of the License, or (at your option) any later version. ++ ++ This library is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ Lesser General Public License for more details. ++ ++ You should have received a copy of the GNU Lesser General Public ++ License along with this library, in file named COPYING.LGPL; if not, ++ write to the Free Software Foundation, Inc., 59 Temple Place, ++ Suite 330, Boston, MA 02111-1307 USA ++ ++ */ ++ ++#ifdef HAVE_CONFIG_H ++# include "config.h" ++#endif ++#ifdef __MINGW32__ ++#include ++#include ++#else ++#include ++#include ++#include ++#include ++#include ++#endif ++#include ++#include ++#include ++#include ++#include ++#include ++#include "udns.h" ++ ++#ifndef HAVE_GETOPT ++# include "getopt.c" ++#endif ++ ++#ifndef AF_INET6 ++# define AF_INET6 10 ++#endif ++ ++static char *progname; ++static int verbose = 1; ++static int errors; ++static int notfound; ++ ++/* verbosity level: ++ * <0 - bare result ++ * 0 - bare result and error messages ++ * 1 - readable result ++ * 2 - received packet contents and `trying ...' stuff ++ * 3 - sent and received packet contents ++ */ ++ ++static void die(int errnum, const char *fmt, ...) { ++ va_list ap; ++ fprintf(stderr, "%s: ", progname); ++ va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap); ++ if (errnum) fprintf(stderr, ": %s\n", strerror(errnum)); ++ else putc('\n', stderr); ++ fflush(stderr); ++ exit(1); ++} ++ ++static const char *dns_xntop(int af, const void *src) { ++ static char buf[6*5+4*4]; ++ return dns_ntop(af, src, buf, sizeof(buf)); ++} ++ ++struct query { ++ const char *name; /* original query string */ ++ unsigned char *dn; /* the DN being looked up */ ++ enum dns_type qtyp; /* type of the query */ ++}; ++ ++static void query_free(struct query *q) { ++ free(q->dn); ++ free(q); ++} ++ ++static struct query * ++query_new(const char *name, const unsigned char *dn, enum dns_type qtyp) { ++ struct query *q = malloc(sizeof(*q)); ++ unsigned l = dns_dnlen(dn); ++ unsigned char *cdn = malloc(l); ++ if (!q || !cdn) die(0, "out of memory"); ++ memcpy(cdn, dn, l); ++ q->name = name; ++ q->dn = cdn; ++ q->qtyp = qtyp; ++ return q; ++} ++ ++static enum dns_class qcls = DNS_C_IN; ++ ++static void ++dnserror(struct query *q, int errnum) { ++ if (verbose >= 0) ++ fprintf(stderr, "%s: unable to lookup %s record for %s: %s\n", progname, ++ dns_typename(q->qtyp), dns_dntosp(q->dn), dns_strerror(errnum)); ++ if (errnum == DNS_E_NXDOMAIN || errnum == DNS_E_NODATA) ++ ++notfound; ++ else ++ ++errors; ++ query_free(q); ++} ++ ++static const unsigned char * ++printtxt(const unsigned char *c) { ++ unsigned n = *c++; ++ const unsigned char *e = c + n; ++ if (verbose > 0) while(c < e) { ++ if (*c < ' ' || *c >= 127) printf("\\%03u", *c); ++ else if (*c == '\\' || *c == '"') printf("\\%c", *c); ++ else putchar(*c); ++ ++c; ++ } ++ else ++ fwrite(c, n, 1, stdout); ++ return e; ++} ++ ++static void ++printhex(const unsigned char *c, const unsigned char *e) { ++ while(c < e) ++ printf("%02x", *c++); ++} ++ ++static unsigned char to_b64[] = ++"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; ++ ++static void ++printb64(const unsigned char *c, const unsigned char *e) { ++ while(c < e) { ++ putchar(to_b64[c[0] >> 2]); ++ if (c+1 < e) { ++ putchar(to_b64[(c[0] & 0x3) << 4 | c[1] >> 4]); ++ if (c+2 < e) { ++ putchar(to_b64[(c[1] & 0xf) << 2 | c[2] >> 6]); ++ putchar(to_b64[c[2] & 0x3f]); ++ } ++ else { ++ putchar(to_b64[(c[1] & 0xf) << 2]); ++ putchar('='); ++ break; ++ } ++ } ++ else { ++ putchar(to_b64[(c[0] & 0x3) << 4]); ++ putchar('='); ++ putchar('='); ++ break; ++ } ++ c += 3; ++ } ++} ++ ++static void ++printdate(time_t time) { ++ struct tm *tm = gmtime(&time); ++ printf("%04d%02d%02d%02d%02d%02d", ++ tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, ++ tm->tm_hour, tm->tm_min, tm->tm_sec); ++} ++ ++static void ++printrr(const struct dns_parse *p, struct dns_rr *rr) { ++ const unsigned char *pkt = p->dnsp_pkt; ++ const unsigned char *end = p->dnsp_end; ++ const unsigned char *dptr = rr->dnsrr_dptr; ++ const unsigned char *dend = rr->dnsrr_dend; ++ unsigned char *dn = rr->dnsrr_dn; ++ const unsigned char *c; ++ unsigned n; ++ ++ if (verbose > 0) { ++ if (verbose > 1) { ++ if (!p->dnsp_rrl && !rr->dnsrr_dn[0] && rr->dnsrr_typ == DNS_T_OPT) { ++ printf(";EDNS%d OPT record (UDPsize: %d, ERcode: %d, Flags: 0x%02x): %d bytes\n", ++ (rr->dnsrr_ttl>>16) & 0xff, /* version */ ++ rr->dnsrr_cls, /* udp size */ ++ (rr->dnsrr_ttl>>24) & 0xff, /* extended rcode */ ++ rr->dnsrr_ttl & 0xffff, /* flags */ ++ rr->dnsrr_dsz); ++ return; ++ } ++ n = printf("%s.", dns_dntosp(rr->dnsrr_dn)); ++ printf("%s%u\t%s\t%s\t", ++ n > 15 ? "\t" : n > 7 ? "\t\t" : "\t\t\t", ++ rr->dnsrr_ttl, ++ dns_classname(rr->dnsrr_cls), ++ dns_typename(rr->dnsrr_typ)); ++ } ++ else ++ printf("%s. %s ", dns_dntosp(rr->dnsrr_dn), dns_typename(rr->dnsrr_typ)); ++ } ++ ++ switch(rr->dnsrr_typ) { ++ ++ case DNS_T_CNAME: ++ case DNS_T_PTR: ++ case DNS_T_NS: ++ case DNS_T_MB: ++ case DNS_T_MD: ++ case DNS_T_MF: ++ case DNS_T_MG: ++ case DNS_T_MR: ++ if (dns_getdn(pkt, &dptr, end, dn, DNS_MAXDN) <= 0) goto xperr; ++ printf("%s.", dns_dntosp(dn)); ++ break; ++ ++ case DNS_T_A: ++ if (rr->dnsrr_dsz != 4) goto xperr; ++ printf("%d.%d.%d.%d", dptr[0], dptr[1], dptr[2], dptr[3]); ++ break; ++ ++ case DNS_T_AAAA: ++ if (rr->dnsrr_dsz != 16) goto xperr; ++ printf("%s", dns_xntop(AF_INET6, dptr)); ++ break; ++ ++ case DNS_T_MX: ++ c = dptr + 2; ++ if (dns_getdn(pkt, &c, end, dn, DNS_MAXDN) <= 0 || c != dend) goto xperr; ++ printf("%d %s.", dns_get16(dptr), dns_dntosp(dn)); ++ break; ++ ++ case DNS_T_TXT: ++ /* first verify it */ ++ for(c = dptr; c < dend; c += n) { ++ n = *c++; ++ if (c + n > dend) goto xperr; ++ } ++ c = dptr; n = 0; ++ while (c < dend) { ++ if (verbose > 0) printf(n++ ? "\" \"":"\""); ++ c = printtxt(c); ++ } ++ if (verbose > 0) putchar('"'); ++ break; ++ ++ case DNS_T_HINFO: /* CPU, OS */ ++ c = dptr; ++ n = *c++; if ((c += n) >= dend) goto xperr; ++ n = *c++; if ((c += n) != dend) goto xperr; ++ c = dptr; ++ if (verbose > 0) putchar('"'); ++ c = printtxt(c); ++ if (verbose > 0) printf("\" \""); else putchar(' '); ++ printtxt(c); ++ if (verbose > 0) putchar('"'); ++ break; ++ ++ case DNS_T_WKS: ++ c = dptr; ++ if (dptr + 4 + 2 >= end) goto xperr; ++ printf("%s %d", dns_xntop(AF_INET, dptr), dptr[4]); ++ c = dptr + 5; ++ for (n = 0; c < dend; ++c, n += 8) { ++ if (*c) { ++ unsigned b; ++ for (b = 0; b < 8; ++b) ++ if (*c & (1 << (7-b))) printf(" %d", n + b); ++ } ++ } ++ break; ++ ++ case DNS_T_SRV: /* prio weight port targetDN */ ++ c = dptr; ++ c += 2 + 2 + 2; ++ if (dns_getdn(pkt, &c, end, dn, DNS_MAXDN) <= 0 || c != dend) goto xperr; ++ c = dptr; ++ printf("%d %d %d %s.", ++ dns_get16(c+0), dns_get16(c+2), dns_get16(c+4), ++ dns_dntosp(dn)); ++ break; ++ ++ case DNS_T_NAPTR: /* order pref flags serv regexp repl */ ++ c = dptr; ++ c += 4; /* order, pref */ ++ for (n = 0; n < 3; ++n) ++ if (c >= dend) goto xperr; ++ else c += *c + 1; ++ if (dns_getdn(pkt, &c, end, dn, DNS_MAXDN) <= 0 || c != dend) goto xperr; ++ c = dptr; ++ printf("%u %u", dns_get16(c+0), dns_get16(c+2)); ++ c += 4; ++ for(n = 0; n < 3; ++n) { ++ putchar(' '); ++ if (verbose > 0) putchar('"'); ++ c = printtxt(c); ++ if (verbose > 0) putchar('"'); ++ } ++ printf(" %s.", dns_dntosp(dn)); ++ break; ++ ++ case DNS_T_KEY: ++ case DNS_T_DNSKEY: ++ /* flags(2) proto(1) algo(1) pubkey */ ++ case DNS_T_DS: ++ case DNS_T_DLV: ++ /* ktag(2) proto(1) algo(1) pubkey */ ++ c = dptr; ++ if (c + 2 + 1 + 1 > dend) goto xperr; ++ printf("%d %d %d", dns_get16(c), c[2], c[3]); ++ c += 2 + 1 + 1; ++ if (c < dend) { ++ putchar(' '); ++ printb64(c, dend); ++ } ++ break; ++ ++ case DNS_T_SIG: ++ case DNS_T_RRSIG: ++ /* type(2) algo(1) labels(1) ottl(4) sexp(4) sinc(4) tag(2) sdn sig */ ++ c = dptr; ++ c += 2 + 1 + 1 + 4 + 4 + 4 + 2; ++ if (dns_getdn(pkt, &c, end, dn, DNS_MAXDN) <= 0) goto xperr; ++ printf("%s %u %u %u ", ++ dns_typename(dns_get16(dptr)), dptr[2], dptr[3], dns_get32(dptr+4)); ++ printdate(dns_get32(dptr+8)); ++ putchar(' '); ++ printdate(dns_get32(dptr+12)); ++ printf(" %d %s. ", dns_get16(dptr+10), dns_dntosp(dn)); ++ printb64(c, dend); ++ break; ++ ++ case DNS_T_SSHFP: /* algo(1), fp type(1), fp... */ ++ if (dend < dptr + 3) goto xperr; ++ printf("%u %u ", dptr[0], dptr[1]); /* algo, fp type */ ++ printhex(dptr + 2, dend); ++ break; ++ ++#if 0 /* unused RR types? */ ++ case DNS_T_NSEC: /* nextDN bitmaps */ ++ c = dptr; ++ if (dns_getdn(pkt, &c, end, dn, DNS_MAXDN) <= 0) goto xperr; ++ printf("%s.", dns_dntosp(dn)); ++ unfinished. ++ break; ++#endif ++ ++ ++ case DNS_T_SOA: ++ c = dptr; ++ if (dns_getdn(pkt, &c, end, dn, DNS_MAXDN) <= 0 || ++ dns_getdn(pkt, &c, end, dn, DNS_MAXDN) <= 0 || ++ c + 4*5 != dend) ++ goto xperr; ++ dns_getdn(pkt, &dptr, end, dn, DNS_MAXDN); ++ printf("%s. ", dns_dntosp(dn)); ++ dns_getdn(pkt, &dptr, end, dn, DNS_MAXDN); ++ printf("%s. ", dns_dntosp(dn)); ++ printf("%u %u %u %u %u", ++ dns_get32(dptr), dns_get32(dptr+4), dns_get32(dptr+8), ++ dns_get32(dptr+12), dns_get32(dptr+16)); ++ break; ++ ++ case DNS_T_MINFO: ++ c = dptr; ++ if (dns_getdn(pkt, &c, end, dn, DNS_MAXDN) <= 0 || ++ dns_getdn(pkt, &c, end, dn, DNS_MAXDN) <= 0 || ++ c != dend) ++ goto xperr; ++ dns_getdn(pkt, &dptr, end, dn, DNS_MAXDN); ++ printf("%s. ", dns_dntosp(dn)); ++ dns_getdn(pkt, &dptr, end, dn, DNS_MAXDN); ++ printf("%s.", dns_dntosp(dn)); ++ break; ++ ++ case DNS_T_NULL: ++ default: ++ printhex(dptr, dend); ++ break; ++ } ++ putchar('\n'); ++ return; ++ ++xperr: ++ printf("\n"); ++ ++errors; ++} ++ ++static int ++printsection(struct dns_parse *p, int nrr, const char *sname) { ++ struct dns_rr rr; ++ int r; ++ if (!nrr) return 0; ++ if (verbose > 1) printf("\n;; %s section (%d):\n", sname, nrr); ++ ++ p->dnsp_rrl = nrr; ++ while((r = dns_nextrr(p, &rr)) > 0) ++ printrr(p, &rr); ++ if (r < 0) printf("<>\n"); ++ return r; ++} ++ ++/* dbgcb will only be called if verbose > 1 */ ++static void ++dbgcb(int code, const struct sockaddr *sa, unsigned slen, ++ const unsigned char *pkt, int r, ++ const struct dns_query *unused_q, void *unused_data) { ++ struct dns_parse p; ++ const unsigned char *cur, *end; ++ int numqd; ++ ++ if (code > 0) { ++ printf(";; trying %s.\n", dns_dntosp(dns_payload(pkt))); ++ printf(";; sending %d bytes query to ", r); ++ } ++ else ++ printf(";; received %d bytes response from ", r); ++ if (sa->sa_family == AF_INET && slen >= sizeof(struct sockaddr_in)) ++ printf("%s port %d\n", ++ dns_xntop(AF_INET, &((struct sockaddr_in*)sa)->sin_addr), ++ htons(((struct sockaddr_in*)sa)->sin_port)); ++#ifdef HAVE_IPv6 ++ else if (sa->sa_family == AF_INET6 && slen >= sizeof(struct sockaddr_in6)) ++ printf("%s port %d\n", ++ dns_xntop(AF_INET6, &((struct sockaddr_in6*)sa)->sin6_addr), ++ htons(((struct sockaddr_in6*)sa)->sin6_port)); ++#endif ++ else ++ printf("<>\n", sa->sa_family); ++ if (code > 0 && verbose < 3) { ++ putchar('\n'); ++ return; ++ } ++ ++ if (code == -2) printf(";; reply from unexpected source\n"); ++ if (code == -5) printf(";; reply to a query we didn't sent (or old)\n"); ++ if (r < DNS_HSIZE) { ++ printf(";; short packet (%d bytes)\n", r); ++ return; ++ } ++ if (dns_opcode(pkt) != 0) ++ printf(";; unexpected opcode %d\n", dns_opcode(pkt)); ++ if (dns_tc(pkt) != 0) ++ printf(";; warning: TC bit set, probably incomplete reply\n"); ++ ++ printf(";; ->>HEADER<<- opcode: "); ++ switch(dns_opcode(pkt)) { ++ case 0: printf("QUERY"); break; ++ case 1: printf("IQUERY"); break; ++ case 2: printf("STATUS"); break; ++ default: printf("UNKNOWN(%u)", dns_opcode(pkt)); break; ++ } ++ printf(", status: %s, id: %d, size: %d\n;; flags:", ++ dns_rcodename(dns_rcode(pkt)), dns_qid(pkt), r); ++ if (dns_qr(pkt)) printf(" qr"); ++ if (dns_aa(pkt)) printf(" aa"); ++ if (dns_tc(pkt)) printf(" tc"); ++ if (dns_rd(pkt)) printf(" rd"); ++ if (dns_ra(pkt)) printf(" ra"); ++ /* if (dns_z(pkt)) printf(" z"); only one reserved bit left */ ++ if (dns_ad(pkt)) printf(" ad"); ++ if (dns_cd(pkt)) printf(" cd"); ++ numqd = dns_numqd(pkt); ++ printf("; QUERY: %d, ANSWER: %d, AUTHORITY: %d, ADDITIONAL: %d\n", ++ numqd, dns_numan(pkt), dns_numns(pkt), dns_numar(pkt)); ++ if (numqd != 1) ++ printf(";; unexpected number of entries in QUERY section: %d\n", ++ numqd); ++ printf("\n;; QUERY SECTION (%d):\n", numqd); ++ cur = dns_payload(pkt); ++ end = pkt + r; ++ while(numqd--) { ++ if (dns_getdn(pkt, &cur, end, p.dnsp_dnbuf, DNS_MAXDN) <= 0 || ++ cur + 4 > end) { ++ printf("; invalid query section\n"); ++ return; ++ } ++ r = printf(";%s.", dns_dntosp(p.dnsp_dnbuf)); ++ printf("%s%s\t%s\n", ++ r > 23 ? "\t" : r > 15 ? "\t\t" : r > 7 ? "\t\t\t" : "\t\t\t\t", ++ dns_classname(dns_get16(cur+2)), dns_typename(dns_get16(cur))); ++ cur += 4; ++ } ++ ++ p.dnsp_pkt = pkt; ++ p.dnsp_cur = p.dnsp_ans = cur; ++ p.dnsp_end = end; ++ p.dnsp_qdn = NULL; ++ p.dnsp_qcls = p.dnsp_qtyp = 0; ++ p.dnsp_ttl = 0xffffffffu; ++ p.dnsp_nrr = 0; ++ ++ r = printsection(&p, dns_numan(pkt), "ANSWER"); ++ if (r == 0) ++ r = printsection(&p, dns_numns(pkt), "AUTHORITY"); ++ if (r == 0) ++ r = printsection(&p, dns_numar(pkt), "ADDITIONAL"); ++ putchar('\n'); ++} ++ ++static void dnscb(struct dns_ctx *ctx, void *result, void *data) { ++ int r = dns_status(ctx); ++ struct query *q = data; ++ struct dns_parse p; ++ struct dns_rr rr; ++ unsigned nrr; ++ unsigned char dn[DNS_MAXDN]; ++ const unsigned char *pkt, *cur, *end; ++ if (!result) { ++ dnserror(q, r); ++ return; ++ } ++ pkt = result; end = pkt + r; cur = dns_payload(pkt); ++ dns_getdn(pkt, &cur, end, dn, sizeof(dn)); ++ dns_initparse(&p, NULL, pkt, cur, end); ++ p.dnsp_qcls = p.dnsp_qtyp = 0; ++ nrr = 0; ++ while((r = dns_nextrr(&p, &rr)) > 0) { ++ if (!dns_dnequal(dn, rr.dnsrr_dn)) continue; ++ if ((qcls == DNS_C_ANY || qcls == rr.dnsrr_cls) && ++ (q->qtyp == DNS_T_ANY || q->qtyp == rr.dnsrr_typ)) ++ ++nrr; ++ else if (rr.dnsrr_typ == DNS_T_CNAME && !nrr) { ++ if (dns_getdn(pkt, &rr.dnsrr_dptr, end, ++ p.dnsp_dnbuf, sizeof(p.dnsp_dnbuf)) <= 0 || ++ rr.dnsrr_dptr != rr.dnsrr_dend) { ++ r = DNS_E_PROTOCOL; ++ break; ++ } ++ else { ++ if (verbose == 1) { ++ printf("%s.", dns_dntosp(dn)); ++ printf(" CNAME %s.\n", dns_dntosp(p.dnsp_dnbuf)); ++ } ++ dns_dntodn(p.dnsp_dnbuf, dn, sizeof(dn)); ++ } ++ } ++ } ++ if (!r && !nrr) ++ r = DNS_E_NODATA; ++ if (r < 0) { ++ dnserror(q, r); ++ free(result); ++ return; ++ } ++ if (verbose < 2) { /* else it is already printed by dbgfn */ ++ dns_rewind(&p, NULL); ++ p.dnsp_qtyp = q->qtyp == DNS_T_ANY ? 0 : q->qtyp; ++ p.dnsp_qcls = qcls == DNS_C_ANY ? 0 : qcls; ++ while(dns_nextrr(&p, &rr)) ++ printrr(&p, &rr); ++ } ++ free(result); ++ query_free(q); ++} ++ ++int main(int argc, char **argv) { ++ int i; ++ int fd; ++ fd_set fds; ++ struct timeval tv; ++ time_t now; ++ char *ns[DNS_MAXSERV]; ++ int nns = 0; ++ struct query *q; ++ enum dns_type qtyp = 0; ++ struct dns_ctx *nctx = NULL; ++ int flags = 0; ++ ++ if (!(progname = strrchr(argv[0], '/'))) progname = argv[0]; ++ else argv[0] = ++progname; ++ ++ if (argc <= 1) ++ die(0, "try `%s -h' for help", progname); ++ ++ if (dns_init(NULL, 0) < 0 || !(nctx = dns_new(NULL))) ++ die(errno, "unable to initialize dns library"); ++ /* we keep two dns contexts: one may be needed to resolve ++ * nameservers if given as names, using default options. ++ */ ++ ++ while((i = getopt(argc, argv, "vqt:c:an:o:f:h")) != EOF) switch(i) { ++ case 'v': ++verbose; break; ++ case 'q': --verbose; break; ++ case 't': ++ if (optarg[0] == '*' && !optarg[1]) ++ i = DNS_T_ANY; ++ else if ((i = dns_findtypename(optarg)) <= 0) ++ die(0, "unrecognized query type `%s'", optarg); ++ qtyp = i; ++ break; ++ case 'c': ++ if (optarg[0] == '*' && !optarg[1]) ++ i = DNS_C_ANY; ++ else if ((i = dns_findclassname(optarg)) < 0) ++ die(0, "unrecognized query class `%s'", optarg); ++ qcls = i; ++ break; ++ case 'a': ++ qtyp = DNS_T_ANY; ++ ++verbose; ++ break; ++ case 'n': ++ if (nns >= DNS_MAXSERV) ++ die(0, "too many nameservers, %d max", DNS_MAXSERV); ++ ns[nns++] = optarg; ++ break; ++ case 'o': ++ case 'f': { ++ char *opt; ++ const char *const delim = " \t,;"; ++ for(opt = strtok(optarg, delim); opt != NULL; opt = strtok(NULL, delim)) { ++ if (dns_set_opts(NULL, optarg) == 0) ++ ; ++ else if (strcmp(opt, "aa") == 0) flags |= DNS_AAONLY; ++ else if (strcmp(optarg, "nord") == 0) flags |= DNS_NORD; ++ else if (strcmp(optarg, "dnssec") == 0) flags |= DNS_SET_DO; ++ else if (strcmp(optarg, "do") == 0) flags |= DNS_SET_DO; ++ else if (strcmp(optarg, "cd") == 0) flags |= DNS_SET_CD; ++ else ++ die(0, "invalid option: `%s'", opt); ++ } ++ break; ++ } ++ case 'h': ++ printf( ++"%s: simple DNS query tool (using udns version %s)\n" ++"Usage: %s [options] domain-name...\n" ++"where options are:\n" ++" -h - print this help and exit\n" ++" -v - be more verbose\n" ++" -q - be less verbose\n" ++" -t type - set query type (A, AAA, PTR etc)\n" ++" -c class - set query class (IN (default), CH, HS, *)\n" ++" -a - equivalent to -t ANY -v\n" ++" -n ns - use given nameserver(s) instead of default\n" ++" (may be specified multiple times)\n" ++" -o opt,opt,... (comma- or space-separated list,\n" ++" may be specified more than once):\n" ++" set resovler options (the same as setting $RES_OPTIONS):\n" ++" timeout:sec - initial query timeout\n" ++" attempts:num - number of attempt to resovle a query\n" ++" ndots:num - if name has more than num dots, lookup it before search\n" ++" port:num - port number for queries instead of default 53\n" ++" udpbuf:num - size of UDP buffer (use EDNS0 if >512)\n" ++" or query flags:\n" ++" aa,nord,dnssec,do,cd - set query flag (auth-only, no recursion,\n" ++" enable DNSSEC (DNSSEC Ok), check disabled)\n" ++ , progname, dns_version(), progname); ++ return 0; ++ default: ++ die(0, "try `%s -h' for help", progname); ++ } ++ ++ argc -= optind; argv += optind; ++ if (!argc) ++ die(0, "no name(s) to query specified"); ++ ++ if (nns) { ++ /* if nameservers given as names, resolve them. ++ * We only allow IPv4 nameservers as names for now. ++ * Ok, it is easy enouth to try both AAAA and A, ++ * but the question is what to do by default. ++ */ ++ struct sockaddr_in sin; ++ int j, r = 0, opened = 0; ++ memset(&sin, 0, sizeof(sin)); ++ sin.sin_family = AF_INET; ++ sin.sin_port = htons(dns_set_opt(NULL, DNS_OPT_PORT, -1)); ++ dns_add_serv(NULL, NULL); ++ for(i = 0; i < nns; ++i) { ++ if (dns_pton(AF_INET, ns[i], &sin.sin_addr) <= 0) { ++ struct dns_rr_a4 *rr; ++ if (!opened) { ++ if (dns_open(nctx) < 0) ++ die(errno, "unable to initialize dns context"); ++ opened = 1; ++ } ++ rr = dns_resolve_a4(nctx, ns[i], 0); ++ if (!rr) ++ die(0, "unable to resolve nameserver %s: %s", ++ ns[i], dns_strerror(dns_status(nctx))); ++ for(j = 0; j < rr->dnsa4_nrr; ++j) { ++ sin.sin_addr = rr->dnsa4_addr[j]; ++ if ((r = dns_add_serv_s(NULL, (struct sockaddr *)&sin)) < 0) ++ break; ++ } ++ free(rr); ++ } ++ else ++ r = dns_add_serv_s(NULL, (struct sockaddr *)&sin); ++ if (r < 0) ++ die(errno, "unable to add nameserver %s", ++ dns_xntop(AF_INET, &sin.sin_addr)); ++ } ++ } ++ dns_free(nctx); ++ ++ fd = dns_open(NULL); ++ if (fd < 0) ++ die(errno, "unable to initialize dns context"); ++ ++ if (verbose > 1) ++ dns_set_dbgfn(NULL, dbgcb); ++ ++ if (flags) ++ dns_set_opt(NULL, DNS_OPT_FLAGS, flags); ++ ++ for (i = 0; i < argc; ++i) { ++ char *name = argv[i]; ++ union { ++ struct in_addr addr; ++ struct in6_addr addr6; ++ } a; ++ unsigned char dn[DNS_MAXDN]; ++ enum dns_type l_qtyp = 0; ++ int abs; ++ if (dns_pton(AF_INET, name, &a.addr) > 0) { ++ dns_a4todn(&a.addr, 0, dn, sizeof(dn)); ++ l_qtyp = DNS_T_PTR; ++ abs = 1; ++ } ++#ifdef HAVE_IPv6 ++ else if (dns_pton(AF_INET6, name, &a.addr6) > 0) { ++ dns_a6todn(&a.addr6, 0, dn, sizeof(dn)); ++ l_qtyp = DNS_T_PTR; ++ abs = 1; ++ } ++#endif ++ else if (!dns_ptodn(name, strlen(name), dn, sizeof(dn), &abs)) ++ die(0, "invalid name `%s'\n", name); ++ else ++ l_qtyp = DNS_T_A; ++ if (qtyp) l_qtyp = qtyp; ++ q = query_new(name, dn, l_qtyp); ++ if (abs) abs = DNS_NOSRCH; ++ if (!dns_submit_dn(NULL, dn, qcls, l_qtyp, abs, 0, dnscb, q)) ++ dnserror(q, dns_status(NULL)); ++ } ++ ++ FD_ZERO(&fds); ++ now = 0; ++ while((i = dns_timeouts(NULL, -1, now)) > 0) { ++ FD_SET(fd, &fds); ++ tv.tv_sec = i; ++ tv.tv_usec = 0; ++ i = select(fd+1, &fds, 0, 0, &tv); ++ now = time(NULL); ++ if (i > 0) dns_ioevent(NULL, now); ++ } ++ ++ return errors ? 1 : notfound ? 100 : 0; ++} +diff --git a/libudns/ex-rdns.c b/libudns/ex-rdns.c +new file mode 100644 +index 0000000..1e1e90d +--- /dev/null ++++ b/libudns/ex-rdns.c +@@ -0,0 +1,114 @@ ++/* ex-rdns.c ++ parallel rDNS resolver example - read IP addresses from stdin, ++ write domain names to stdout ++ ++ Copyright (C) 2005 Michael Tokarev ++ This file is part of UDNS library, an async DNS stub resolver. ++ ++ This library is free software; you can redistribute it and/or ++ modify it under the terms of the GNU Lesser General Public ++ License as published by the Free Software Foundation; either ++ version 2.1 of the License, or (at your option) any later version. ++ ++ This library is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ Lesser General Public License for more details. ++ ++ You should have received a copy of the GNU Lesser General Public ++ License along with this library, in file named COPYING.LGPL; if not, ++ write to the Free Software Foundation, Inc., 59 Temple Place, ++ Suite 330, Boston, MA 02111-1307 USA ++ ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "udns.h" ++ ++static int curq; ++ ++static const char *n2ip(const unsigned char *c) { ++ static char b[sizeof("255.255.255.255")]; ++ sprintf(b, "%u.%u.%u.%u", c[0], c[1], c[2], c[3]); ++ return b; ++} ++static void dnscb(struct dns_ctx *ctx, struct dns_rr_ptr *rr, void *data) { ++ const char *ip = n2ip((unsigned char *)&data); ++ int i; ++ --curq; ++ if (rr) { ++ printf("%s", ip); ++ for(i = 0; i < rr->dnsptr_nrr; ++i) ++ printf(" %s", rr->dnsptr_ptr[i]); ++ putchar('\n'); ++ free(rr); ++ } ++ else ++ fprintf(stderr, "%s: %s\n", ip, dns_strerror(dns_status(ctx))); ++} ++ ++int main(int argc, char **argv) { ++ int c; ++ time_t now; ++ int maxq = 10; ++ struct pollfd pfd; ++ char linebuf[1024]; ++ char *eol; ++ int eof; ++ ++ if (dns_init(NULL, 1) < 0) { ++ fprintf(stderr, "unable to initialize dns library\n"); ++ return 1; ++ } ++ while((c = getopt(argc, argv, "m:r")) != EOF) switch(c) { ++ case 'm': maxq = atoi(optarg); break; ++ case 'r': ++ dns_set_opt(0, DNS_OPT_FLAGS, ++ dns_set_opt(0, DNS_OPT_FLAGS, -1) | DNS_NORD); ++ break; ++ default: return 1; ++ } ++ if (argc != optind) return 1; ++ ++ pfd.fd = dns_sock(0); ++ pfd.events = POLLIN; ++ now = time(NULL); ++ c = optind; ++ eof = 0; ++ while(curq || !eof) { ++ if (!eof && curq < maxq) { ++ union { struct in_addr a; void *p; } pa; ++ if (!fgets(linebuf, sizeof(linebuf), stdin)) { ++ eof = 1; ++ continue; ++ } ++ eol = strchr(linebuf, '\n'); ++ if (eol) *eol = '\0'; ++ if (!linebuf[0]) continue; ++ if (dns_pton(AF_INET, linebuf, &pa.a) <= 0) ++ fprintf(stderr, "%s: invalid address\n", linebuf); ++ else if (dns_submit_a4ptr(0, &pa.a, dnscb, pa.p) == 0) ++ fprintf(stderr, "%s: unable to submit query: %s\n", ++ linebuf, dns_strerror(dns_status(0))); ++ else ++ ++curq; ++ continue; ++ } ++ if (curq) { ++ c = dns_timeouts(0, -1, now); ++ c = poll(&pfd, 1, c < 0 ? -1 : c * 1000); ++ now = time(NULL); ++ if (c) ++ dns_ioevent(0, now); ++ } ++ } ++ return 0; ++} +diff --git a/libudns/getopt.c b/libudns/getopt.c +new file mode 100644 +index 0000000..e15a7a0 +--- /dev/null ++++ b/libudns/getopt.c +@@ -0,0 +1,165 @@ ++/* getopt.c ++ * Simple getopt() implementation. ++ * ++ * Standard interface: ++ * extern int getopt(int argc, char *const *argv, const char *opts); ++ * extern int optind; current index in argv[] ++ * extern char *optarg; argument for the current option ++ * extern int optopt; the current option ++ * extern int opterr; to control error printing ++ * ++ * Some minor extensions: ++ * ignores leading `+' sign in opts[] (unemplemented GNU extension) ++ * handles optional arguments, in form "x::" in opts[] ++ * if opts[] starts with `:', will return `:' in case of missing required ++ * argument, instead of '?'. ++ * ++ * Compile with -DGETOPT_NO_OPTERR to never print errors internally. ++ * Compile with -DGETOPT_NO_STDIO to use write() calls instead of fprintf() for ++ * error reporting (ignored with -DGETOPT_NO_OPTERR). ++ * Compile with -DGETOPT_CLASS=static to get static linkage. ++ * Compile with -DGETOPT_MY to redefine all visible symbols to be prefixed ++ * with "my_", like my_getopt instead of getopt. ++ * Compile with -DTEST to get a test executable. ++ * ++ * Written by Michael Tokarev. Public domain. ++ */ ++ ++#include ++ ++#ifndef GETOPT_CLASS ++# define GETOPT_CLASS ++#endif ++#ifdef GETOPT_MY ++# define optarg my_optarg ++# define optind my_optind ++# define opterr my_opterr ++# define optopt my_optopt ++# define getopt my_getopt ++#endif ++ ++GETOPT_CLASS char *optarg /* = NULL */; ++GETOPT_CLASS int optind = 1; ++GETOPT_CLASS int opterr = 1; ++GETOPT_CLASS int optopt; ++ ++static char *nextc /* = NULL */; ++ ++#if defined(GETOPT_NO_OPTERR) ++ ++#define printerr(argv, msg) ++ ++#elif defined(GETOPT_NO_STDIO) ++ ++extern int write(int, void *, int); ++ ++static void printerr(char *const *argv, const char *msg) { ++ if (opterr) { ++ char buf[64]; ++ unsigned pl = strlen(argv[0]); ++ unsigned ml = strlen(msg); ++ char *p; ++ if (pl + /*": "*/2 + ml + /*" -- c\n"*/6 > sizeof(buf)) { ++ write(2, argv[0], pl); ++ p = buf; ++ } ++ else { ++ memcpy(buf, argv[0], ml); ++ p = buf + pl; ++ } ++ *p++ = ':'; *p++ = ' '; ++ memcpy(p, msg, ml); p += ml; ++ *p++ = ' '; *p++ = '-'; *p++ = '-'; *p++ = ' '; ++ *p++ = optopt; ++ *p++ = '\n'; ++ write(2, buf, p - buf); ++ } ++} ++ ++#else ++ ++#include ++static void printerr(char *const *argv, const char *msg) { ++ if (opterr) ++ fprintf(stderr, "%s: %s -- %c\n", argv[0], msg, optopt); ++} ++ ++#endif ++ ++GETOPT_CLASS int getopt(int argc, char *const *argv, const char *opts) { ++ char *p; ++ ++ optarg = 0; ++ if (*opts == '+') /* GNU extension (permutation) - isn't supported */ ++ ++opts; ++ ++ if (!optind) { /* a way to reset things */ ++ nextc = 0; ++ optind = 1; ++ } ++ ++ if (!nextc || !*nextc) { /* advance to the next argv element */ ++ /* done scanning? */ ++ if (optind >= argc) ++ return -1; ++ /* not an optional argument */ ++ if (argv[optind][0] != '-') ++ return -1; ++ /* bare `-' */ ++ if (argv[optind][1] == '\0') ++ return -1; ++ /* special case `--' argument */ ++ if (argv[optind][1] == '-' && argv[optind][2] == '\0') { ++ ++optind; ++ return -1; ++ } ++ nextc = argv[optind] + 1; ++ } ++ ++ optopt = *nextc++; ++ if (!*nextc) ++ ++optind; ++ p = strchr(opts, optopt); ++ if (!p || optopt == ':') { ++ printerr(argv, "illegal option"); ++ return '?'; ++ } ++ if (p[1] == ':') { ++ if (*nextc) { ++ optarg = nextc; ++ nextc = NULL; ++ ++optind; ++ } ++ else if (p[2] != ':') { /* required argument */ ++ if (optind >= argc) { ++ printerr(argv, "option requires an argument"); ++ return *opts == ':' ? ':' : '?'; ++ } ++ else ++ optarg = argv[optind++]; ++ } ++ } ++ return optopt; ++} ++ ++#ifdef TEST ++ ++#include ++ ++int main(int argc, char **argv) { ++ int c; ++ while((c = getopt(argc, argv, "ab:c::")) != -1) switch(c) { ++ case 'a': ++ case 'b': ++ case 'c': ++ printf("option %c %s\n", c, optarg ? optarg : "(none)"); ++ break; ++ default: ++ return -1; ++ } ++ for(c = optind; c < argc; ++c) ++ printf("non-opt: %s\n", argv[c]); ++ return 0; ++} ++ ++#endif +diff --git a/libudns/inet_XtoX.c b/libudns/inet_XtoX.c +new file mode 100644 +index 0000000..6f05db6 +--- /dev/null ++++ b/libudns/inet_XtoX.c +@@ -0,0 +1,321 @@ ++/* inet_XtoX.c ++ * Simple implementation of the following functions: ++ * inet_ntop(), inet_ntoa(), inet_pton(), inet_aton(). ++ * ++ * Differences from traditional implementaitons: ++ * o modifies destination buffers even on error return. ++ * o no fancy (hex, or 1.2) input support in inet_aton() ++ * o inet_aton() does not accept junk after an IP address. ++ * o inet_ntop(AF_INET) requires at least 16 bytes in dest, ++ * and inet_ntop(AF_INET6) at least 40 bytes ++ * (traditional inet_ntop() will try to fit anyway) ++ * ++ * Compile with -Dinet_XtoX_prefix=pfx_ to have pfx_*() instead of inet_*() ++ * Compile with -Dinet_XtoX_no_ntop or -Dinet_XtoX_no_pton ++ * to disable net2str or str2net conversions. ++ * ++ * #define inet_XtoX_prototypes and #include "this_file.c" ++ * to get function prototypes only (but not for inet_ntoa()). ++ * #define inet_XtoX_decl to be `static' for static visibility, ++ * or use __declspec(dllexport) or somesuch... ++ * ++ * Compile with -DTEST to test against stock implementation. ++ * ++ * Written by Michael Tokarev. Public domain. ++ */ ++ ++#ifdef inet_XtoX_prototypes ++ ++struct in_addr; ++ ++#else ++ ++#include ++ ++#ifdef TEST ++ ++# include ++# include ++# include ++# include ++# include ++# include ++# include ++# undef inet_XtoX_prefix ++# define inet_XtoX_prefix mjt_inet_ ++# undef inet_XtoX_no_ntop ++# undef inet_XtoX_no_pton ++ ++#endif /* TEST */ ++ ++#endif /* inet_XtoX_prototypes */ ++ ++#ifndef inet_XtoX_prefix ++# define inet_XtoX_prefix inet_ ++#endif ++#ifndef inet_XtoX_decl ++# define inet_XtoX_decl /* empty */ ++#endif ++ ++#define cc2_(x,y) cc2__(x,y) ++#define cc2__(x,y) x##y ++#define fn(x) cc2_(inet_XtoX_prefix,x) ++ ++#ifndef inet_XtoX_no_ntop ++ ++inet_XtoX_decl const char * ++fn(ntop)(int af, const void *src, char *dst, int size); ++ ++#ifndef inet_XtoX_prototypes ++ ++static int mjt_ntop4(const void *_src, char *dst, int size) { ++ unsigned i, x, r; ++ char *p; ++ const unsigned char *s = _src; ++ if (size < 4*4) /* for simplicity, disallow non-max-size buffer */ ++ return 0; ++ for (i = 0, p = dst; i < 4; ++i) { ++ if (i) *p++ = '.'; ++ x = r = s[i]; ++ if (x > 99) { *p++ = (char)(r / 100 + '0'); r %= 100; } ++ if (x > 9) { *p++ = (char)(r / 10 + '0'); r %= 10; } ++ *p++ = (char)(r + '0'); ++ } ++ *p = '\0'; ++ return 1; ++} ++ ++static char *hexc(char *p, unsigned x) { ++ static char hex[16] = "0123456789abcdef"; ++ if (x > 0x0fff) *p++ = hex[(x >>12) & 15]; ++ if (x > 0x00ff) *p++ = hex[(x >> 8) & 15]; ++ if (x > 0x000f) *p++ = hex[(x >> 4) & 15]; ++ *p++ = hex[x & 15]; ++ return p; ++} ++ ++static int mjt_ntop6(const void *_src, char *dst, int size) { ++ unsigned i; ++ unsigned short w[8]; ++ unsigned bs = 0, cs = 0; ++ unsigned bl = 0, cl = 0; ++ char *p; ++ const unsigned char *s = _src; ++ ++ if (size < 40) /* for simplicity, disallow non-max-size buffer */ ++ return 0; ++ ++ for(i = 0; i < 8; ++i, s += 2) { ++ w[i] = (((unsigned short)(s[0])) << 8) | s[1]; ++ if (!w[i]) { ++ if (!cl++) cs = i; ++ } ++ else { ++ if (cl > bl) bl = cl, bs = cs; ++ } ++ } ++ if (cl > bl) bl = cl, bs = cs; ++ p = dst; ++ if (bl == 1) ++ bl = 0; ++ if (bl) { ++ for(i = 0; i < bs; ++i) { ++ if (i) *p++ = ':'; ++ p = hexc(p, w[i]); ++ } ++ *p++ = ':'; ++ i += bl; ++ if (i == 8) ++ *p++ = ':'; ++ } ++ else ++ i = 0; ++ for(; i < 8; ++i) { ++ if (i) *p++ = ':'; ++ if (i == 6 && !bs && (bl == 6 || (bl == 5 && w[5] == 0xffff))) ++ return mjt_ntop4(s - 4, p, size - (p - dst)); ++ p = hexc(p, w[i]); ++ } ++ *p = '\0'; ++ return 1; ++} ++ ++inet_XtoX_decl const char * ++fn(ntop)(int af, const void *src, char *dst, int size) { ++ switch(af) { ++ /* don't use AF_*: don't mess with headers */ ++ case 2: /* AF_INET */ if (mjt_ntop4(src, dst, size)) return dst; break; ++ case 10: /* AF_INET6 */ if (mjt_ntop6(src, dst, size)) return dst; break; ++ default: errno = EAFNOSUPPORT; return (char*)0; ++ } ++ errno = ENOSPC; ++ return (char*)0; ++} ++ ++inet_XtoX_decl const char * ++fn(ntoa)(struct in_addr addr) { ++ static char buf[4*4]; ++ mjt_ntop4(&addr, buf, sizeof(buf)); ++ return buf; ++} ++ ++#endif /* inet_XtoX_prototypes */ ++#endif /* inet_XtoX_no_ntop */ ++ ++#ifndef inet_XtoX_no_pton ++ ++inet_XtoX_decl int fn(pton)(int af, const char *src, void *dst); ++inet_XtoX_decl int fn(aton)(const char *src, struct in_addr *addr); ++ ++#ifndef inet_XtoX_prototypes ++ ++static int mjt_pton4(const char *c, void *dst) { ++ unsigned char *a = dst; ++ unsigned n, o; ++ for (n = 0; n < 4; ++n) { ++ if (*c < '0' || *c > '9') ++ return 0; ++ o = *c++ - '0'; ++ while(*c >= '0' && *c <= '9') ++ if ((o = o * 10 + (*c++ - '0')) > 255) ++ return 0; ++ if (*c++ != (n == 3 ? '\0' : '.')) ++ return 0; ++ *a++ = (unsigned char)o; ++ } ++ return 1; ++} ++ ++static int mjt_pton6(const char *c, void *dst) { ++ unsigned short w[8], *a = w, *z, *i; ++ unsigned v, o; ++ const char *sc; ++ unsigned char *d = dst; ++ if (*c != ':') z = (unsigned short*)0; ++ else if (*++c != ':') return 0; ++ else ++c, z = a; ++ i = 0; ++ for(;;) { ++ v = 0; ++ sc = c; ++ for(;;) { ++ if (*c >= '0' && *c <= '9') o = *c - '0'; ++ else if (*c >= 'a' && *c <= 'f') o = *c - 'a' + 10; ++ else if (*c >= 'A' && *c <= 'F') o = *c - 'A' + 10; ++ else break; ++ v = (v << 4) | o; ++ if (v > 0xffff) return 0; ++ ++c; ++ } ++ if (sc == c) { ++ if (z == a && !*c) ++ break; ++ else ++ return 0; ++ } ++ if (*c == ':') { ++ if (a >= w + 8) ++ return 0; ++ *a++ = v; ++ if (*++c == ':') { ++ if (z) ++ return 0; ++ z = a; ++ if (!*++c) ++ break; ++ } ++ } ++ else if (!*c) { ++ if (a >= w + 8) ++ return 0; ++ *a++ = v; ++ break; ++ } ++ else if (*c == '.') { ++ if (a > w + 6) ++ return 0; ++ if (!mjt_pton4(sc, d)) ++ return 0; ++ *a++ = ((unsigned)(d[0]) << 8) | d[1]; ++ *a++ = ((unsigned)(d[2]) << 8) | d[3]; ++ break; ++ } ++ else ++ return 0; ++ } ++ v = w + 8 - a; ++ if ((v && !z) || (!v && z)) ++ return 0; ++ for(i = w; ; ++i) { ++ if (i == z) ++ while(v--) { *d++ = '\0'; *d++ = '\0'; } ++ if (i >= a) ++ break; ++ *d++ = (unsigned char)((*i >> 8) & 255); ++ *d++ = (unsigned char)(*i & 255); ++ } ++ return 1; ++} ++ ++inet_XtoX_decl int fn(pton)(int af, const char *src, void *dst) { ++ switch(af) { ++ /* don't use AF_*: don't mess with headers */ ++ case 2 /* AF_INET */: return mjt_pton4(src, dst); ++ case 10 /* AF_INET6 */: return mjt_pton6(src, dst); ++ default: errno = EAFNOSUPPORT; return -1; ++ } ++} ++ ++inet_XtoX_decl int fn(aton)(const char *src, struct in_addr *addr) { ++ return mjt_pton4(src, addr); ++} ++ ++#endif /* inet_XtoX_prototypes */ ++ ++#endif /* inet_XtoX_no_pton */ ++ ++#ifdef TEST ++ ++int main(int argc, char **argv) { ++ int i; ++ char n0[16], n1[16]; ++ char p0[64], p1[64]; ++ int af = AF_INET; ++ int pl = sizeof(p0); ++ int r0, r1; ++ const char *s0, *s1; ++ ++ while((i = getopt(argc, argv, "46a:p:")) != EOF) switch(i) { ++ case '4': af = AF_INET; break; ++ case '6': af = AF_INET6; break; ++ case 'a': case 'p': pl = atoi(optarg); break; ++ default: return 1; ++ } ++ for(i = optind; i < argc; ++i) { ++ char *a = argv[i]; ++ ++ printf("%s:\n", a); ++ r0 = inet_pton(af, a, n0); ++ printf(" p2n stock: %s\n", ++ (r0 < 0 ? "(notsupp)" : !r0 ? "(inval)" : fn(ntop)(af,n0,p0,sizeof(p0)))); ++ r1 = fn(pton)(af, a, n1); ++ printf(" p2n this : %s\n", ++ (r1 < 0 ? "(notsupp)" : !r1 ? "(inval)" : fn(ntop)(af,n1,p1,sizeof(p1)))); ++ ++ if ((r0 > 0) != (r1 > 0) || ++ (r0 > 0 && r1 > 0 && memcmp(n0, n1, af == AF_INET ? 4 : 16) != 0)) ++ printf(" DIFFER!\n"); ++ ++ s0 = inet_ntop(af, n1, p0, pl); ++ printf(" n2p stock: %s\n", s0 ? s0 : "(inval)"); ++ s1 = fn(ntop)(af, n1, p1, pl); ++ printf(" n2p this : %s\n", s1 ? s1 : "(inval)"); ++ if ((s0 != 0) != (s1 != 0) || ++ (s0 && s1 && strcmp(s0, s1) != 0)) ++ printf(" DIFFER!\n"); ++ ++ } ++ return 0; ++} ++ ++#endif /* TEST */ +diff --git a/libudns/rblcheck.1 b/libudns/rblcheck.1 +new file mode 100644 +index 0000000..6c822c0 +--- /dev/null ++++ b/libudns/rblcheck.1 +@@ -0,0 +1,151 @@ ++.\" rblcheck.1 ++.\" rblckeck manpage ++.\" ++.\" Copyright (C) 2005 Michael Tokarev ++.\" This file is part of UDNS library, an async DNS stub resolver. ++.\" ++.\" This library is free software; you can redistribute it and/or ++.\" modify it under the terms of the GNU Lesser General Public ++.\" License as published by the Free Software Foundation; either ++.\" version 2.1 of the License, or (at your option) any later version. ++.\" ++.\" This library is distributed in the hope that it will be useful, ++.\" but WITHOUT ANY WARRANTY; without even the implied warranty of ++.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++.\" Lesser General Public License for more details. ++.\" ++.\" You should have received a copy of the GNU Lesser General Public ++.\" License along with this library, in file named COPYING.LGPL; if not, ++.\" write to the Free Software Foundation, Inc., 59 Temple Place, ++.\" Suite 330, Boston, MA 02111-1307 USA ++ ++.TH rblckeck 1 "Apr 2005" "User Utilities" ++ ++.SH NAME ++rblckeck \- DNSBL lookup utility ++ ++.SH SYNOPSYS ++.B rblcheck ++.RB [\| \-s ++.IR zone \|] ++.RB [\| \-S ++.IR zone\-file \|] ++.RB [\| \-c \|] ++.RB [\| \-tmvq \|] ++.RB [\| \-n ++.IR nsaddr \|] ++.IR address \|.\|.\|. ++ ++.SH DESCRIPTION ++.B rblcheck ++is a simple command-line to perform DNSBL (DNS-based blocklists) lookups. ++For every IP address (or a name, in which case it will be resolved to an ++address first), the utility verifies whenever it is listed in a (list of) ++DNS blocklists specified with ++.B \-s ++or ++.B \-S ++options, optionally obtains text assotiated with the listing (usually it ++is either some description about the reason of the listing or an URL ++referring to such a description), and displays results on standard output. ++.PP ++The program is implemented on top of ++.BR udns (3) ++library. ++ ++.SH OPTIONS ++ ++The following options are recognized by ++.BR rblcheck : ++ ++.TP ++.B \-s \fIzone\fR ++add the given \fIzone\fR DNSBL name to the list of active zones. ++.TP ++.B \-S \fIzone-file\fR ++add list of zones from the named \fIzone-file\fR to the list of ++active zones (the file specifies one zone as the first word on a ++line, empty lines and lines starting with `#' character are ignored). ++.TP ++.B \-c ++reset active zone list. ++.TP ++.B \-v ++be more verbose, produce more detailed output. ++.TP ++.B \-q ++the opposite for \fB\-v\fR -- produce less detailed output. ++.TP ++.B \-t ++obtain text for listed addresses. ++.TP ++.B \-n \fInsaddr\fR ++Use the given nameserver (given as IPv4 or IPv6 address) instead of the ++default. The same effect may be achieved by setting $NSCACHEIP environment ++variable. ++.TP ++.B \-m ++stop after first hit, ie after the first address which is found to be ++listed. ++ ++.TP ++.B \-h ++print short help and exit. ++ ++.PP ++If no ++.BR \-s , ++.BR \-S ++and ++.B \-c ++options are given, ++.B rblcheck ++will try to obtain list of zones using $RBLCHECK_ZONES environment variable, ++or ~/.rblcheckrc, or /etc/rblckechrc files, in that order. If no zones are ++found, it will exit unsuccessefully. ++ ++.SH "RETURN VALUE" ++When no addresses given are listed and no errors occured, ++.B rblcheck ++exits with code 0. If at least one address is listed, ++.B rblcheck ++returns 100. In case of DNS errors, ++.B rblcheck ++returns 2. ++ ++.SH ENVIRONMENT ++ ++.TP ++.B $RBLCHECK_ZONES ++if no ++.BR \-s , ++.B \-S ++or ++.B \-c ++option is given, ++.B rblcheck ++tries this variable to obtain list of DNSBL zones to check against. ++ ++.SH FILES ++ ++.TP ++$HOME/.rblcheckrc and /etc/rblcheckrc ++if no ++.BR \-s , ++.B \-S ++or ++.B \-c ++option is given, and no $RBLCHECK_ZONES environment variable is set, ++.B rblcheck ++will try the two files (the first one that exists) to obtain list of ++DNSBL zones to check against. ++Each line specifies one zone (only first word in each line is used). ++Empty lines and lines starting with `#' character are ignored. ++ ++.SH "SEE ALSO" ++.BR dnsget (1) ++.BR resolv.conf (5) ++.BR udns (3). ++ ++.SH AUTHOR ++This program and manual pages are written by Michael Tokarev. +diff --git a/libudns/rblcheck.c b/libudns/rblcheck.c +new file mode 100644 +index 0000000..b89b1a3 +--- /dev/null ++++ b/libudns/rblcheck.c +@@ -0,0 +1,378 @@ ++/* rblcheck.c ++ dnsbl (rbl) checker application ++ ++ Copyright (C) 2005 Michael Tokarev ++ This file is part of UDNS library, an async DNS stub resolver. ++ ++ This library is free software; you can redistribute it and/or ++ modify it under the terms of the GNU Lesser General Public ++ License as published by the Free Software Foundation; either ++ version 2.1 of the License, or (at your option) any later version. ++ ++ This library is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ Lesser General Public License for more details. ++ ++ You should have received a copy of the GNU Lesser General Public ++ License along with this library, in file named COPYING.LGPL; if not, ++ write to the Free Software Foundation, Inc., 59 Temple Place, ++ Suite 330, Boston, MA 02111-1307 USA ++ ++ */ ++ ++#ifdef HAVE_CONFIG_H ++# include "config.h" ++#endif ++#include ++#include ++#include ++#ifdef __MINGW32__ ++# include ++#else ++# include ++# include ++# include ++# include ++#endif ++#include ++#include ++#include ++#include "udns.h" ++ ++#ifndef HAVE_GETOPT ++# include "getopt.c" ++#endif ++ ++static const char *version = "udns-rblcheck 0.4"; ++static char *progname; ++ ++static void error(int die, const char *fmt, ...) { ++ va_list ap; ++ fprintf(stderr, "%s: ", progname); ++ va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap); ++ putc('\n', stderr); ++ fflush(stderr); ++ if (die) ++ exit(1); ++} ++ ++struct rblookup { ++ struct ipcheck *parent; ++ struct in_addr key; ++ const char *zone; ++ struct dns_rr_a4 *addr; ++ struct dns_rr_txt *txt; ++}; ++ ++struct ipcheck { ++ const char *name; ++ int naddr; ++ int listed; ++ struct rblookup *lookup; ++}; ++ ++#define notlisted ((void*)1) ++ ++static int nzones, nzalloc; ++static const char **zones; ++ ++static int do_txt; ++static int stopfirst; ++static int verbose = 1; ++/* verbosity level: ++ * <0 - only bare As/TXTs ++ * 0 - what RBL result ++ * 1(default) - what is listed by RBL: result ++ * 2 - what is[not ]listed by RBL: result, name lookups ++ */ ++ ++static int listed; ++static int failures; ++ ++static void *ecalloc(int size, int cnt) { ++ void *t = calloc(size, cnt); ++ if (!t) ++ error(1, "out of memory"); ++ return t; ++} ++ ++static void addzone(const char *zone) { ++ if (nzones >= nzalloc) { ++ const char **zs = (const char**)ecalloc(sizeof(char*), (nzalloc += 16)); ++ if (zones) { ++ memcpy(zs, zones, nzones * sizeof(char*)); ++ free(zones); ++ } ++ zones = zs; ++ } ++ zones[nzones++] = zone; ++} ++ ++static int addzonefile(const char *fname) { ++ FILE *f = fopen(fname, "r"); ++ char linebuf[2048]; ++ if (!f) ++ return 0; ++ while(fgets(linebuf, sizeof(linebuf), f)) { ++ char *p = linebuf, *e; ++ while(*p == ' ' || *p == '\t') ++p; ++ if (*p == '#' || *p == '\n') continue; ++ e = p; ++ while(*e && *e != ' ' && *e != '\t' && *e != '\n') ++ ++e; ++ *e++ = '\0'; ++ p = memcpy(ecalloc(e - p, 1), p, e - p); // strdup ++ addzone(p); ++ } ++ fclose(f); ++ return 1; ++} ++ ++static void dnserror(struct rblookup *ipl, const char *what) { ++ char buf[4*4]; ++ error(0, "unable to %s for %s (%s): %s", ++ what, dns_ntop(AF_INET, &ipl->key, buf, sizeof(buf)), ++ ipl->zone, dns_strerror(dns_status(0))); ++ ++failures; ++} ++ ++static void display_result(struct ipcheck *ipc) { ++ int j; ++ struct rblookup *l, *le; ++ char buf[4*4]; ++ if (!ipc->naddr) return; ++ for (l = ipc->lookup, le = l + nzones * ipc->naddr; l < le; ++l) { ++ if (!l->addr) continue; ++ if (verbose < 2 && l->addr == notlisted) continue; ++ if (verbose >= 0) { ++ dns_ntop(AF_INET, &l->key, buf, sizeof(buf)); ++ if (ipc->name) printf("%s[%s]", ipc->name, buf); ++ else printf("%s", buf); ++ } ++ if (l->addr == notlisted) { ++ printf(" is NOT listed by %s\n", l->zone); ++ continue; ++ } ++ else if (verbose >= 1) ++ printf(" is listed by %s: ", l->zone); ++ else if (verbose >= 0) ++ printf(" %s ", l->zone); ++ if (verbose >= 1 || !do_txt) ++ for (j = 0; j < l->addr->dnsa4_nrr; ++j) ++ printf("%s%s", j ? " " : "", ++ dns_ntop(AF_INET, &l->addr->dnsa4_addr[j], buf, sizeof(buf))); ++ if (!do_txt) ; ++ else if (l->txt) { ++ for(j = 0; j < l->txt->dnstxt_nrr; ++j) { ++ unsigned char *t = l->txt->dnstxt_txt[j].txt; ++ unsigned char *e = t + l->txt->dnstxt_txt[j].len; ++ printf("%s\"", verbose > 0 ? "\n\t" : j ? " " : ""); ++ while(t < e) { ++ if (*t < ' ' || *t >= 127) printf("\\x%02x", *t); ++ else if (*t == '\\' || *t == '"') printf("\\%c", *t); ++ else putchar(*t); ++ ++t; ++ } ++ putchar('"'); ++ } ++ free(l->txt); ++ } ++ else ++ printf("%s", verbose > 0 ? "\n\t" : ""); ++ free(l->addr); ++ putchar('\n'); ++ } ++ free(ipc->lookup); ++} ++ ++static void txtcb(struct dns_ctx *ctx, struct dns_rr_txt *r, void *data) { ++ struct rblookup *ipl = data; ++ if (r) { ++ ipl->txt = r; ++ ++ipl->parent->listed; ++ } ++ else if (dns_status(ctx) != DNS_E_NXDOMAIN) ++ dnserror(ipl, "lookup DNSBL TXT record"); ++} ++ ++static void a4cb(struct dns_ctx *ctx, struct dns_rr_a4 *r, void *data) { ++ struct rblookup *ipl = data; ++ if (r) { ++ ipl->addr = r; ++ ++listed; ++ if (do_txt) { ++ if (dns_submit_a4dnsbl_txt(0, &ipl->key, ipl->zone, txtcb, ipl)) ++ return; ++ dnserror(ipl, "submit DNSBL TXT record"); ++ } ++ ++ipl->parent->listed; ++ } ++ else if (dns_status(ctx) != DNS_E_NXDOMAIN) ++ dnserror(ipl, "lookup DNSBL A record"); ++ else ++ ipl->addr = notlisted; ++} ++ ++static int ++submit_a_queries(struct ipcheck *ipc, ++ int naddr, const struct in_addr *addr) { ++ int z, a; ++ struct rblookup *rl = ecalloc(sizeof(*rl), nzones * naddr); ++ ipc->lookup = rl; ++ ipc->naddr = naddr; ++ for(a = 0; a < naddr; ++a) { ++ for(z = 0; z < nzones; ++z) { ++ rl->key = addr[a]; ++ rl->zone = zones[z]; ++ rl->parent = ipc; ++ if (!dns_submit_a4dnsbl(0, &rl->key, rl->zone, a4cb, rl)) ++ dnserror(rl, "submit DNSBL A query"); ++ ++rl; ++ } ++ } ++ return 0; ++} ++ ++static void namecb(struct dns_ctx *ctx, struct dns_rr_a4 *rr, void *data) { ++ struct ipcheck *ipc = data; ++ if (rr) { ++ submit_a_queries(ipc, rr->dnsa4_nrr, rr->dnsa4_addr); ++ free(rr); ++ } ++ else { ++ error(0, "unable to lookup `%s': %s", ++ ipc->name, dns_strerror(dns_status(ctx))); ++ ++failures; ++ } ++} ++ ++static int submit(struct ipcheck *ipc) { ++ struct in_addr addr; ++ if (dns_pton(AF_INET, ipc->name, &addr) > 0) { ++ submit_a_queries(ipc, 1, &addr); ++ ipc->name = NULL; ++ } ++ else if (!dns_submit_a4(0, ipc->name, 0, namecb, ipc)) { ++ error(0, "unable to submit name query for %s: %s\n", ++ ipc->name, dns_strerror(dns_status(0))); ++ ++failures; ++ } ++ return 0; ++} ++ ++static void waitdns(struct ipcheck *ipc) { ++ struct timeval tv; ++ fd_set fds; ++ int c; ++ int fd = dns_sock(NULL); ++ time_t now = 0; ++ FD_ZERO(&fds); ++ while((c = dns_timeouts(NULL, -1, now)) > 0) { ++ FD_SET(fd, &fds); ++ tv.tv_sec = c; ++ tv.tv_usec = 0; ++ c = select(fd+1, &fds, NULL, NULL, &tv); ++ now = time(NULL); ++ if (c > 0) ++ dns_ioevent(NULL, now); ++ if (stopfirst && ipc->listed) ++ break; ++ } ++} ++ ++int main(int argc, char **argv) { ++ int c; ++ struct ipcheck ipc; ++ char *nameserver = NULL; ++ int zgiven = 0; ++ ++ if (!(progname = strrchr(argv[0], '/'))) progname = argv[0]; ++ else argv[0] = ++progname; ++ ++ while((c = getopt(argc, argv, "hqtvms:S:cn:")) != EOF) switch(c) { ++ case 's': ++zgiven; addzone(optarg); break; ++ case 'S': ++ ++zgiven; ++ if (addzonefile(optarg)) break; ++ error(1, "unable to read zonefile `%s'", optarg); ++ case 'c': ++zgiven; nzones = 0; break; ++ case 'q': --verbose; break; ++ case 'v': ++verbose; break; ++ case 't': do_txt = 1; break; ++ case 'n': nameserver = optarg; break; ++ case 'm': ++stopfirst; break; ++ case 'h': ++ printf("%s: %s (udns library version %s).\n", ++ progname, version, dns_version()); ++ printf("Usage is: %s [options] address..\n", progname); ++ printf( ++"Where options are:\n" ++" -h - print this help and exit\n" ++" -s service - add the service (DNSBL zone) to the serice list\n" ++" -S service-file - add the DNSBL zone(s) read from the given file\n" ++" -c - clear service list\n" ++" -v - increase verbosity level (more -vs => more verbose)\n" ++" -q - decrease verbosity level (opposite of -v)\n" ++" -t - obtain and print TXT records if any\n" ++" -m - stop checking after first address match in any list\n" ++" -n ipaddr - use the given nameserver instead of the default\n" ++"(if no -s or -S option is given, use $RBLCHECK_ZONES, ~/.rblcheckrc\n" ++"or /etc/rblcheckrc in that order)\n" ++ ); ++ return 0; ++ default: ++ error(1, "use `%s -h' for help", progname); ++ } ++ ++ if (!zgiven) { ++ char *s = getenv("RBLCHECK_ZONES"); ++ if (s) { ++ char *k; ++ s = strdup(s); ++ for(k = strtok(s, " \t"); k; k = strtok(NULL, " \t")) ++ addzone(k); ++ free(s); ++ } ++ else { /* probably worthless on windows? */ ++ char *path; ++ char *home = getenv("HOME"); ++ if (!home) home = "."; ++ path = malloc(strlen(home) + 1 + sizeof(".rblcheckrc")); ++ sprintf(path, "%s/.rblcheckrc", home); ++ if (!addzonefile(path)) ++ addzonefile("/etc/rblcheckrc"); ++ free(path); ++ } ++ } ++ if (!nzones) ++ error(1, "no service (zone) list specified (-s or -S option)"); ++ ++ argv += optind; ++ argc -= optind; ++ ++ if (!argc) ++ return 0; ++ ++ if (dns_init(NULL, 0) < 0) ++ error(1, "unable to initialize DNS library: %s", strerror(errno)); ++ if (nameserver) { ++ dns_add_serv(NULL, NULL); ++ if (dns_add_serv(NULL, nameserver) < 0) ++ error(1, "wrong IP address for a nameserver: `%s'", nameserver); ++ } ++ if (dns_open(NULL) < 0) ++ error(1, "unable to initialize DNS library: %s", strerror(errno)); ++ ++ for (c = 0; c < argc; ++c) { ++ if (c && (verbose > 1 || (verbose == 1 && do_txt))) putchar('\n'); ++ memset(&ipc, 0, sizeof(ipc)); ++ ipc.name = argv[c]; ++ submit(&ipc); ++ waitdns(&ipc); ++ display_result(&ipc); ++ if (stopfirst > 1 && listed) break; ++ } ++ ++ return listed ? 100 : failures ? 2 : 0; ++} +diff --git a/libudns/udns.3 b/libudns/udns.3 +new file mode 100644 +index 0000000..23222aa +--- /dev/null ++++ b/libudns/udns.3 +@@ -0,0 +1,1352 @@ ++.\" udns.3 ++.\" udns library manpage ++.\" ++.\" Copyright (C) 2005-2014 Michael Tokarev ++.\" This file is part of UDNS library, an async DNS stub resolver. ++.\" ++.\" This library is free software; you can redistribute it and/or ++.\" modify it under the terms of the GNU Lesser General Public ++.\" License as published by the Free Software Foundation; either ++.\" version 2.1 of the License, or (at your option) any later version. ++.\" ++.\" This library is distributed in the hope that it will be useful, ++.\" but WITHOUT ANY WARRANTY; without even the implied warranty of ++.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++.\" Lesser General Public License for more details. ++.\" ++.\" You should have received a copy of the GNU Lesser General Public ++.\" License along with this library, in file named COPYING.LGPL; if not, ++.\" write to the Free Software Foundation, Inc., 59 Temple Place, ++.\" Suite 330, Boston, MA 02111-1307 USA ++ ++.TH udns 3 "Jan 2014" "Library Functions" ++ ++.SH NAME ++udns \- stub DNS resolver library ++ ++.SH SYNOPSYS ++.nf ++#include ++struct \fBdns_ctx\fR; ++struct \fBdns_query\fR; ++extern struct dns_ctx \fBdns_defctx\fR; ++struct dns_ctx *\fIctx\fR; ++typedef void \fBdns_query_fn\fR(\fIctx\fR, void *\fIresult\fR, void *\fIdata\fR); ++typedef int ++\fBdns_parse_fn\fR(const unsigned char *\fIqnd\fR, ++ const unsigned char *\fIpkt\fR, ++ const unsigned char *\fIcur\fR, ++ const unsigned char *\fIend\fR, ++ void **\fIresultp\fR); ++ ++\fBcc\fR ... -l\fBudns\fR ++.fi ++ ++.SH DESCRIPTION ++ ++.PP ++The DNS library, \fBudns\fR, implements thread-safe stub DNS resolver ++functionality, which may be used both traditional, syncronous way ++and asyncronously, with application-supplied event loop. ++ ++.PP ++While DNS works with both TCP and UDP, performing UDP query first and ++if the result does not fit in UDP buffer (512 bytes max for original ++DNS protocol), retrying the query over TCP, the library uses UDP only, ++but uses EDNS0 (RFC2671) extensions which allows larger UDP buffers. ++ ++.PP ++The library uses single UDP socket to perform all operations even when ++asking multiple nameservers. This way, it is very simple to use the ++library in asyncronous event-loop applications: an application should ++add only single socket to the set of filedescriptors it monitors for I/O. ++ ++.PP ++The library uses two main objects, \fIresolver context\fR of type ++\fBstruct\ dns_ctx\fR, and \fIquery structure\fR of type ++\fBstruct\ dns_query\fR, both are opaque for an application. ++Resolver context holds global information about the resolver, ++such as list of nameservers to use, list of active requests and the like. ++Query objects holds information about a single DNS query in progress and ++are allocated/processed/freed by the library. Pointer to query structure ++may be treated as an identifier of an in-progress query and may be used ++to cancel the asyncronous query or to wait for it to complete. ++ ++.PP ++Asyncronous interface works as follows. An application initializes ++resolver context, submits any number of queries for it using one of ++supplied \fBdns_submit_\fIXXX\fR() routines (each return the query ++identifier as pointer to query structure), waits for input on the ++UDP socket used by the library, and gives some control to the library ++by calling \fBdns_ioevent\fR() and \fBdns_timeouts\fR() routines when ++appropriate. The library performs all necessary processing and executes ++application supplied callback routine when a query completes (either ++successefully or not), giving it the result if any, pointer to the ++resolver context (from which completion status may be obtained), and ++the data pointer supplied by an application when the query has been ++submitted. When submitting a query, an application requests how to ++handle the reply -- to either return raw DNS reply packet for its ++own low-level processing, or it may provide an address of \fIparsing ++routine\fR of type \fBdns_parse_fn\fR to perform conversion of on-wire ++format into easy to use data structure (the library provides parsing ++routines for several commonly used resource record types, as well as ++type-safe higher-level inteface that requests parsing automatically). ++The I/O monitoring and timeout handling may be either traditional ++select() or poll() based, or any callback-driven technique may be ++used. ++ ++.PP ++Additionally, the library provides traditional syncronous interface, ++which may be intermixed with asyncronous calls (during syncronous ++query processing, other asyncronous queries for the same resolver ++context continued to be processed as usual). An application uses ++one of numerous \fBdns_resolve_\fIXXX\fR() routines provided by the ++library to perform a query. As with asyncronous interface, an ++application may either request to return raw DNS packet or type-specific ++data structure by providing the parsing routine to handle the reply. ++Every routine from \fBdns_resolve_\fIXXX\fR() series return pointer ++to result or NULL in case of any error. Query completion status ++(or length of the raw DNS packet) is available from the resolver ++context using \fBdns_status\fR() routine, the same way as for the ++asyncronous interface. ++ ++.PP ++Internally, library uses on-wire format of domain names, referred ++to as \fIDN format\fR in this manual page. This is a series of domain ++\fIlabels\fR whith preceeding length byte, terminated by zero-length ++label wich is integral part of the DN format. There are several routines ++provided to convert from traditional asciiz string to DN and back. ++Higher-level type-specific query interface hides the DN format from ++an application. ++ ++.SH "COMMON DEFINITIONS" ++ ++.PP ++Every DNS Resource Record (RR) has a \fItype\fR and a \fIclass\fR. ++The library defines several integer constants, \fBDNS_C_\fIXXX\fR and ++\fBDNS_T_\fIXXX\fR, to use as symbolic names for RR classes and types, ++such as \fBDNS_C_IN\fR for Internet class, \fBDNS_T_A\fR for IPv4 ++address record type and so on. See udns.h header file for complete list ++of all such constants. ++ ++.PP ++The following constants are defined in udns.h header file: ++.IP "\fBDNS_MAXDN\fR (255 bytes)" ++Maximum length of the domain name in internal (on-wire) DN format. ++.IP "\fBDNS_MAXLABEL\fR (63 bytes)" ++Maximum length of a single label in DN format. ++.IP "\fBDNS_MAXNAME\fR (1024 bytes)" ++Maximum length of asciiz format of a domain name. ++.IP "\fBDNS_HSIZE\fR (12 bytes)" ++Size of header in DNS packet. ++.IP "\fBDNS_PORT\fR (53)" ++Default port to use when contacting a DNS server. ++.IP "\fBDNS_MAXSERV\fR (6 servers)" ++Maximum number of DNS servers to use. ++.IP "\fBDNS_MAXPACKET\fR (512 bytes)" ++Maximum length of DNS UDP packet as specified by original DNS protocol ++.IP "\fBDNS_EDNS0PACKET\fR (4096 bytes)" ++Default length of DNS UDP packet (with EDNS0 extensions) the library uses. ++Note that recursive nameservers usually resides near the client asking them ++to resolve names, e.g. on the same LAN segment or even on the same host, so ++UDP packet fragmentation isn't a problem in most cases. Note also that ++the size of actual packets will be as many bytes as actual reply size requires, ++which is smaller than this value in almost all cases. ++ ++.PP ++Additionally, several constants are defined to simplify work with raw DNS ++packets, such as DNS response codes (\fBDNS_R_\fIXXX\fR), DNS header layout ++(\fBDNS_H_\fIXXX\fR) and others. Again, see udns.h for complete list. ++Library error codes (\fBDNS_E_\fIXXX\fR) are described later in this ++manual page. ++ ++.SH "RESOLVER CONTEXT" ++ ++.PP ++Resolver context, of type \fBstruct\ dns_ctx\fR, is an object which is ++opaque to an application. Several routines provided by the library ++to initialize, copy and free resolver contexts. Most other high-level ++routines in this library expects a pointer to resolver context, \fIctx\fR, ++as the first argument. There is a default resolver context available, ++named \fBdns_defctx\fR. When the context pointer \fIctx\fR passed to ++a routine is NULL, \fBdns_defctx\fR is used. Several resolver contexts ++may be active at the same time, for example, when an application is ++multi-threaded and each thread uses resolver. ++.PP ++In order to use the library, an application should initialize and open ++one or more resolver context objects. These are two separate actions, ++performed by \fBdns_init\fR() (or \fBdns_reset\fR()), and \fBdns_open\fR(). ++Between the two calls, an application is free to pefrorm additional ++initialisation, such as setting custom nameservers, options or domain search ++lists. Optionally, in case no additional custom initialisation is required, ++\fBdns_init\fR() may open the context if \fIdo_open\fR argument (see below) ++is non-zero. ++.PP ++When initializing resolver context, the library uses information from ++system file /etc/resolv.conf (see \fBresolv.conf\fR(5)), consults ++environment variables \fB$LOCALDOMAIN\fR, \fB$NSCACHEIP\fR, ++\fB$NAMESERVERS\fR and \fB$RES_OPTIONS\fR, and local host name to obtain ++list of local nameservers, domain name search list and various resolver ++options. ++.PP ++The following routines to initialize resolver context are available: ++.PP ++.nf ++void \fBdns_reset\fR(\fIctx\fR) ++int \fBdns_init\fR(\fIctx\fR, int \fIdo_open\fR) ++.fi ++.RS ++\fBdns_reset\fR() resets a given resolver context to default values, ++preparing it to be opened by \fBdns_open\fR(). ++It is ok to call this routine against opened and active context - all active ++queries will be dropped, sockets will be closed and so on. This routine ++does not initialize any parameters from system configuration files, use ++\fBdns_init\fR() for this. There's no error return - operation always ++succeeds. \fBdns_init\fR() does everything \fBdns_reset\fR() does, ++plus initializes various parameters of the context according to system ++configuration and process environment variables. If \fIdo_open\fR is ++non-zero, \fBdns_init\fR() calls \fIdns_open\fR(), so that the whole ++library initialisation is performed in a single step. ++.RE ++.PP ++.nf ++struct dns_ctx *\fBdns_new\fR(struct dns_ctx *\fIcopy\fR) ++void \fBdns_free\fR(\fIctx\fR) ++.fi ++.RS ++\fBdns_new\fR() allocates new resolver context and copies all parameters ++for a given resolver context \fIcopy\fR, or default context if \fIcopy\fR ++is NULL, and returns pointer to the newly allocated context. The context ++being copied should be initialized. ++\fBdns_new\fR() may fail if there's no memory available to make a copy ++of \fIcopy\fR, in which case the routine will return NULL pointer. ++\fBdns_free\fR() is used to close assotiated socket and free resolver ++context resources and cancelling (abandoming) all active queries ++assotiated with it. It's an error to free \fBdns_defctx\fR, only ++dynamically allocated contexts returned by \fBdns_new\fR() are allowed ++to be freed by \fBdns_free\fR(). ++.RE ++.PP ++.nf ++int \fBdns_add_serv\fR(\fIctx\fR, const char *\fIservaddr\fR) ++int \fBdns_add_serv_s\fR(\fIctx\fR, const struct sockaddr *\fIsa\fR) ++int \fBdns_add_srch\fR(\fIctx\fR, const char *\fIsrch\fR) ++.fi ++.RS ++Add an element to list of nameservers (\fBdns_add_serv\fR(), as ++asciiz-string \fIservaddr\fR with an IP address of the nameserver, ++and \fBdns_add_serv_s\fR(), as initialized socket address \fIsa\fR), ++or search list (\fBdns_add_srch\fR(), as a pointer to domain name) ++for the given context \fIctx\fR. If the last argument is a NULL ++pointer, the corresponding list (search or nameserver) is reset ++instead. Upon successeful completion, each routine returns new ++number of elements in the list in question. On error, negative ++value is returned and global variable \fBerrno\fR is set appropriately. ++It is an error to call any of this functions if the context is ++opened (after \fBdns_open\fR() or \fBdns_init\fR() with non-zero argument). ++.RE ++.PP ++.nf ++int \fBdns_set_opts\fR(\fIctx\fR, const char *\fIopts\fR) ++.fi ++.RS ++set resolver context options from \fIopts\fR string, in the same way as ++processing \fBoptions\fR statement in resolv.conf and \fB$RES_OPTIONS\fR ++environment variable. Return number of unrecognized/invalid options ++found (all recognized and valid options gets processed). ++.RE ++.PP ++.nf ++void \fBdns_set_opt\fR(\fIctx\fR, int \fIopt\fR, \fIval\fR) ++.fi ++.RS ++.B TODO ++The \fIflags\fR argument is a bitmask with the following bits defined: ++.IP \fBDNS_NOSRCH\fR ++do not perform domain name search in search list. ++.IP \fBDNS_NORD\fR ++do not request recursion when performing queries ++(i.e. don't set RD flag in querues). ++.IP \fBDNS_AAONLY\fR ++request authoritative answers only (i.e. set AA ++flag in queries). ++.RE ++ ++.PP ++.nf ++int \fBdns_open\fR(\fIctx\fR) ++int \fBdns_sock\fR(const \fIctx\fR) ++void \fBdns_close\fR(\fIctx\fR) ++.fi ++.RS ++\fBdns_open\fR() opens the UDP socket used for queries if not already ++open, and return assotiated filedescriptor (or negative value in case ++of error). Before any query can be submitted, the context should be ++opened using this routine. And before opening, the context should be ++initialized. ++\fBdns_sock\fR() return the UDP socket if open, or -1 if not. ++\fBdns_close\fR() closes the UDP socket if it was open, and drops all active ++queries if any. ++.RE ++ ++.PP ++.nf ++int \fBdns_active\fR(const \fIctx\fR) ++.fi ++.RS ++return number of active queries queued for the given context ++\fIctx\fR, or zero if none. ++.RE ++ ++.PP ++.nf ++int \fBdns_status\fR(const \fIctx\fR) ++.fi ++.RS ++return status code from last operation. When using syncronous ++interface, this is the query completion status of the last query. ++With asyncronous interface, from within the callback routine, ++this is the query completion status of the query for which the ++callback is being called. When query submission fails, this ++is the error code indicating failure reason. All error codes ++are negative and are represented by \fBDNS_E_\fIXXX\fR constants ++described below. ++.RE ++ ++.PP ++.nf ++void \fBdns_ioevent\fR(\fIctx\fR, time_t \fInow\fR) ++.fi ++.RS ++this routine may be called by an application to process I/O ++events on the UDP socket used by the library, as returned ++by \fBdns_sock\fR(). The routine tries to receive incoming ++UDP datagram from the socket and process it. The socket is ++set up to be non-blocking, so it is safe to call the routine ++even if there's no data to read. The routine will process ++as many datagrams as are queued for the socket, so it is ++safe to use it with either level-triggered or edge-triggered ++I/O monitoring model. The \fInow\fR argument is either a ++current time as returned by \fBtime\fR(), or 0, in which ++case the routine will obtain current time by it's own. ++.RE ++ ++.PP ++.nf ++int \fBdns_timeouts\fR(\fIctx\fR, int \fImaxwait\fR, time_t \fInow\fR) ++.fi ++.RS ++process any pending timeouts and return number of secounds ++from current time (\fInow\fR if it is not 0) to the time when ++the library wants the application to pass it control to process ++more queued requests. In case when there are no requests pending, ++this time is -1. The routine will not request a time larger than ++\fImaxwait\fR secounds if it is greather or equal to zero. If ++\fInow\fR is 0, the routine will obtain current time by it's own; ++when it is not 0, it should contain current time as returned by ++\fBtime\fR(). ++.RE ++ ++.PP ++.nf ++typedef void \fBdns_utm_fn\fR(\fIctx\fR, int \fItimeout\fR, void *\fIdata\fR) ++void \fBdns_set_tmcbck\fR(\fIctx\fR, dns_utm_fn *\fIutmfn\fR, void *\fIdata\fR) ++.fi ++.RS ++An application may use custom callback-based I/O multiplexing mechanism. ++Usually such a mechanism have concept of a \fItimer\fR, and an ability ++to register a timer event in a form of a callback routine which will ++be executed after certain amount of time. In order to use such an ++event mechanism, udns provides an ability to register and de-register ++timer events necessary for internal processing using whatever event ++mechanism an application uses. For this to work, it is possible to ++assotiate a pointer to a routine that will perform necessary work for ++(de)registering timer events with a given resolver context, and ++udns will call that routine at appropriate times. Prototype of ++such a routine is shown by \fBdns_utm_fn\fR typedef above. Libudns ++assotiates single timer with resolver context. User-supplied \fIutmfn\fR ++routine will be called by the library with the following arguments: ++.IP "\fIctx\fR == NULL" ++delete user timer, at context free time or when an application changes ++user timer request routine using \fBdns_set_tmcbck\fR(); ++.IP "\fIctx\fR != NULL, \fItimeout\fR < 0" ++don't fire timer anymore, when there are no active requests; ++.IP "\fIctx\fR != NULL, \fItimeout\fR == 0" ++fire timer at the next possibility, but not immediately; ++.IP "\fIctx\fR != NULL, \fItimeout\fR > 0" ++fire timer after \fItimeout\fR seconds after now. ++.PP ++The \fIdata\fR argument passed to the routine will be the same ++as passed to \fBdns_set_tmcbck\fR(). ++.PP ++When a timer expires, an application should call \fBdns_timeouts\fR() ++routine (see below). Non-callback timer usage is provided too. ++.RE ++ ++.PP ++.B XXXX TODO: some more resolver context routines, like dns_set_dbgfn() etc. ++ ++.SH "QUERY INTERFACE" ++ ++.PP ++There are two ways to perform DNS queries: traditional syncronous ++way, when udns performs all the necessary processing and return ++control to the application only when the query completes, and ++asyncronous way, when an application submits one or more queries ++to the library using given resolver context, and waits for completion ++by monitoring filedescriptor used by library and calling library ++routines to process input on that filedescriptor. Asyncronous mode ++works with callback routines: an application supplies an address of ++a routine to execute when the query completes, and a data pointer, ++which is passed to the callback routine. ++ ++.PP ++Queries are submitted to the library in a form of \fBstruct\ dns_query\fR. ++To perform asyncronous query, an application calls one of the ++\fBdns_submit_\fIXXX\fR() rounines, and provides necessary information ++for a callback, together with all the query parameters. ++When the query completes, library will call application-supplied callback ++routine, giving it the resolver context (wich holds query completion status), ++dynamically allocated result (which will be either raw DNS packet or, if ++applicatin requested parsing the result by specifying non-NULL parse routine, ++ready-to-use type-specific structure), and a data pointer provided by an ++application when it submitted the query. It is the application who's ++responsible for freeing the result memory. ++.PP ++Generic query callback routine looks like this: ++.nf ++typedef void ++\fBdns_query_fn\fR(\fIctx\fR, void *\fIresult\fR, void *\fIdata\fR) ++.fi ++Type-specific query interface expects similar form of callback ++routine with the only difference in type of \fBresult\fR argument, ++which will be pointer to specific data structure (decoded reply) ++instead of this void pointer to raw DNS packet data. ++ ++.PP ++Result parsing routine looks like this: ++.nf ++typedef int ++\fBdns_parse_fn\fR(const unsigned char *\fIqdn\fR, ++ const unsigned char *\fIpkt\fR, ++ const unsigned char *\fIcur\fR, ++ const unsigned char *\fIend\fR, ++ void **\fIresultp\fR); ++.fi ++When called by the library, the arguments are as follows: ++\fIpkt\fR points to the start of the packet received; ++\fIend\fR points past the end of the packet received; ++\fIcur\fR points past the query DN in the query section of the ++packet; ++\fIqdn\fR points to the original query DN. ++The routine should allocate a single buffer to hold the result, ++parse the reply filling in the buffer, and return the buffer ++using \fIresultp\fR argument. It returns 0 in case of error, ++or udns error code (\fBDNS_E_\fIXXX\fR constants) in case of ++error. ++Note that by the time when the parse routine is called by the ++library, packet is already verified to be a reply to the ++original query, by matching query DN, query class and query type. ++ ++.PP ++Type-specific query inteface supplies necessary parsing routines ++automatically. ++ ++.PP ++In case of error, query completion status as returned by ++\fBdns_status\fR(\fIctx\fR), will contain one of the following values: ++.IP "positive value" ++length of raw DNS packet if parsing is not requested. ++.IP 0 ++the query was successeful and the \fIreply\fR points to type-specific ++data structure. ++.IP \fBDNS_E_TEMPFAIL\fR ++temporary error, the resolver nameserver was not able to ++process our query or timed out. ++.IP \fBDNS_E_PROTOCOL\fR ++protocol error, a nameserver returned malformed reply. ++.IP \fBDNS_E_NXDOMAIN\fR ++the domain name does not exist. ++.IP \fBDNS_E_NODATA\fR ++there is no data of requested type found. ++.IP \fBDNS_E_NOMEM\fR ++out of memory while processing request. ++.IP \fBDNS_E_BADQUERY\fR ++some aspect of the query (most common is the domain name in question) ++is invalid, and the library can't even start a query. ++ ++.PP ++Library provides two series of routines which uses similar interface -- ++one for asyncronous queries and another for syncronous queries. There ++are two general low-level routines in each series to submit (asyncronous ++interface) and resolve (syncronous interface) queries, as well as several ++type-specific routines with more easy-to-use interfaces. To submit ++an asyncronous query, use one of \fBdns_submit_\fIXXX\fR() routine, each ++of which accepts query parameters, pointers to callback routine and to ++callback data, and optional current time hint. Note type-specific ++\fBdns_submit_\fIXXX\fR() routines expects specific type of the callback ++routine as well, which accepts reply as a pointer to corresponding ++structure, not a void pointer). Every \fBdns_submit_\fIXXX\fR() routine ++return pointer to internal query structure of type struct\ dns_query, ++used as an identifier for the given query. ++ ++.PP ++To resolve a query syncronously, use one of \fBdns_resolve_\fIXXX\fR() ++routines, which accepts the same query parameters (but not the ++callback pointers) as corresponding \fBdns_submit_\fIXXX\fR(), and ++return the query result, which is the same as passed to the callback ++routine in case of asyncronous interface. ++ ++.PP ++In either case, the result memory (if the query completed successefully) ++is dynamically allocated and should be freed by an application. If ++the query failed for any reason, the result will be NULL, and error ++status will be available from \fBdns_status\fR(\fIctx\fR) routine ++as shown above. ++ ++.PP ++.nf ++struct dns_query * ++\fBdns_submit_dn\fR(\fIctx\fR, ++ const unsigned char *\fIdn\fR, \fIqcls\fR, \fIqtyp\fR, \fIflags\fR, ++ \fIparse\fR, \fIcbck\fR, \fIdata\fR) ++struct dns_query * ++\fBdns_submit_p\fR(\fIctx\fR, ++ const char *\fIname\fR, \fIqcls\fR, \fIqtyp\fR, \fIflags\fR, ++ \fIparse\fR, \fIcbck\fR, \fIdata\fR) ++ enum dns_class \fIqcls\fR; ++ enum dns_type \fIqtyp\fR; ++ int \fIflags\fR; ++ dns_parse_fn *\fIparse\fR; ++ dns_query_fn *\fIcbck\fR; ++ void *\fIdata\fR; ++.fi ++.RS ++submit a query for processing for the given resolver context \fIctx\fR. ++Two routines differs only in 3rd argument, which is domain name in ++DN format (\fIdn\fR) or asciiz string (\fIname\fR). The query will be ++performed for the given domain name, with type \fIqtyp\fR in class \fIqcls\fR, ++using option bits in \fIflags\fR, using RR parsing routine pointed by ++\fIparse\fR if not-NULL, and upon completion, \fIcbck\fR function will ++be called with the \fIdata\fR argument. ++In case of successeful query submission, ++the routine return pointer to internal query structure which may be treated ++as an identifier of the query as used by the library, and may be used as an ++argument for \fBdns_cancel\fR() routine. In case of error, NULL will be ++returned, and context error status (available using \fIdns_status\fR() routine) ++will be set to corresponding error code, which in this case may be ++DNS_E_BADQUERY if the \fIname\fR of \fIdn\fR is invalid, DNS_E_NOMEM if ++there's no memory available to allocate query structure, or DNS_E_TEMPFAIL ++if an internal error occured. ++.RE ++ ++.PP ++.nf ++void *\fBdns_resolve_dn\fR(\fIctx\fR, ++ const unsigned char *\fIdn\fR, \fIqcls\fR, \fIqtyp\fR, \fIflags\fR, \fIparse\fR); ++void *\fBdns_resolve_p\fR(\fIctx\fR, ++ const char *\fIname\fR, \fIqcls\fR, \fIqtyp\fR, \fIflags\fR, \fIparse\fR) ++ enum dns_class \fIqcls\fR; ++ enum dns_type \fIqtyp\fR; ++ int \fIflags\fR; ++ dns_parse_fn *\fIparse\fR; ++.fi ++.RS ++syncronous interface. The routines perform all the steps necessary to resolve ++the given query and return the result. If there's no positive result for any ++reason, all the routines return NULL, and set context error status (available ++using \fBdns_status\fR() routine) to indicate the error code. If the query ++was successeful, context status code will contain either the length of the ++raw DNS reply packet if \fIparse\fR argument was NULL (in which case the return ++value is pointer to the reply DNS packet), or 0 (in which case the return value ++is the result of \fIparse\fR routine). If the query successeful (return value ++is not NULL), the memory returned was dynamically allocated by the library ++and should be free()d by application after use. ++.RE ++ ++.PP ++.nf ++void *\fBdns_resolve\fR(\fIctx\fR, struct dns_query *\fIq\fR) ++.fi ++.RS ++wait for the given query \fIq\fR, as returned by one of ++\fBdns_submit_\fIXXX\fR() routines, for completion, and ++return the result. The callback routine will not be called ++for this query. After completion, the query identifier \fIq\fR ++is not valid. Both \fBdns_resolve_dn\fR() and \fBdns_resolve_p\fR() ++are just wrappers around corresponding submit routines and this ++\fBdns_resolve\fR() routine. ++.RE ++ ++.PP ++.nf ++void \fBdns_cancel\fR(\fIctx\fR, struct dns_query *\fIq\fR) ++.fi ++.RS ++cancel an active query \fIq\fR, without calling a callback routine. ++After completion, the query identifier \fIq\fR is not valid. ++.RE ++ ++.SH "TYPE-SPECIFIC QUERIES" ++ ++.PP ++In addition to the generic low-level query interface, the library provides ++a set of routines to perform specific queries in a type-safe manner, as ++well as parsers for several well-known resource record types. The library ++implements high-level interface for A, AAAA, PTR, MX and TXT records ++and DNSBL and RHSBL functionality. These routines returns specific types ++as result of a query, instead of raw DNS packets. The following types ++and routines are available. ++ ++.PP ++.nf ++struct \fBdns_rr_null\fR { ++ char *\fBdnsn_qname\fR; /* original query name */ ++ char *\fBdnsn_cname\fR; /* canonical name */ ++ unsigned \fBdnsn_ttl\fR; /* Time-To-Live (TTL) value */ ++ int \fBdnsn_nrr\fR; /* number of records in the set */ ++}; ++.fi ++.PP ++NULL RR set, used as a base for all other RR type structures. ++Every RR structure as used by the library have four standard ++fields as in struct\ \fBdns_rr_null\fR. ++ ++.SS "IN A Queries" ++.PP ++.nf ++struct \fBdns_rr_a4\fR { /* IN A RRset */ ++ char *\fBdnsa4_qname\fR; /* original query name */ ++ char *\fBdnsa4_cname\fR; /* canonical name */ ++ unsigned \fBdnsa4_ttl\fR; /* Time-To-Live (TTL) value */ ++ int \fBdnsa4_nrr\fR; /* number of addresses in the set */ ++ struct in_addr \fBdnsa4_addr\fR[]; /* array of addresses */ ++}; ++typedef void ++ \fBdns_query_a4_fn\fR(\fIctx\fR, struct dns_rr_a4 *\fIresult\fR, \fIdata\fR) ++dns_parse_fn \fBdns_parse_a4\fB; ++struct dns_query * ++\fBdns_submit_a4\fB(\fIctx\fR, const char *\fIname\fR, int \fIflags\fR, ++ dns_query_a4_fn *\fIcbck\fR, \fIdata\fR); ++struct dns_rr_a4 * ++\fBdns_resolve_a4\fB(\fIctx\fR, const char *\fIname\fR, int \fIflags\fR); ++.fi ++.PP ++The \fBdns_rr_a4\fR structure holds a result of an \fBIN A\fR query, ++which is an array of IPv4 addresses. Callback routine for IN A queries ++expected to be of type \fBdns_query_a4_fn\fR, which expects pointer to ++\fBdns_rr_a4\fR structure as query result instead of raw DNS packet. ++The \fBdns_parse_a4\fR() is used to convert raw DNS reply packet into ++\fBdns_rr_a4\fR structure (it is used internally and may be used directly too ++with generic query interface). Routines \fBdns_submit_a4\fR() and ++\fBdns_resolve_a4\fR() are used to perform A IN queries in a type-safe ++manner. The \fIname\fR parameter is the domain name in question, and ++\fIflags\fR is query flags bitmask, with one bit, DNS_NOSRCH, of practical ++interest (if the \fIname\fR is absolute, that is, it ends up with a dot, ++DNS_NOSRCH flag will be set automatically). ++ ++.SS "IN AAAA Queries" ++.PP ++.nf ++struct \fBdns_rr_a6\fR { /* IN AAAA RRset */ ++ char *\fBdnsa6_qname\fR; /* original query name */ ++ char *\fBdnsa6_cname\fR; /* canonical name */ ++ unsigned \fBdnsa6_ttl\fR; /* Time-To-Live (TTL) value */ ++ int \fBdnsa6_nrr\fR; /* number of addresses in the set */ ++ struct in6_addr \fBdnsa6_addr\fR[]; /* array of addresses */ ++}; ++typedef void ++ \fBdns_query_a6_fn\fR(\fIctx\fR, struct dns_rr_a6 *\fIresult\fR, \fIdata\fR) ++dns_parse_fn \fBdns_parse_a6\fB; ++struct dns_query * ++\fBdns_submit_a6\fB(\fIctx\fR, const char *\fIname\fR, int \fIflags\fR, ++ dns_query_a6_fn *\fIcbck\fR, \fIdata\fR); ++struct dns_rr_a6 * ++\fBdns_resolve_a6\fB(\fIctx\fR, const char *\fIname\fR, int \fIflags\fR); ++.fi ++.PP ++The \fBdns_rr_a6\fR structure holds a result of an \fBIN AAAA\fR query, ++which is an array of IPv6 addresses. Callback routine for IN AAAA queries ++expected to be of type \fBdns_query_a6_fn\fR, which expects pointer to ++\fBdns_rr_a6\fR structure as query result instead of raw DNS packet. ++The \fBdns_parse_a6\fR() is used to convert raw DNS reply packet into ++\fBdns_rr_a6\fR structure (it is used internally and may be used directly too ++with generic query interface). Routines \fBdns_submit_a6\fR() and ++\fBdns_resolve_a6\fR() are used to perform AAAA IN queries in a type-safe ++manner. The \fIname\fR parameter is the domain name in question, and ++\fIflags\fR is query flags bitmask, with one bit, DNS_NOSRCH, of practical ++interest (if the \fIname\fR is absolute, that is, it ends up with a dot, ++DNS_NOSRCH flag will be set automatically). ++ ++.SS "IN PTR Queries" ++.PP ++.nf ++struct \fBdns_rr_ptr\fR { /* IN PTR RRset */ ++ char *\fBdnsptr_qname\fR; /* original query name */ ++ char *\fBdnsptr_cname\fR; /* canonical name */ ++ unsigned \fBdnsptr_ttl\fR; /* Time-To-Live (TTL) value */ ++ int \fBdnsptr_nrr\fR; /* number of domain name pointers */ ++ char *\fBdnsptr_ptr\fR[]; /* array of domain name pointers */ ++}; ++typedef void ++ \fBdns_query_ptr_fn\fR(\fIctx\fR, struct dns_rr_ptr *\fIresult\fR, \fIdata\fR) ++dns_parse_fn \fBdns_parse_ptr\fB; ++struct dns_query * ++\fBdns_submit_a4ptr\fB(\fIctx\fR, const struct in_addr *\fBaddr\fR, ++ dns_query_ptr_fn *\fIcbck\fR, \fIdata\fR); ++struct dns_rr_ptr * ++\fBdns_resolve_a4ptr\fB(\fIctx\fR, const struct in_addr *\fBaddr\fR); ++struct dns_query * ++\fBdns_submit_a6ptr\fB(\fIctx\fR, const struct in6_addr *\fBaddr\fR, ++ dns_query_ptr_fn *\fIcbck\fR, \fIdata\fR); ++struct dns_rr_ptr * ++\fBdns_resolve_a6ptr\fB(\fIctx\fR, const struct in6_addr *\fBaddr\fR); ++.fi ++.PP ++The \fBdns_rr_ptr\fR structure holds a result of an IN PTR query, which ++is an array of domain name pointers for a given IPv4 or IPv6 address. ++Callback routine for IN PTR queries expected to be of type ++\fBdns_query_ptr_fn\fR, which expects pointer to \fBdns_rr_ptr\fR ++structure as query result instead of raw DNS packet. The \fBdns_parse_ptr\fR() ++is used to convert raw DNS reply packet into \fBdns_rr_ptr\fR structure ++(it is used internally and may be used directly too with generic query ++interface). Routines \fBdns_submit_a4ptr\fR() and \fBdns_resolve_a4ptr\fR() ++are used to perform IN PTR queries for IPv4 addresses in a type-safe ++manner. Routines \fBdns_submit_a6ptr\fR() and \fBdns_resolve_a6ptr\fR() ++are used to perform IN PTR queries for IPv6 addresses. ++ ++.SS "IN MX Queries" ++.PP ++.nf ++struct \fBdns_mx\fR { /* single MX record */ ++ int \fBpriority\fR; /* priority value of this MX */ ++ char *\fBname\fR; /* domain name of this MX */ ++}; ++struct \fBdns_rr_mx\fR { /* IN MX RRset */ ++ char *\fBdnsmx_qname\fR; /* original query name */ ++ char *\fBdnsmx_cname\fR; /* canonical name */ ++ unsigned \fBdnsmx_ttl\fR; /* Time-To-Live (TTL) value */ ++ int \fBdnsmx_nrr\fR; /* number of mail exchangers in the set */ ++ struct dns_mx \fBdnsmx_mx\fR[]; /* array of mail exchangers */ ++}; ++typedef void ++ \fBdns_query_mx_fn\fR(\fIctx\fR, struct dns_rr_mx *\fIresult\fR, \fIdata\fR) ++dns_parse_fn \fBdns_parse_mx\fB; ++struct dns_query * ++\fBdns_submit_mx\fB(\fIctx\fR, const char *\fIname\fR, int \fIflags\fR, ++ dns_query_mx_fn *\fIcbck\fR, \fIdata\fR); ++struct dns_rr_mx * ++\fBdns_resolve_mx\fB(\fIctx\fR, const char *\fIname\fR, int \fIflags\fR); ++.fi ++.PP ++The \fBdns_rr_mx\fR structure holds a result of an IN MX query, which ++is an array of mail exchangers for a given domain. Callback routine for IN MX ++queries expected to be of type \fBdns_query_mx_fn\fR, which expects pointer to ++\fBdns_rr_mx\fR structure as query result instead of raw DNS packet. ++The \fBdns_parse_mx\fR() is used to convert raw DNS reply packet into ++\fBdns_rr_mx\fR structure (it is used internally and may be used directly too ++with generic query interface). Routines \fBdns_submit_mx\fR() and ++\fBdns_resolve_mx\fR() are used to perform IN MX queries in a type-safe ++manner. The \fIname\fR parameter is the domain name in question, and ++\fIflags\fR is query flags bitmask, with one bit, DNS_NOSRCH, of practical ++interest (if the \fIname\fR is absolute, that is, it ends up with a dot, ++DNS_NOSRCH flag will be set automatically). ++ ++.SS "TXT Queries" ++.PP ++.nf ++struct \fBdns_txt\fR { /* single TXT record */ ++ int \fBlen\fR; /* length of the text */ ++ unsigned char *\fBtxt\fR; /* pointer to the text */ ++}; ++struct \fBdns_rr_txt\fR { /* TXT RRset */ ++ char *\fBdnstxt_qname\fR; /* original query name */ ++ char *\fBdnstxt_cname\fR; /* canonical name */ ++ unsigned \fBdnstxt_ttl\fR; /* Time-To-Live (TTL) value */ ++ int \fBdnstxt_nrr\fR; /* number of text records in the set */ ++ struct dns_txt \fBdnstxt_txt\fR[]; /* array of TXT records */ ++}; ++typedef void ++ \fBdns_query_txt_fn\fR(\fIctx\fR, struct dns_rr_txt *\fIresult\fR, \fIdata\fR) ++dns_parse_fn \fBdns_parse_txt\fB; ++struct dns_query * ++\fBdns_submit_txt\fB(\fIctx\fR, const char *\fIname\fR, enum dns_class \fIqcls\fR, ++ int \fIflags\fR, dns_query_txt_fn *\fIcbck\fR, \fIdata\fR); ++struct dns_rr_txt * ++\fBdns_resolve_txt\fB(\fIctx\fR, const char *\fIname\fR, ++ enum dns_class \fIqcls\fR, int \fIflags\fR); ++.fi ++.PP ++The \fBdns_rr_txt\fR structure holds a result of a TXT query, which is an ++array of text records for a given domain name. Callback routine for TXT ++queries expected to be of type \fBdns_query_txt_fn\fR, which expects pointer ++to \fBdns_rr_txt\fR structure as query result instead of raw DNS packet. ++The \fBdns_parse_txt\fR() is used to convert raw DNS reply packet into ++\fBdns_rr_txt\fR structure (it is used internally and may be used directly too ++with generic query interface). Routines \fBdns_submit_txt\fR() and ++\fBdns_resolve_txt\fR() are used to perform IN MX queries in a type-safe ++manner. The \fIname\fR parameter is the domain name in question, and ++\fIflags\fR is query flags bitmask, with one bit, DNS_NOSRCH, of practical ++interest (if the \fIname\fR is absolute, that is, it ends up with a dot, ++DNS_NOSRCH flag will be set automatically). Note that each TXT string ++is represented by \fBstruct\ dns_txt\fR, while zero-terminated (and the ++len field of the structure does not include the terminator), may contain ++embedded null characters -- content of TXT records is not interpreted ++by the library in any way. ++ ++.SS "SRV Queries" ++.PP ++.nf ++struct \fBdns_srv\fR { /* single SRV record */ ++ int \fBpriority\fR; /* priority of the record */ ++ int \fBweight\fR; /* weight of the record */ ++ int \fBport\fR; /* the port number to connect to */ ++ char *\fBname\fR; /* target host name */ ++}; ++struct \fBdns_rr_srv\fR { /* SRV RRset */ ++ char *\fBdnssrv_qname\fR; /* original query name */ ++ char *\fBdnssrv_cname\fR; /* canonical name */ ++ unsigned \fBdnssrv_ttl\fR; /* Time-To-Live (TTL) value */ ++ int \fBdnssrv_nrr\fR; /* number of text records in the set */ ++ struct dns_srv \fBdnssrv_srv\fR[]; /* array of SRV records */ ++}; ++typedef void ++ \fBdns_query_srv_fn\fR(\fIctx\fR, struct dns_rr_srv *\fIresult\fR, \fIdata\fR) ++dns_parse_fn \fBdns_parse_srv\fB; ++struct dns_query * ++\fBdns_submit_srv\fB(\fIctx\fR, const char *\fIname\fR, const char *\fIservice\fR, const char *\fIprotocol\fR, ++ int \fIflags\fR, dns_query_txt_fn *\fIcbck\fR, \fIdata\fR); ++struct dns_rr_srv * ++\fBdns_resolve_srv\fB(\fIctx\fR, const char *\fIname\fR, const char *\fIservice\fR, const char *\fIprotocol\fR, ++ int \fIflags\fR); ++.fi ++.PP ++The \fBdns_rr_srv\fR structure holds a result of an IN SRV (rfc2782) query, ++which is an array of servers (together with port numbers) which are performing ++operations for a given \fIservice\fR using given \fIprotocol\fR on a target ++domain \fIname\fR. Callback routine for IN SRV queries expected to be of type ++\fBdns_query_srv_fn\fR, which expects pointer to \fBdns_rr_srv\fR structure as ++query result instead of raw DNS packet. The \fBdns_parse_srv\fR() is used to ++convert raw DNS reply packet into \fBdns_rr_srv\fR structure (it is used ++internally and may be used directly too with generic query interface). ++Routines \fBdns_submit_srv\fR() and \fBdns_resolve_srv\fR() are used to ++perform IN SRV queries in a type-safe manner. The \fIname\fR parameter ++is the domain name in question, \fIservice\fR and \fRprotocl\fR specifies the ++service and the protocol in question (the library will construct query DN ++according to rfc2782 rules) and may be NULL (in this case the library ++assumes \fIname\fR parameter holds the complete SRV query), and ++\fIflags\fR is query flags bitmask, with one bit, DNS_NOSRCH, of practical ++interest (if the \fIname\fR is absolute, that is, it ends up with a dot, ++DNS_NOSRCH flag will be set automatically). ++ ++.SS "NAPTR Queries" ++.PP ++.nf ++struct \fBdns_naptr\fR { /* single NAPTR record */ ++ int \fBorder\fR; /* record order */ ++ int \fBpreference\fR; /* preference of this record */ ++ char *\fBflags\fR; /* application-specific flags */ ++ char *\fBservice\fR; /* service parameter */ ++ char *\fBregexp\fR; /* substitutional regular expression */ ++ char *\fBreplacement\fR; /* replacement string */ ++}; ++struct \fBdns_rr_naptr\fR { /* NAPTR RRset */ ++ char *\fBdnsnaptr_qname\fR; /* original query name */ ++ char *\fBdnsnaptr_cname\fR; /* canonical name */ ++ unsigned \fBdnsnaptr_ttl\fR; /* Time-To-Live (TTL) value */ ++ int \fBdnsnaptr_nrr\fR; /* number of text records in the set */ ++ struct dns_naptr \fBdnsnaptr_naptr\fR[]; /* array of NAPTR records */ ++}; ++typedef void ++ \fBdns_query_naptr_fn\fR(\fIctx\fR, struct dns_rr_naptr *\fIresult\fR, \fIdata\fR) ++dns_parse_fn \fBdns_parse_naptr\fB; ++struct dns_query * ++\fBdns_submit_naptr\fB(\fIctx\fR, const char *\fIname\fR, int \fIflags\fR, ++ dns_query_txt_fn *\fIcbck\fR, \fIdata\fR); ++struct dns_rr_naptr * ++\fBdns_resolve_naptr\fB(\fIctx\fR, const char *\fIname\fR, int \fIflags\fR); ++.fi ++.PP ++The \fBdns_rr_naptr\fR structure holds a result of an IN NAPTR (rfc3403) query. ++Callback routine for IN NAPTR queries expected to be of type ++\fBdns_query_naptr_fn\fR, expects pointer to \fBdns_rr_naptr\fR ++structure as query result instead of raw DNS packet. ++The \fBdns_parse_naptr\fR() is used to convert raw DNS reply packet into ++\fBdns_rr_naptr\fR structure (it is used ++internally and may be used directly too with generic query interface). ++Routines \fBdns_submit_naptr\fR() and \fBdns_resolve_naptr\fR() are used to ++perform IN NAPTR queries in a type-safe manner. The \fIname\fR parameter ++is the domain name in question, and \fIflags\fR is query flags bitmask, ++with one bit, DNS_NOSRCH, of practical interest (if the \fIname\fR is ++absolute, that is, it ends up with a dot, DNS_NOSRCH flag will be set ++automatically). ++ ++.SS "DNSBL Interface" ++.PP ++A DNS-based blocklists, or a DNSBLs, are in wide use nowadays, especially ++to protect mailservers from spammers. The library provides DNSBL interface, ++a set of routines to perform queries against DNSBLs. Routines accepts an ++IP address (IPv4 and IPv6 are both supported) and a base DNSBL zone as ++query parameters, and returns either \fBdns_rr_a4\fR or \fBdns_rr_txt\fR ++structure. Note that IPv6 interface return IPv4 RRset. ++.PP ++.nf ++struct dns_query * ++\fBdns_submit_a4dnsbl\fR(\fIctx\fR, ++ const struct in_addr *\fIaddr\fR, const char *\fIdnsbl\fR, ++ dns_query_a4_fn *\fIcbck\fR, void *\fIdata\fR); ++struct dns_query * ++\fBdns_submit_a4dnsbl_txt\fR(\fIctx\fR, ++ const struct in_addr *\fIaddr\fR, const char *\fIdnsbl\fR, ++ dns_query_txt_fn *\fIcbck\fR, void *\fIdata\fR); ++struct dns_query * ++\fBdns_submit_a6dnsbl\fR(\fIctx\fR, ++ const struct in6_addr *\fIaddr\fR, const char *\fIdnsbl\fR, ++ dns_query_a4_fn *\fIcbck\fR, void *\fIdata\fR); ++struct dns_query * ++\fBdns_submit_a6dnsbl_txt\fR(\fIctx\fR, ++ const struct in6_addr *\fIaddr\fR, const char *\fIdnsbl\fR, ++ dns_query_txt_fn *\fIcbck\fR, void *\fIdata\fR); ++struct dns_rr_a4 *\fBdns_resolve_a4dnsbl\fR(\fIctx\fR, ++ const struct in_addr *\fIaddr\fR, const char *\fIdnsbl\fR) ++struct dns_rr_txt *\fBdns_resolve_a4dnsbl_txt\fR(\fIctx\fR, ++ const struct in_addr *\fIaddr\fR, const char *\fIdnsbl\fR) ++struct dns_rr_a4 *\fBdns_resolve_a6dnsbl\fR(\fIctx\fR, ++ const struct in6_addr *\fIaddr\fR, const char *\fIdnsbl\fR) ++struct dns_rr_txt *\fBdns_resolve_a6dnsbl_txt\fR(\fIctx\fR, ++ const struct in6_addr *\fIaddr\fR, const char *\fIdnsbl\fR) ++.fi ++Perform (submit or resolve) a DNSBL query for the given \fIdnsbl\fR ++domain and an IP \fIaddr\fR in question, requesting either A or TXT ++records. ++ ++.SS "RHSBL Interface" ++.PP ++RHSBL is similar to DNSBL, but instead of an IP address, the ++parameter is a domain name. ++.PP ++.nf ++struct dns_query * ++\fBdns_submit_rhsbl\fR(\fIctx\fR, const char *\fIname\fR, const char *\fIrhsbl\fR, ++ dns_query_a4_fn *\fIcbck\fR, void *\fIdata\fR); ++struct dns_query * ++\fBdns_submit_rhsbl_txt\fR(\fIctx\fR, const char *\fIname\fR, const char *\fIrhsbl\fR, ++ dns_query_txt_fn *\fIcbck\fR, void *\fIdata\fR); ++struct dns_rr_a4 * ++\fBdns_resolve_rhsbl\fR(\fIctx\fR, const char *\fIname\fR, const char *\fIrhsbl\fR); ++struct dns_rr_txt * ++\fBdns_resolve_rhsbl_txt\fR(\fIctx\fR, const char *\fIname\fR, const char *\fIrhsbl\fR); ++.fi ++Perform (submit or resolve) a RHSBL query for the given \fIrhsbl\fR ++domain and \fIname\fR in question, requesting either A or TXT records. ++ ++ ++.SH "LOW-LEVEL INTERFACE" ++ ++.SS "Domain Names (DNs)" ++ ++.PP ++A DN is a series of domain name labels each starts with length byte, ++followed by empty label (label with zero length). The following ++routines to work with DNs are provided. ++ ++.PP ++.nf ++unsigned \fBdns_dnlen\fR(const unsigned char *\fIdn\fR) ++.fi ++.RS ++return length of the domain name \fIdn\fR, including the terminating label. ++.RE ++ ++.PP ++.nf ++unsigned \fBdns_dnlabels\fR(const unsigned char *\fIdn\fR) ++.fi ++.RS ++return number of non-zero labels in domain name \fIdn\fR. ++.RE ++ ++.PP ++.nf ++unsigned \fBdns_dnequal\fR(\fIdn1\fR, \fIdn2\fR) ++ const unsigned char *\fIdn1\fR, *\fIdn2\fR; ++.fi ++.RS ++test whenever the two domain names, \fIdn1\fR and \fIdn2\fR, are ++equal (case-insensitive). Return domain name length if equal ++or 0 if not. ++.RE ++ ++.PP ++.nf ++unsigned \fBdns_dntodn\fR(\fIsdn\fR, \fIddn\fR, \fIdnsiz\fR) ++ const unsigned char *\fIsdn\fR; ++ unsigned char *\fIddn\fR; ++ unsigned \fIdnsiz\fR; ++.fi ++.RS ++copies the source domain name \fIsdn\fR to destination buffer \fIddn\fR ++of size \fIdnsiz\fR. Return domain name length or 0 if \fIddn\fR is ++too small. ++.RE ++ ++.PP ++.nf ++int \fBdns_ptodn\fR(\fIname\fR, \fInamelen\fR, \fIdn\fR, \fIdnsiz\fR, \fIisabs\fR) ++int \fBdns_sptodn\fR(\fIname\fR, \fIdn\fR, \fIdnsiz\fR) ++ const char *\fIname\fR; unsigned \fInamelen\fR; ++ unsigned char *\fIdn\fR; unsigned \fIdnsiz\fR; ++ int *\fIisabs\fR; ++.fi ++.RS ++convert asciiz name \fIname\fR of length \fInamelen\fR to DN format, ++placing result into buffer \fIdn\fR of size \fIdnsiz\fR. Return ++length of the DN if successeful, 0 if the \fIdn\fR buffer supplied is ++too small, or negative value if \fIname\fR is invalid. If \fIisabs\fR ++is non-NULL and conversion was successeful, *\fIisabs\fR will be set to ++either 1 or 0 depending whenever \fIname\fR was absolute (i.e. ending with ++a dot) or not. Name length, \fInamelength\fR, may be zero, in which case ++strlen(\fIname\fR) will be used. Second form, \fBdns_sptodn\fR(), is a ++simplified form of \fBdns_ptodn\fR(), equivalent to ++.br ++.nf ++\fBdns_ptodn\fR(\fIname\fR, 0, \fIdn\fR, \fIdnlen\fR, 0). ++.fi ++.RE ++ ++.PP ++.nf ++extern const unsigned char \fBdns_inaddr_arpa_dn\fR[] ++int \fBdns_a4todn\fR(const struct in_addr *\fIaddr\fR, const unsigned char *\fItdn\fR, ++ unsigned char *\fIdn\fR, unsigned \fIdnsiz\fR) ++int \fBdns_a4ptodn\fR(const struct in_addr *\fIaddr\fR, const char *\fItname\fR, ++ unsigned char *\fIdn\fR, unsigned \fIdnsiz\fR) ++extern const unsigned char \fBdns_ip6_arpa_dn\fR[] ++int \fBdns_a6todn\fR(const struct in6_addr *\fIaddr\fR, const unsigned char *\fItdn\fR, ++ unsigned char *\fIdn\fR, unsigned \fIdnsiz\fR) ++int \fBdns_a6ptodn\fR(const struct in6_addr *\fIaddr\fR, const char *\fItname\fR, ++ unsigned char *\fIdn\fR, unsigned \fIdnsiz\fR) ++.fi ++.RS ++several variants of routines to convert IPv4 and IPv6 address \fIaddr\fR ++into reverseDNS-like domain name in DN format, storing result in \fIdn\fR ++of size \fIdnsiz\fR. \fItdn\fR (or \fItname\fR) is the base zone name, ++like in-addr.arpa for IPv4 or in6.arpa for IPv6. If \fItdn\fR (or \fItname\fR) ++is NULL, \fBdns_inaddr_arpa_dn\fR (or \fBdns_ip6_arpa_dn\fR) will be used. ++The routines may be used to construct a DN for a DNSBL lookup for example. ++All routines return length of the resulting DN on success, -1 if resulting ++DN is invalid, or 0 if the \fIdn\fR buffer (\fIdnsiz\fR) is too small. ++To hold standard rDNS DN, a buffer of size \fBDNS_A4RSIZE\fR (30 bytes) for ++IPv4 address, or \fBDNS_A6RSIZE\fR (74 bytes) for IPv6 address, is sufficient. ++.RE ++ ++.PP ++.nf ++int \fBdns_dntop\fR(\fIdn\fR, \fIname\fR, \fInamesiz\fR) ++ const unsigned char *\fIdn\fR; ++ const char *\fIname\fR; unsigned \fInamesiz\fR; ++.fi ++.RS ++convert domain name \fIdn\fR in DN format to asciiz string, placing result ++into \fIname\fR buffer of size \fInamesiz\fR. Maximum length of asciiz ++representation of domain name is \fBDNS_MAXNAME\fR (1024) bytes. Root ++domain is represented as empty string. Return length of the resulting name ++(including terminating character, i.e. strlen(name)+1) on success, 0 if the ++\fIname\fR buffer is too small, or negative value if \fIdn\fR is invalid ++(last case should never happen since all routines in this library which ++produce domain names ensure the DNs generated are valid). ++.RE ++ ++.PP ++.nf ++const char *\fBdns_dntosp\fR(const unsigned char *\fIdn\fR) ++.fi ++.RS ++convert domain name \fIdn\fR in DN format to asciiz string using static ++buffer. Return the resulting asciiz string on success or NULL on failure. ++Note since this routine uses static buffer, it is not thread-safe. ++.RE ++ ++.PP ++.nf ++unsigned \fBdns_dntop_size\fR(const unsigned char *\fIdn\fR) ++.fi ++.RS ++return the buffer size needed to convert the \fIdn\fR domain name ++in DN format to asciiz string, for \fBdns_dntop\fR(). The routine ++return either the size of buffer required, including the trailing ++zero byte, or 0 if \fIdn\fR is invalid. ++.RE ++ ++.SS "Working with DNS Packets" ++ ++.PP ++The following routines are provided to encode and decode DNS on-wire ++packets. This is low-level interface. ++ ++.PP ++DNS response codes (returned by \fBdns_rcode\fR() routine) are ++defined as constants prefixed with \fBDNS_R_\fR. See udns.h ++header file for the complete list. In particular, constants ++\fBDNS_R_NOERROR\fR (0), \fBDNS_R_SERVFAIL\fR, \fBDNS_R_NXDOMAIN\fR ++may be of interest to an application. ++ ++.PP ++.nf ++unsigned \fBdns_get16\fR(const unsigned char *\fIp\fR) ++unsigned \fBdns_get32\fR(const unsigned char *\fIp\fR) ++.fi ++.RS ++helper routines, convert 16-bit or 32-bit integer in on-wire ++format pointed to by \fIp\fR to unsigned. ++.RE ++ ++.PP ++.nf ++unsigned char *\fBdns_put16\fR(unsigned char *\fId\fR, unsigned \fIn\fR) ++unsigned char *\fBdns_put32\fR(unsigned char *\fId\fR, unsigned \fIn\fR) ++.fi ++.RS ++helper routine, convert unsigned 16-bit or 32-bit integer \fIn\fR to ++on-wire format to buffer pointed to by \fId\fR, return \fId\fR+2 or ++\fId\fR+4. ++.RE ++ ++.PP ++.nf ++\fBDNS_HSIZE\fR (12) ++.fi ++.RS ++defines size of DNS header. Data section ++in the DNS packet immediately follows the header. In the header, ++there are query identifier (id), various flags and codes, ++and number of resource records in various data sections. ++See udns.h header file for complete list of DNS header definitions. ++.RE ++ ++.PP ++.nf ++unsigned \fBdns_qid\fR(const unsigned char *\fIpkt\fR) ++int \fBdns_rd\fR(const unsigned char *\fIpkt\fR) ++int \fBdns_tc\fR(const unsigned char *\fIpkt\fR) ++int \fBdns_aa\fR(const unsigned char *\fIpkt\fR) ++int \fBdns_qr\fR(const unsigned char *\fIpkt\fR) ++int \fBdns_ra\fR(const unsigned char *\fIpkt\fR) ++unsigned \fBdns_opcode\fR(const unsigned char *\fIpkt\fR) ++unsigned \fBdns_rcode\fR(const unsigned char *\fIpkt\fR) ++unsigned \fBdns_numqd\fR(const unsigned char *\fIpkt\fR) ++unsigned \fBdns_numan\fR(const unsigned char *\fIpkt\fR) ++unsigned \fBdns_numns\fR(const unsigned char *\fIpkt\fR) ++unsigned \fBdns_numar\fR(const unsigned char *\fIpkt\fR) ++const unsigned char *\fBdns_payload\fR(const unsigned char *\fIpkt\fR) ++.fi ++.RS ++return various parts from the DNS packet header \fIpkt\fR: ++query identifier (qid), ++recursion desired (rd) flag, ++truncation occured (tc) flag, ++authoritative answer (aa) flag, ++query response (qr) flag, ++recursion available (ra) flag, ++operation code (opcode), ++result code (rcode), ++number of entries in question section (numqd), ++number of answers (numan), ++number of authority records (numns), ++number of additional records (numar), ++and the pointer to the packet data (payload). ++.RE ++ ++.PP ++.nf ++int \fBdns_getdn\fR(\fIpkt\fR, \fIcurp\fR, \fIpkte\fR, \fIdn\fR, \fIdnsiz\fR) ++const unsigned char *\fBdns_skipdn\fR(\fIcur\fR, \fIpkte\fR) ++ const unsigned char *\fIpkt\fR, *\fIpkte\fR, **\fIcurp\fR, *\fIcur\fR; ++ unsigned char *\fIdn\fR; unsigned \fIdnsiz\fR; ++.fi ++.RS ++\fBdns_getdn\fR() extract DN from DNS packet \fIpkt\fR which ends before ++\fIpkte\fR starting at position *\fIcurp\fR into buffer pointed to by ++\fIdn\fR of size \fIdnsiz\fR. Upon successeful completion, *\fIcurp\fR ++will point to the next byte in the packet after the extracted domain name. ++It return positive number (length of the DN if \fIdn\fR) upon successeful ++completion, negative value on error (when the packet contains invalid data), ++or zero if the \fIdnsiz\fR is too small (maximum length of a domain name is ++\fBDNS_MAXDN\fR). \fBdns_skipdn\fR() return pointer to the next byte in ++DNS packet which ends up before \fIpkte\fR after a domain name which starts ++at the \fIcur\fP byte, or NULL if the packet is invalid. \fBdns_skipdn\fR() ++is more or less equivalent to what \fBdns_getdn\fR() does, except it does not ++actually extract the domain name in question, and uses simpler interface. ++.RE ++ ++.PP ++.nf ++struct \fBdns_rr\fR { ++ unsigned char \fBdnsrr_dn\fR[DNS_MAXDN]; /* the RR DN name */ ++ enum dns_class \fBdnsrr_cls\fR; /* class of the RR */ ++ enum dns_type \fBdnsrr_typ\fR; /* type of the RR */ ++ unsigned \fBdnsrr_ttl\fR; /* TTL value */ ++ unsigned \fBdnsrr_dsz\fR; /* size of data in bytes */ ++ const unsigned char *\fBdnsrr_dptr\fR; /* pointer to the first data byte */ ++ const unsigned char *\fBdnsrr_dend\fR; /* next byte after RR */ ++}; ++.fi ++.RS ++The \fBdns_rr\fR structure is used to hold information about ++single DNS Resource Record (RR) in an easy to use form. ++.RE ++ ++.PP ++.nf ++struct \fBdns_parse\fR { ++ const unsigned char *\fBdnsp_pkt\fR; /* pointer to the packet being parsed */ ++ const unsigned char *\fBdnsp_end\fR; /* end of the packet pointer */ ++ const unsigned char *\fBdnsp_cur\fR; /* current packet positionn */ ++ const unsigned char *\fBdnsp_ans\fR; /* pointer to the answer section */ ++ int \fBdnsp_rrl\fR; /* number of RRs left */ ++ int \fBdnsp_nrr\fR; /* number of relevant RRs seen so far */ ++ unsigned \fBdnsp_ttl\fR; /* TTL value so far */ ++ const unsigned char *\fBdnsp_qdn\fR; /* the domain of interest or NULL */ ++ enum dns_class \fBdnsp_qcls\fR; /* class of interest or 0 for any */ ++ enum dns_type \fBdnsp_qtyp\fR; /* type of interest or 0 for any */ ++ unsigned char \fBdnsp_dnbuf\fR[DNS_MAXDN]; /* domain name buffer */ ++}; ++.fi ++.RS ++The \fBdns_parse\fR structure is used to parse DNS reply packet. ++It holds information about the packet being parsed (dnsp_pkt, dnsp_end and ++dnsp_cur fields), number of RRs in the current section left to do, and ++the information about specific RR which we're looking for (dnsp_qdn, ++dnsp_qcls and dnsp_qtyp fields). ++.RE ++ ++.PP ++.nf ++int \fBdns_initparse\fR(struct dns_parse *\fIp\fR, ++ const unsigned char *\fIqdn\fR, ++ const unsigned char *\fIpkt\fR, ++ const unsigned char *\fIcur\fR, ++ const unsigned char *\fIend\fR) ++.fi ++.RS ++initializes the RR parsing structure \fIp\fR. Arguments \fIpkt\fR, \fIcur\fR ++and \fIend\fR should describe the received packet: \fIpkt\fR is the start of ++the packet, \fIend\fR points to the next byte after the end of the packet, ++and \fIcur\fR points past the query DN in query section (to query class+type ++information). And \fIqdn\fR points to the query DN. This is the arguments ++passed to \fBdns_parse_fn\fR() routine. \fBdns_initparse\fR() initializes ++\fBdnsp_pkt\fR, \fBdnsp_end\fR and \fBdnsp_qdn\fR fields to the corresponding ++arguments, extracts and initializes \fBdnsp_qcls\fR and \fBdnsp_qtyp\fR ++fields to the values found at \fIcur\fR pointer, initializes ++\fBdnsp_cur\fR and \fBdnsp_ans\fR fields to be \fIcur\fR+4 (to the start of ++answer section), and initializes \fBdnsp_rrl\fR field to be number of entries ++in answer section. \fBdnsp_ttl\fR will be set to max TTL value, 0xffffffff, ++and \fBdnsp_nrr\fR to 0. ++.RE ++ ++.PP ++.nf ++int \fBdns_nextrr\fR(struct dns_parse *\fIp\fR, struct dns_rr *\fIrr\fR); ++.fi ++.RS ++searches for next RR in the packet based on the criteria provided in ++the \fIp\fR structure, filling in the \fIrr\fR structure and ++advancing \fIp\fR->\fBdnsp_cur\fR to the next RR in the packet. ++RR selection is based on dnsp_qdn, dnsp_qcls and dnsp_qtyp fields in ++the dns_parse structure. Any (or all) of the 3 fields may be 0, ++which means any actual value from the packet is acceptable. In case ++the field isn't 0 (or NULL for dnsp_qdn), only RRs with corresponding ++characteristics are acceptable. Additionally, when dnsp_qdn is non-NULL, ++\fBdns_nextrr\fR() performs automatic CNAME expansion. ++Routine will return positive value on success, 0 in case it reached the end ++of current section in the packet (\fIp\fR->\fBdnsp_rrl\fR is zero), or ++negative value if next RR can not be decoded (packet format is invalid). ++The routine updates \fIp\fR->\fBdnsp_qdn\fR automatically when this ++field is non-NULL and it encounters appropriate CNAME RRs (saving CNAME ++target in \fIp\fR->\fBdnsp_dnbuf\fR), so after end of the process, ++\fIp\fR->\fBdnsp_qdn\fR will point to canonical name of the domain ++in question. The routine updates \fIp\fR->\fBdnsp_ttl\fR value to ++be the minimum TTL of all RRs found. ++.RE ++ ++.PP ++.nf ++void \fBdns_rewind\fR(struct dns_parse *\fIp\fR, const unsigned char *\fIqdn\fR) ++.fi ++.RS ++this routine "rewinds" the packet parse state structure to be at the ++same state as after a call to \fBdns_initparse\fR(), i.e. reposition ++the parse structure \fIp\fR to the start of answer section and ++initialize \fIp\fR->\fBdnsp_rrl\fR to the number of entries in ++answer section. ++.RE ++ ++.PP ++.nf ++int \fBdns_stdrr_size\fR(const struct dns_parse *\fIp\fR); ++.fi ++.RS ++return size to hold standard RRset structure information, as shown ++in \fBdns_rr_null\fR structure (for the query and canonical ++names). Used to calculate amount of memory to allocate for common ++part of type-specific RR structures in parsing routines. ++.RE ++ ++.PP ++.nf ++void *\fBdns_stdrr_finish\fR(struct dns_rr_null *\fIret\fR, char *\fIcp\fR, ++ const struct dns_parse *\fIp\fR); ++.fi ++.RS ++initializes standard RRset fields in \fIret\fR structure using buffer ++pointed to by \fIcp\fR, which should have at least as many bytes ++as \fBdns_stdrr_size\fR(\fIp\fR) returned. Used to finalize common ++part of type-specific RR structures in parsing routines. ++.RE ++ ++.PP ++See library source for usage examples of all the above low-level routines, ++especially source of the parsing routines. ++ ++.SS "Auxilary Routines" ++ ++.PP ++.nf ++int \fBdns_pton\fR(int \fIaf\fR, const char *\fIsrc\fR, void *\fIdst\fR); ++.fi ++.RS ++privides functionality similar to standard \fBinet_pton\fR() routine, ++to convert printable representation of an IP address of family \fIaf\fR ++(either \fBAF_INET\fR or \fBAF_INET6\fR) pointed to by \fIsrc\fR into ++binary form suitable for socket addresses and transmission over network, ++in buffer pointed to by \fIdst\fR. The destination buffer should be ++of size 4 for \fBAF_INET\fR family or 16 for \fBAF_INET6\fR. ++The return value is positive on success, 0 if \fIsrc\fR is not a valid text ++representation of an address of family \fIaf\fR, or negative if the ++given address family is not supported. ++.RE ++ ++.PP ++.nf ++const char *\fBdns_ntop\fR(int \fIaf\fR, const void *\fIsrc\fR, ++ char *\fIdst\fR, int \fIdstsize\fR) ++.fi ++.RS ++privides functionality similar to standard \fBinet_ntop\fR() routine, ++to convert binary representation of an IP address of family \fIaf\fR ++(either \fBAF_INET\fR or \fBAF_INET6\fR) pointed to by \fIsrc\fR ++(either 4 or 16 bytes) into printable form in buffer in buffer pointed ++to by \fIdst\fR of size \fIdstsize\fR. The destination buffer should be ++at least of size 16 bytes for \fBAF_INET\fR family or 46 bytes for ++\fBAF_INET6\fR. The return value is either \fIdst\fR, or NULL pointer ++if \fIdstsize\fR is too small to hold this address or if the given ++address family is not supported. ++.RE ++ ++.SH AUTHOR ++.PP ++The \fBudns\fR library has been written by Michael Tokarev, mjt+udns@tls.msk.ru. ++ ++.SH VERSION ++.PP ++This manual page corresponds to udns version 0.4, released Jan-2014. +diff --git a/libudns/udns.h b/libudns/udns.h +new file mode 100644 +index 0000000..dabee0d +--- /dev/null ++++ b/libudns/udns.h +@@ -0,0 +1,778 @@ ++/* udns.h ++ header file for the UDNS library. ++ ++ Copyright (C) 2005 Michael Tokarev ++ This file is part of UDNS library, an async DNS stub resolver. ++ ++ This library is free software; you can redistribute it and/or ++ modify it under the terms of the GNU Lesser General Public ++ License as published by the Free Software Foundation; either ++ version 2.1 of the License, or (at your option) any later version. ++ ++ This library is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ Lesser General Public License for more details. ++ ++ You should have received a copy of the GNU Lesser General Public ++ License along with this library, in file named COPYING.LGPL; if not, ++ write to the Free Software Foundation, Inc., 59 Temple Place, ++ Suite 330, Boston, MA 02111-1307 USA ++ ++ */ ++ ++#ifndef UDNS_VERSION /* include guard */ ++ ++#define UDNS_VERSION "0.4" ++ ++#ifdef __MINGW32__ ++# ifdef UDNS_DYNAMIC_LIBRARY ++# ifdef DNS_LIBRARY_BUILD ++# define UDNS_API __declspec(dllexport) ++# define UDNS_DATA_API __declspec(dllexport) ++# else ++# define UDNS_API __declspec(dllimport) ++# define UDNS_DATA_API __declspec(dllimport) ++# endif ++# endif ++#endif ++ ++#ifndef UDNS_API ++# define UDNS_API ++#endif ++#ifndef UDNS_DATA_API ++# define UDNS_DATA_API ++#endif ++ ++#include /* for time_t */ ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++/* forward declarations if sockets stuff isn't #include'd */ ++struct in_addr; ++struct in6_addr; ++struct sockaddr; ++ ++/**************************************************************************/ ++/**************** Common definitions **************************************/ ++ ++UDNS_API const char * ++dns_version(void); ++ ++struct dns_ctx; ++struct dns_query; ++ ++/* shorthand for [const] unsigned char */ ++typedef unsigned char dnsc_t; ++typedef const unsigned char dnscc_t; ++ ++#define DNS_MAXDN 255 /* max DN length */ ++#define DNS_DNPAD 1 /* padding for DN buffers */ ++#define DNS_MAXLABEL 63 /* max DN label length */ ++#define DNS_MAXNAME 1024 /* max asciiz domain name length */ ++#define DNS_HSIZE 12 /* DNS packet header size */ ++#define DNS_PORT 53 /* default domain port */ ++#define DNS_MAXSERV 6 /* max servers to consult */ ++#define DNS_MAXPACKET 512 /* max traditional-DNS UDP packet size */ ++#define DNS_EDNS0PACKET 4096 /* EDNS0 packet size to use */ ++ ++enum dns_class { /* DNS RR Classes */ ++ DNS_C_INVALID = 0, /* invalid class */ ++ DNS_C_IN = 1, /* Internet */ ++ DNS_C_CH = 3, /* CHAOS */ ++ DNS_C_HS = 4, /* HESIOD */ ++ DNS_C_ANY = 255 /* wildcard */ ++}; ++ ++enum dns_type { /* DNS RR Types */ ++ DNS_T_INVALID = 0, /* Cookie. */ ++ DNS_T_A = 1, /* Host address. */ ++ DNS_T_NS = 2, /* Authoritative server. */ ++ DNS_T_MD = 3, /* Mail destination. */ ++ DNS_T_MF = 4, /* Mail forwarder. */ ++ DNS_T_CNAME = 5, /* Canonical name. */ ++ DNS_T_SOA = 6, /* Start of authority zone. */ ++ DNS_T_MB = 7, /* Mailbox domain name. */ ++ DNS_T_MG = 8, /* Mail group member. */ ++ DNS_T_MR = 9, /* Mail rename name. */ ++ DNS_T_NULL = 10, /* Null resource record. */ ++ DNS_T_WKS = 11, /* Well known service. */ ++ DNS_T_PTR = 12, /* Domain name pointer. */ ++ DNS_T_HINFO = 13, /* Host information. */ ++ DNS_T_MINFO = 14, /* Mailbox information. */ ++ DNS_T_MX = 15, /* Mail routing information. */ ++ DNS_T_TXT = 16, /* Text strings. */ ++ DNS_T_RP = 17, /* Responsible person. */ ++ DNS_T_AFSDB = 18, /* AFS cell database. */ ++ DNS_T_X25 = 19, /* X_25 calling address. */ ++ DNS_T_ISDN = 20, /* ISDN calling address. */ ++ DNS_T_RT = 21, /* Router. */ ++ DNS_T_NSAP = 22, /* NSAP address. */ ++ DNS_T_NSAP_PTR = 23, /* Reverse NSAP lookup (deprecated). */ ++ DNS_T_SIG = 24, /* Security signature. */ ++ DNS_T_KEY = 25, /* Security key. */ ++ DNS_T_PX = 26, /* X.400 mail mapping. */ ++ DNS_T_GPOS = 27, /* Geographical position (withdrawn). */ ++ DNS_T_AAAA = 28, /* Ip6 Address. */ ++ DNS_T_LOC = 29, /* Location Information. */ ++ DNS_T_NXT = 30, /* Next domain (security). */ ++ DNS_T_EID = 31, /* Endpoint identifier. */ ++ DNS_T_NIMLOC = 32, /* Nimrod Locator. */ ++ DNS_T_SRV = 33, /* Server Selection. */ ++ DNS_T_ATMA = 34, /* ATM Address */ ++ DNS_T_NAPTR = 35, /* Naming Authority PoinTeR */ ++ DNS_T_KX = 36, /* Key Exchange */ ++ DNS_T_CERT = 37, /* Certification record */ ++ DNS_T_A6 = 38, /* IPv6 address (deprecates AAAA) */ ++ DNS_T_DNAME = 39, /* Non-terminal DNAME (for IPv6) */ ++ DNS_T_SINK = 40, /* Kitchen sink (experimentatl) */ ++ DNS_T_OPT = 41, /* EDNS0 option (meta-RR) */ ++ DNS_T_DS = 43, /* DNSSEC */ ++ DNS_T_SSHFP = 44, ++ DNS_T_IPSECKEY = 45, ++ DNS_T_RRSIG = 46, /* DNSSEC */ ++ DNS_T_NSEC = 47, /* DNSSEC */ ++ DNS_T_DNSKEY = 48, ++ DNS_T_DHCID = 49, ++ DNS_T_NSEC3 = 50, ++ DNS_T_NSEC3PARAMS = 51, ++ DNS_T_TALINK = 58, /* draft-ietf-dnsop-trust-history */ ++ DNS_T_SPF = 99, ++ DNS_T_UINFO = 100, ++ DNS_T_UID = 101, ++ DNS_T_GID = 102, ++ DNS_T_UNSPEC = 103, ++ DNS_T_TSIG = 250, /* Transaction signature. */ ++ DNS_T_IXFR = 251, /* Incremental zone transfer. */ ++ DNS_T_AXFR = 252, /* Transfer zone of authority. */ ++ DNS_T_MAILB = 253, /* Transfer mailbox records. */ ++ DNS_T_MAILA = 254, /* Transfer mail agent records. */ ++ DNS_T_ANY = 255, /* Wildcard match. */ ++ DNS_T_ZXFR = 256, /* BIND-specific, nonstandard. */ ++ DNS_T_DLV = 32769, /* RFC 4431, 5074, DNSSEC Lookaside Validation */ ++ DNS_T_MAX = 65536 ++}; ++ ++/**************************************************************************/ ++/**************** Domain Names (DNs) **************************************/ ++ ++/* return length of the DN */ ++UDNS_API unsigned ++dns_dnlen(dnscc_t *dn); ++ ++/* return #of labels in a DN */ ++UDNS_API unsigned ++dns_dnlabels(dnscc_t *dn); ++ ++/* lower- and uppercase single DN char */ ++#define DNS_DNLC(c) ((c) >= 'A' && (c) <= 'Z' ? (c) - 'A' + 'a' : (c)) ++#define DNS_DNUC(c) ((c) >= 'a' && (c) <= 'z' ? (c) - 'a' + 'A' : (c)) ++ ++/* compare the DNs, return dnlen of equal or 0 if not */ ++UDNS_API unsigned ++dns_dnequal(dnscc_t *dn1, dnscc_t *dn2); ++ ++/* copy one DN to another, size checking */ ++UDNS_API unsigned ++dns_dntodn(dnscc_t *sdn, dnsc_t *ddn, unsigned ddnsiz); ++ ++/* convert asciiz string of length namelen (0 to use strlen) to DN */ ++UDNS_API int ++dns_ptodn(const char *name, unsigned namelen, ++ dnsc_t *dn, unsigned dnsiz, int *isabs); ++ ++/* simpler form of dns_ptodn() */ ++#define dns_sptodn(name,dn,dnsiz) dns_ptodn((name),0,(dn),(dnsiz),0) ++ ++UDNS_DATA_API extern dnscc_t dns_inaddr_arpa_dn[14]; ++#define DNS_A4RSIZE 30 ++UDNS_API int ++dns_a4todn(const struct in_addr *addr, dnscc_t *tdn, ++ dnsc_t *dn, unsigned dnsiz); ++UDNS_API int ++dns_a4ptodn(const struct in_addr *addr, const char *tname, ++ dnsc_t *dn, unsigned dnsiz); ++UDNS_API dnsc_t * ++dns_a4todn_(const struct in_addr *addr, dnsc_t *dn, dnsc_t *dne); ++ ++UDNS_DATA_API extern dnscc_t dns_ip6_arpa_dn[10]; ++#define DNS_A6RSIZE 74 ++UDNS_API int ++dns_a6todn(const struct in6_addr *addr, dnscc_t *tdn, ++ dnsc_t *dn, unsigned dnsiz); ++UDNS_API int ++dns_a6ptodn(const struct in6_addr *addr, const char *tname, ++ dnsc_t *dn, unsigned dnsiz); ++UDNS_API dnsc_t * ++dns_a6todn_(const struct in6_addr *addr, dnsc_t *dn, dnsc_t *dne); ++ ++/* convert DN into asciiz string */ ++UDNS_API int ++dns_dntop(dnscc_t *dn, char *name, unsigned namesiz); ++ ++/* convert DN into asciiz string, using static buffer (NOT thread-safe!) */ ++UDNS_API const char * ++dns_dntosp(dnscc_t *dn); ++ ++/* return buffer size (incl. null byte) required for asciiz form of a DN */ ++UDNS_API unsigned ++dns_dntop_size(dnscc_t *dn); ++ ++/* either wrappers or reimplementations for inet_ntop() and inet_pton() */ ++UDNS_API const char *dns_ntop(int af, const void *src, char *dst, int size); ++UDNS_API int dns_pton(int af, const char *src, void *dst); ++ ++/**************************************************************************/ ++/**************** DNS raw packet layout ***********************************/ ++ ++enum dns_rcode { /* reply codes */ ++ DNS_R_NOERROR = 0, /* ok, no error */ ++ DNS_R_FORMERR = 1, /* format error */ ++ DNS_R_SERVFAIL = 2, /* server failed */ ++ DNS_R_NXDOMAIN = 3, /* domain does not exists */ ++ DNS_R_NOTIMPL = 4, /* not implemented */ ++ DNS_R_REFUSED = 5, /* query refused */ ++ /* these are for BIND_UPDATE */ ++ DNS_R_YXDOMAIN = 6, /* Name exists */ ++ DNS_R_YXRRSET = 7, /* RRset exists */ ++ DNS_R_NXRRSET = 8, /* RRset does not exist */ ++ DNS_R_NOTAUTH = 9, /* Not authoritative for zone */ ++ DNS_R_NOTZONE = 10, /* Zone of record different from zone section */ ++ /*ns_r_max = 11,*/ ++ /* The following are TSIG extended errors */ ++ DNS_R_BADSIG = 16, ++ DNS_R_BADKEY = 17, ++ DNS_R_BADTIME = 18 ++}; ++ ++static __inline unsigned dns_get16(dnscc_t *s) { ++ return ((unsigned)s[0]<<8) | s[1]; ++} ++static __inline unsigned dns_get32(dnscc_t *s) { ++ return ((unsigned)s[0]<<24) | ((unsigned)s[1]<<16) ++ | ((unsigned)s[2]<<8) | s[3]; ++} ++static __inline dnsc_t *dns_put16(dnsc_t *d, unsigned n) { ++ *d++ = (dnsc_t)((n >> 8) & 255); *d++ = (dnsc_t)(n & 255); return d; ++} ++static __inline dnsc_t *dns_put32(dnsc_t *d, unsigned n) { ++ *d++ = (dnsc_t)((n >> 24) & 255); *d++ = (dnsc_t)((n >> 16) & 255); ++ *d++ = (dnsc_t)((n >> 8) & 255); *d++ = (dnsc_t)(n & 255); ++ return d; ++} ++ ++/* DNS Header layout */ ++enum { ++ /* bytes 0:1 - query ID */ ++ DNS_H_QID1 = 0, ++ DNS_H_QID2 = 1, ++ DNS_H_QID = DNS_H_QID1, ++#define dns_qid(pkt) dns_get16((pkt)+DNS_H_QID) ++ /* byte 2: flags1 */ ++ DNS_H_F1 = 2, ++ DNS_HF1_QR = 0x80, /* query response flag */ ++#define dns_qr(pkt) ((pkt)[DNS_H_F1]&DNS_HF1_QR) ++ DNS_HF1_OPCODE = 0x78, /* opcode, 0 = query */ ++#define dns_opcode(pkt) (((pkt)[DNS_H_F1]&DNS_HF1_OPCODE)>>3) ++ DNS_HF1_AA = 0x04, /* auth answer */ ++#define dns_aa(pkt) ((pkt)[DNS_H_F1]&DNS_HF1_AA) ++ DNS_HF1_TC = 0x02, /* truncation flag */ ++#define dns_tc(pkt) ((pkt)[DNS_H_F1]&DNS_HF1_TC) ++ DNS_HF1_RD = 0x01, /* recursion desired (may be set in query) */ ++#define dns_rd(pkt) ((pkt)[DNS_H_F1]&DNS_HF1_RD) ++ /* byte 3: flags2 */ ++ DNS_H_F2 = 3, ++ DNS_HF2_RA = 0x80, /* recursion available */ ++#define dns_ra(pkt) ((pkt)[DNS_H_F2]&DNS_HF2_RA) ++ DNS_HF2_Z = 0x40, /* reserved */ ++ DNS_HF2_AD = 0x20, /* DNSSEC: authentic data */ ++#define dns_ad(pkt) ((pkt)[DNS_H_F2]&DNS_HF2_AD) ++ DNS_HF2_CD = 0x10, /* DNSSEC: checking disabled */ ++#define dns_cd(pkt) ((pkt)[DNS_H_F2]&DNS_HF2_CD) ++ DNS_HF2_RCODE = 0x0f, /* response code, DNS_R_XXX above */ ++#define dns_rcode(pkt) ((pkt)[DNS_H_F2]&DNS_HF2_RCODE) ++ /* bytes 4:5: qdcount, numqueries */ ++ DNS_H_QDCNT1 = 4, ++ DNS_H_QDCNT2 = 5, ++ DNS_H_QDCNT = DNS_H_QDCNT1, ++#define dns_numqd(pkt) dns_get16((pkt)+4) ++ /* bytes 6:7: ancount, numanswers */ ++ DNS_H_ANCNT1 = 6, ++ DNS_H_ANCNT2 = 7, ++ DNS_H_ANCNT = DNS_H_ANCNT1, ++#define dns_numan(pkt) dns_get16((pkt)+6) ++ /* bytes 8:9: nscount, numauthority */ ++ DNS_H_NSCNT1 = 8, ++ DNS_H_NSCNT2 = 9, ++ DNS_H_NSCNT = DNS_H_NSCNT1, ++#define dns_numns(pkt) dns_get16((pkt)+8) ++ /* bytes 10:11: arcount, numadditional */ ++ DNS_H_ARCNT1 = 10, ++ DNS_H_ARCNT2 = 11, ++ DNS_H_ARCNT = DNS_H_ARCNT1, ++#define dns_numar(pkt) dns_get16((pkt)+10) ++#define dns_payload(pkt) ((pkt)+DNS_HSIZE) ++ /* EDNS0 (OPT RR) flags (Ext. Flags) */ ++ DNS_EF1_DO = 0x80, /* DNSSEC OK */ ++}; ++ ++/* packet buffer: start at pkt, end before pkte, current pos *curp. ++ * extract a DN and set *curp to the next byte after DN in packet. ++ * return -1 on error, 0 if dnsiz is too small, or dnlen on ok. ++ */ ++UDNS_API int ++dns_getdn(dnscc_t *pkt, dnscc_t **curp, dnscc_t *end, ++ dnsc_t *dn, unsigned dnsiz); ++ ++/* skip the DN at position cur in packet ending before pkte, ++ * return pointer to the next byte after the DN or NULL on error */ ++UDNS_API dnscc_t * ++dns_skipdn(dnscc_t *end, dnscc_t *cur); ++ ++struct dns_rr { /* DNS Resource Record */ ++ dnsc_t dnsrr_dn[DNS_MAXDN]; /* the DN of the RR */ ++ enum dns_class dnsrr_cls; /* Class */ ++ enum dns_type dnsrr_typ; /* Type */ ++ unsigned dnsrr_ttl; /* Time-To-Live (TTL) */ ++ unsigned dnsrr_dsz; /* data size */ ++ dnscc_t *dnsrr_dptr; /* pointer to start of data */ ++ dnscc_t *dnsrr_dend; /* past end of data */ ++}; ++ ++struct dns_parse { /* RR/packet parsing state */ ++ dnscc_t *dnsp_pkt; /* start of the packet */ ++ dnscc_t *dnsp_end; /* end of the packet */ ++ dnscc_t *dnsp_cur; /* current packet position */ ++ dnscc_t *dnsp_ans; /* start of answer section */ ++ int dnsp_rrl; /* number of RRs left to go */ ++ int dnsp_nrr; /* RR count so far */ ++ unsigned dnsp_ttl; /* TTL value so far */ ++ dnscc_t *dnsp_qdn; /* the RR DN we're looking for */ ++ enum dns_class dnsp_qcls; /* RR class we're looking for or 0 */ ++ enum dns_type dnsp_qtyp; /* RR type we're looking for or 0 */ ++ dnsc_t dnsp_dnbuf[DNS_MAXDN]; /* domain buffer */ ++}; ++ ++/* initialize the parse structure */ ++UDNS_API void ++dns_initparse(struct dns_parse *p, dnscc_t *qdn, ++ dnscc_t *pkt, dnscc_t *cur, dnscc_t *end); ++ ++/* search next RR, <0=error, 0=no more RRs, >0 = found. */ ++UDNS_API int ++dns_nextrr(struct dns_parse *p, struct dns_rr *rr); ++ ++UDNS_API void ++dns_rewind(struct dns_parse *p, dnscc_t *qdn); ++ ++ ++/**************************************************************************/ ++/**************** Resolver Context ****************************************/ ++ ++/* default resolver context */ ++UDNS_DATA_API extern struct dns_ctx dns_defctx; ++ ++/* reset resolver context to default state, close it if open, drop queries */ ++UDNS_API void ++dns_reset(struct dns_ctx *ctx); ++ ++/* reset resolver context and read in system configuration */ ++UDNS_API int ++dns_init(struct dns_ctx *ctx, int do_open); ++ ++/* return new resolver context with the same settings as copy */ ++UDNS_API struct dns_ctx * ++dns_new(const struct dns_ctx *copy); ++ ++/* free resolver context returned by dns_new(); all queries are dropped */ ++UDNS_API void ++dns_free(struct dns_ctx *ctx); ++ ++/* add nameserver for a resolver context (or reset nslist if serv==NULL) */ ++UDNS_API int ++dns_add_serv(struct dns_ctx *ctx, const char *serv); ++ ++/* add nameserver using struct sockaddr structure (with ports) */ ++UDNS_API int ++dns_add_serv_s(struct dns_ctx *ctx, const struct sockaddr *sa); ++ ++/* add search list element for a resolver context (or reset it if srch==NULL) */ ++UDNS_API int ++dns_add_srch(struct dns_ctx *ctx, const char *srch); ++ ++/* set options for a resolver context */ ++UDNS_API int ++dns_set_opts(struct dns_ctx *ctx, const char *opts); ++ ++enum dns_opt { /* options */ ++ DNS_OPT_FLAGS, /* flags, DNS_F_XXX */ ++ DNS_OPT_TIMEOUT, /* timeout in secounds */ ++ DNS_OPT_NTRIES, /* number of retries */ ++ DNS_OPT_NDOTS, /* ndots */ ++ DNS_OPT_UDPSIZE, /* EDNS0 UDP size */ ++ DNS_OPT_PORT, /* port to use */ ++}; ++ ++/* set or get (if val<0) an option */ ++UDNS_API int ++dns_set_opt(struct dns_ctx *ctx, enum dns_opt opt, int val); ++ ++enum dns_flags { ++ DNS_NOSRCH = 0x00010000, /* do not perform search */ ++ DNS_NORD = 0x00020000, /* request no recursion */ ++ DNS_AAONLY = 0x00040000, /* set AA flag in queries */ ++ DNS_SET_DO = 0x00080000, /* set EDNS0 "DO" bit (DNSSEC OK) */ ++ DNS_SET_CD = 0x00100000, /* set CD bit (DNSSEC: checking disabled) */ ++}; ++ ++/* set the debug function pointer */ ++typedef void ++(dns_dbgfn)(int code, const struct sockaddr *sa, unsigned salen, ++ dnscc_t *pkt, int plen, ++ const struct dns_query *q, void *data); ++UDNS_API void ++dns_set_dbgfn(struct dns_ctx *ctx, dns_dbgfn *dbgfn); ++ ++/* open and return UDP socket */ ++UDNS_API int ++dns_open(struct dns_ctx *ctx); ++ ++/* return UDP socket or -1 if not open */ ++UDNS_API int ++dns_sock(const struct dns_ctx *ctx); ++ ++/* close the UDP socket */ ++UDNS_API void ++dns_close(struct dns_ctx *ctx); ++ ++/* return number of requests queued */ ++UDNS_API int ++dns_active(const struct dns_ctx *ctx); ++ ++/* return status of the last operation */ ++UDNS_API int ++dns_status(const struct dns_ctx *ctx); ++UDNS_API void ++dns_setstatus(struct dns_ctx *ctx, int status); ++ ++/* handle I/O event on UDP socket */ ++UDNS_API void ++dns_ioevent(struct dns_ctx *ctx, time_t now); ++ ++/* process any timeouts, return time in secounds to the ++ * next timeout (or -1 if none) but not greather than maxwait */ ++UDNS_API int ++dns_timeouts(struct dns_ctx *ctx, int maxwait, time_t now); ++ ++/* define timer requesting routine to use */ ++typedef void dns_utm_fn(struct dns_ctx *ctx, int timeout, void *data); ++UDNS_API void ++dns_set_tmcbck(struct dns_ctx *ctx, dns_utm_fn *fn, void *data); ++ ++/**************************************************************************/ ++/**************** Making Queries ******************************************/ ++ ++/* query callback routine */ ++typedef void dns_query_fn(struct dns_ctx *ctx, void *result, void *data); ++ ++/* query parse routine: raw DNS => application structure */ ++typedef int ++dns_parse_fn(dnscc_t *qdn, dnscc_t *pkt, dnscc_t *cur, dnscc_t *end, ++ void **res); ++ ++enum dns_status { ++ DNS_E_NOERROR = 0, /* ok, not an error */ ++ DNS_E_TEMPFAIL = -1, /* timeout, SERVFAIL or similar */ ++ DNS_E_PROTOCOL = -2, /* got garbled reply */ ++ DNS_E_NXDOMAIN = -3, /* domain does not exists */ ++ DNS_E_NODATA = -4, /* domain exists but no data of reqd type */ ++ DNS_E_NOMEM = -5, /* out of memory while processing */ ++ DNS_E_BADQUERY = -6 /* the query is malformed */ ++}; ++ ++/* submit generic DN query */ ++UDNS_API struct dns_query * ++dns_submit_dn(struct dns_ctx *ctx, ++ dnscc_t *dn, int qcls, int qtyp, int flags, ++ dns_parse_fn *parse, dns_query_fn *cbck, void *data); ++/* submit generic name query */ ++UDNS_API struct dns_query * ++dns_submit_p(struct dns_ctx *ctx, ++ const char *name, int qcls, int qtyp, int flags, ++ dns_parse_fn *parse, dns_query_fn *cbck, void *data); ++ ++/* cancel the given async query in progress */ ++UDNS_API int ++dns_cancel(struct dns_ctx *ctx, struct dns_query *q); ++ ++/* resolve a generic query, return the answer */ ++UDNS_API void * ++dns_resolve_dn(struct dns_ctx *ctx, ++ dnscc_t *qdn, int qcls, int qtyp, int flags, ++ dns_parse_fn *parse); ++UDNS_API void * ++dns_resolve_p(struct dns_ctx *ctx, ++ const char *qname, int qcls, int qtyp, int flags, ++ dns_parse_fn *parse); ++UDNS_API void * ++dns_resolve(struct dns_ctx *ctx, struct dns_query *q); ++ ++ ++/* Specific RR handlers */ ++ ++#define dns_rr_common(prefix) \ ++ char *prefix##_cname; /* canonical name */ \ ++ char *prefix##_qname; /* original query name */ \ ++ unsigned prefix##_ttl; /* TTL value */ \ ++ int prefix##_nrr /* number of records */ ++ ++struct dns_rr_null { /* NULL RRset, aka RRset template */ ++ dns_rr_common(dnsn); ++}; ++ ++UDNS_API int ++dns_stdrr_size(const struct dns_parse *p); ++UDNS_API void * ++dns_stdrr_finish(struct dns_rr_null *ret, char *cp, const struct dns_parse *p); ++ ++struct dns_rr_a4 { /* the A RRset */ ++ dns_rr_common(dnsa4); ++ struct in_addr *dnsa4_addr; /* array of addresses, naddr elements */ ++}; ++ ++UDNS_API dns_parse_fn dns_parse_a4; /* A RR parsing routine */ ++typedef void /* A query callback routine */ ++dns_query_a4_fn(struct dns_ctx *ctx, struct dns_rr_a4 *result, void *data); ++ ++/* submit A IN query */ ++UDNS_API struct dns_query * ++dns_submit_a4(struct dns_ctx *ctx, const char *name, int flags, ++ dns_query_a4_fn *cbck, void *data); ++ ++/* resolve A IN query */ ++UDNS_API struct dns_rr_a4 * ++dns_resolve_a4(struct dns_ctx *ctx, const char *name, int flags); ++ ++ ++struct dns_rr_a6 { /* the AAAA RRset */ ++ dns_rr_common(dnsa6); ++ struct in6_addr *dnsa6_addr; /* array of addresses, naddr elements */ ++}; ++ ++UDNS_API dns_parse_fn dns_parse_a6; /* A RR parsing routine */ ++typedef void /* A query callback routine */ ++dns_query_a6_fn(struct dns_ctx *ctx, struct dns_rr_a6 *result, void *data); ++ ++/* submit AAAA IN query */ ++UDNS_API struct dns_query * ++dns_submit_a6(struct dns_ctx *ctx, const char *name, int flags, ++ dns_query_a6_fn *cbck, void *data); ++ ++/* resolve AAAA IN query */ ++UDNS_API struct dns_rr_a6 * ++dns_resolve_a6(struct dns_ctx *ctx, const char *name, int flags); ++ ++ ++struct dns_rr_ptr { /* the PTR RRset */ ++ dns_rr_common(dnsptr); ++ char **dnsptr_ptr; /* array of PTRs */ ++}; ++ ++UDNS_API dns_parse_fn dns_parse_ptr; /* PTR RR parsing routine */ ++typedef void /* PTR query callback */ ++dns_query_ptr_fn(struct dns_ctx *ctx, struct dns_rr_ptr *result, void *data); ++/* submit PTR IN in-addr.arpa query */ ++UDNS_API struct dns_query * ++dns_submit_a4ptr(struct dns_ctx *ctx, const struct in_addr *addr, ++ dns_query_ptr_fn *cbck, void *data); ++/* resolve PTR IN in-addr.arpa query */ ++UDNS_API struct dns_rr_ptr * ++dns_resolve_a4ptr(struct dns_ctx *ctx, const struct in_addr *addr); ++ ++/* the same as above, but for ip6.arpa */ ++UDNS_API struct dns_query * ++dns_submit_a6ptr(struct dns_ctx *ctx, const struct in6_addr *addr, ++ dns_query_ptr_fn *cbck, void *data); ++UDNS_API struct dns_rr_ptr * ++dns_resolve_a6ptr(struct dns_ctx *ctx, const struct in6_addr *addr); ++ ++ ++struct dns_mx { /* single MX RR */ ++ int priority; /* MX priority */ ++ char *name; /* MX name */ ++}; ++struct dns_rr_mx { /* the MX RRset */ ++ dns_rr_common(dnsmx); ++ struct dns_mx *dnsmx_mx; /* array of MXes */ ++}; ++UDNS_API dns_parse_fn dns_parse_mx; /* MX RR parsing routine */ ++typedef void /* MX RR callback */ ++dns_query_mx_fn(struct dns_ctx *ctx, struct dns_rr_mx *result, void *data); ++/* submit MX IN query */ ++UDNS_API struct dns_query * ++dns_submit_mx(struct dns_ctx *ctx, const char *name, int flags, ++ dns_query_mx_fn *cbck, void *data); ++/* resolve MX IN query */ ++UDNS_API struct dns_rr_mx * ++dns_resolve_mx(struct dns_ctx *ctx, const char *name, int flags); ++ ++ ++struct dns_txt { /* single TXT record */ ++ int len; /* length of the text */ ++ dnsc_t *txt; /* pointer to text buffer. May contain nulls. */ ++}; ++struct dns_rr_txt { /* the TXT RRset */ ++ dns_rr_common(dnstxt); ++ struct dns_txt *dnstxt_txt; /* array of TXT records */ ++}; ++UDNS_API dns_parse_fn dns_parse_txt; /* TXT RR parsing routine */ ++typedef void /* TXT RR callback */ ++dns_query_txt_fn(struct dns_ctx *ctx, struct dns_rr_txt *result, void *data); ++/* submit TXT query */ ++UDNS_API struct dns_query * ++dns_submit_txt(struct dns_ctx *ctx, const char *name, int qcls, int flags, ++ dns_query_txt_fn *cbck, void *data); ++/* resolve TXT query */ ++UDNS_API struct dns_rr_txt * ++dns_resolve_txt(struct dns_ctx *ctx, const char *name, int qcls, int flags); ++ ++ ++struct dns_srv { /* single SRV RR */ ++ int priority; /* SRV priority */ ++ int weight; /* SRV weight */ ++ int port; /* SRV port */ ++ char *name; /* SRV name */ ++}; ++struct dns_rr_srv { /* the SRV RRset */ ++ dns_rr_common(dnssrv); ++ struct dns_srv *dnssrv_srv; /* array of SRVes */ ++}; ++UDNS_API dns_parse_fn dns_parse_srv; /* SRV RR parsing routine */ ++typedef void /* SRV RR callback */ ++dns_query_srv_fn(struct dns_ctx *ctx, struct dns_rr_srv *result, void *data); ++/* submit SRV IN query */ ++UDNS_API struct dns_query * ++dns_submit_srv(struct dns_ctx *ctx, ++ const char *name, const char *srv, const char *proto, ++ int flags, dns_query_srv_fn *cbck, void *data); ++/* resolve SRV IN query */ ++UDNS_API struct dns_rr_srv * ++dns_resolve_srv(struct dns_ctx *ctx, ++ const char *name, const char *srv, const char *proto, ++ int flags); ++ ++/* NAPTR (RFC3403) RR type */ ++struct dns_naptr { /* single NAPTR RR */ ++ int order; /* NAPTR order */ ++ int preference; /* NAPTR preference */ ++ char *flags; /* NAPTR flags */ ++ char *service; /* NAPTR service */ ++ char *regexp; /* NAPTR regexp */ ++ char *replacement; /* NAPTR replacement */ ++}; ++ ++struct dns_rr_naptr { /* the NAPTR RRset */ ++ dns_rr_common(dnsnaptr); ++ struct dns_naptr *dnsnaptr_naptr; /* array of NAPTRes */ ++}; ++UDNS_API dns_parse_fn dns_parse_naptr; /* NAPTR RR parsing routine */ ++typedef void /* NAPTR RR callback */ ++dns_query_naptr_fn(struct dns_ctx *ctx, ++ struct dns_rr_naptr *result, void *data); ++/* submit NAPTR IN query */ ++UDNS_API struct dns_query * ++dns_submit_naptr(struct dns_ctx *ctx, const char *name, int flags, ++ dns_query_naptr_fn *cbck, void *data); ++/* resolve NAPTR IN query */ ++UDNS_API struct dns_rr_naptr * ++dns_resolve_naptr(struct dns_ctx *ctx, const char *name, int flags); ++ ++ ++UDNS_API struct dns_query * ++dns_submit_a4dnsbl(struct dns_ctx *ctx, ++ const struct in_addr *addr, const char *dnsbl, ++ dns_query_a4_fn *cbck, void *data); ++UDNS_API struct dns_query * ++dns_submit_a4dnsbl_txt(struct dns_ctx *ctx, ++ const struct in_addr *addr, const char *dnsbl, ++ dns_query_txt_fn *cbck, void *data); ++UDNS_API struct dns_rr_a4 * ++dns_resolve_a4dnsbl(struct dns_ctx *ctx, ++ const struct in_addr *addr, const char *dnsbl); ++UDNS_API struct dns_rr_txt * ++dns_resolve_a4dnsbl_txt(struct dns_ctx *ctx, ++ const struct in_addr *addr, const char *dnsbl); ++ ++UDNS_API struct dns_query * ++dns_submit_a6dnsbl(struct dns_ctx *ctx, ++ const struct in6_addr *addr, const char *dnsbl, ++ dns_query_a4_fn *cbck, void *data); ++UDNS_API struct dns_query * ++dns_submit_a6dnsbl_txt(struct dns_ctx *ctx, ++ const struct in6_addr *addr, const char *dnsbl, ++ dns_query_txt_fn *cbck, void *data); ++UDNS_API struct dns_rr_a4 * ++dns_resolve_a6dnsbl(struct dns_ctx *ctx, ++ const struct in6_addr *addr, const char *dnsbl); ++UDNS_API struct dns_rr_txt * ++dns_resolve_a6dnsbl_txt(struct dns_ctx *ctx, ++ const struct in6_addr *addr, const char *dnsbl); ++ ++UDNS_API struct dns_query * ++dns_submit_rhsbl(struct dns_ctx *ctx, ++ const char *name, const char *rhsbl, ++ dns_query_a4_fn *cbck, void *data); ++UDNS_API struct dns_query * ++dns_submit_rhsbl_txt(struct dns_ctx *ctx, ++ const char *name, const char *rhsbl, ++ dns_query_txt_fn *cbck, void *data); ++UDNS_API struct dns_rr_a4 * ++dns_resolve_rhsbl(struct dns_ctx *ctx, const char *name, const char *rhsbl); ++UDNS_API struct dns_rr_txt * ++dns_resolve_rhsbl_txt(struct dns_ctx *ctx, const char *name, const char *rhsbl); ++ ++/**************************************************************************/ ++/**************** Names, Names ********************************************/ ++ ++struct dns_nameval { ++ int val; ++ const char *name; ++}; ++ ++UDNS_DATA_API extern const struct dns_nameval dns_classtab[]; ++UDNS_DATA_API extern const struct dns_nameval dns_typetab[]; ++UDNS_DATA_API extern const struct dns_nameval dns_rcodetab[]; ++UDNS_API int ++dns_findname(const struct dns_nameval *nv, const char *name); ++#define dns_findclassname(cls) dns_findname(dns_classtab, (cls)) ++#define dns_findtypename(type) dns_findname(dns_typetab, (type)) ++#define dns_findrcodename(rcode) dns_findname(dns_rcodetab, (rcode)) ++ ++UDNS_API const char *dns_classname(enum dns_class cls); ++UDNS_API const char *dns_typename(enum dns_type type); ++UDNS_API const char *dns_rcodename(enum dns_rcode rcode); ++const char *_dns_format_code(char *buf, const char *prefix, int code); ++ ++UDNS_API const char *dns_strerror(int errnum); ++ ++/* simple pseudo-random number generator, code by Bob Jenkins */ ++ ++struct udns_jranctx { /* the context */ ++ unsigned a, b, c, d; ++}; ++ ++/* initialize the RNG with a given seed */ ++UDNS_API void ++udns_jraninit(struct udns_jranctx *x, unsigned seed); ++ ++/* return next random number. 32bits on most platforms so far. */ ++UDNS_API unsigned ++udns_jranval(struct udns_jranctx *x); ++ ++#ifdef __cplusplus ++} /* extern "C" */ ++#endif ++ ++#endif /* include guard */ +diff --git a/libudns/udns_XtoX.c b/libudns/udns_XtoX.c +new file mode 100644 +index 0000000..654a4ef +--- /dev/null ++++ b/libudns/udns_XtoX.c +@@ -0,0 +1,57 @@ ++/* udns_XtoX.c ++ udns_ntop() and udns_pton() routines, which are either ++ - wrappers for inet_ntop() and inet_pton() or ++ - reimplementations of those routines. ++ ++ Copyright (C) 2005 Michael Tokarev ++ This file is part of UDNS library, an async DNS stub resolver. ++ ++ This library is free software; you can redistribute it and/or ++ modify it under the terms of the GNU Lesser General Public ++ License as published by the Free Software Foundation; either ++ version 2.1 of the License, or (at your option) any later version. ++ ++ This library is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ Lesser General Public License for more details. ++ ++ You should have received a copy of the GNU Lesser General Public ++ License along with this library, in file named COPYING.LGPL; if not, ++ write to the Free Software Foundation, Inc., 59 Temple Place, ++ Suite 330, Boston, MA 02111-1307 USA ++ ++ */ ++ ++#ifdef HAVE_CONFIG_H ++# include "config.h" ++#endif ++#include "udns.h" ++ ++#if HAVE_DECL_INET_NTOP == 1 ++ ++#include ++#include ++#include ++ ++const char *dns_ntop(int af, const void *src, char *dst, int size) { ++ return inet_ntop(af, src, dst, size); ++} ++ ++int dns_pton(int af, const char *src, void *dst) { ++ return inet_pton(af, src, dst); ++} ++ ++#else ++ ++#include /* includes */ ++#include /* needed for struct in6_addr */ ++ ++#ifndef EAFNOSUPPORT ++#define EAFNOSUPPORT WSAEAFNOSUPPORT ++#endif ++ ++#define inet_XtoX_prefix dns_ ++#include "inet_XtoX.c" ++ ++#endif +diff --git a/libudns/udns_bl.c b/libudns/udns_bl.c +new file mode 100644 +index 0000000..f6be393 +--- /dev/null ++++ b/libudns/udns_bl.c +@@ -0,0 +1,160 @@ ++/* udns_bl.c ++ DNSBL stuff ++ ++ Copyright (C) 2005 Michael Tokarev ++ This file is part of UDNS library, an async DNS stub resolver. ++ ++ This library is free software; you can redistribute it and/or ++ modify it under the terms of the GNU Lesser General Public ++ License as published by the Free Software Foundation; either ++ version 2.1 of the License, or (at your option) any later version. ++ ++ This library is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ Lesser General Public License for more details. ++ ++ You should have received a copy of the GNU Lesser General Public ++ License along with this library, in file named COPYING.LGPL; if not, ++ write to the Free Software Foundation, Inc., 59 Temple Place, ++ Suite 330, Boston, MA 02111-1307 USA ++ ++ */ ++ ++#include "udns.h" ++#ifndef NULL ++# define NULL 0 ++#endif ++ ++struct dns_query * ++dns_submit_a4dnsbl(struct dns_ctx *ctx, ++ const struct in_addr *addr, const char *dnsbl, ++ dns_query_a4_fn *cbck, void *data) { ++ dnsc_t dn[DNS_MAXDN]; ++ if (dns_a4ptodn(addr, dnsbl, dn, sizeof(dn)) <= 0) { ++ dns_setstatus(ctx, DNS_E_BADQUERY); ++ return NULL; ++ } ++ return ++ dns_submit_dn(ctx, dn, DNS_C_IN, DNS_T_A, DNS_NOSRCH, ++ dns_parse_a4, (dns_query_fn*)cbck, data); ++} ++ ++struct dns_query * ++dns_submit_a4dnsbl_txt(struct dns_ctx *ctx, ++ const struct in_addr *addr, const char *dnsbl, ++ dns_query_txt_fn *cbck, void *data) { ++ dnsc_t dn[DNS_MAXDN]; ++ if (dns_a4ptodn(addr, dnsbl, dn, sizeof(dn)) <= 0) { ++ dns_setstatus(ctx, DNS_E_BADQUERY); ++ return NULL; ++ } ++ return ++ dns_submit_dn(ctx, dn, DNS_C_IN, DNS_T_TXT, DNS_NOSRCH, ++ dns_parse_txt, (dns_query_fn*)cbck, data); ++} ++ ++struct dns_rr_a4 * ++dns_resolve_a4dnsbl(struct dns_ctx *ctx, ++ const struct in_addr *addr, const char *dnsbl) { ++ return (struct dns_rr_a4 *) ++ dns_resolve(ctx, dns_submit_a4dnsbl(ctx, addr, dnsbl, 0, 0)); ++} ++ ++struct dns_rr_txt * ++dns_resolve_a4dnsbl_txt(struct dns_ctx *ctx, ++ const struct in_addr *addr, const char *dnsbl) { ++ return (struct dns_rr_txt *) ++ dns_resolve(ctx, dns_submit_a4dnsbl_txt(ctx, addr, dnsbl, 0, 0)); ++} ++ ++ ++struct dns_query * ++dns_submit_a6dnsbl(struct dns_ctx *ctx, ++ const struct in6_addr *addr, const char *dnsbl, ++ dns_query_a4_fn *cbck, void *data) { ++ dnsc_t dn[DNS_MAXDN]; ++ if (dns_a6ptodn(addr, dnsbl, dn, sizeof(dn)) <= 0) { ++ dns_setstatus(ctx, DNS_E_BADQUERY); ++ return NULL; ++ } ++ return ++ dns_submit_dn(ctx, dn, DNS_C_IN, DNS_T_A, DNS_NOSRCH, ++ dns_parse_a4, (dns_query_fn*)cbck, data); ++} ++ ++struct dns_query * ++dns_submit_a6dnsbl_txt(struct dns_ctx *ctx, ++ const struct in6_addr *addr, const char *dnsbl, ++ dns_query_txt_fn *cbck, void *data) { ++ dnsc_t dn[DNS_MAXDN]; ++ if (dns_a6ptodn(addr, dnsbl, dn, sizeof(dn)) <= 0) { ++ dns_setstatus(ctx, DNS_E_BADQUERY); ++ return NULL; ++ } ++ return ++ dns_submit_dn(ctx, dn, DNS_C_IN, DNS_T_TXT, DNS_NOSRCH, ++ dns_parse_txt, (dns_query_fn*)cbck, data); ++} ++ ++struct dns_rr_a4 * ++dns_resolve_a6dnsbl(struct dns_ctx *ctx, ++ const struct in6_addr *addr, const char *dnsbl) { ++ return (struct dns_rr_a4 *) ++ dns_resolve(ctx, dns_submit_a6dnsbl(ctx, addr, dnsbl, 0, 0)); ++} ++ ++struct dns_rr_txt * ++dns_resolve_a6dnsbl_txt(struct dns_ctx *ctx, ++ const struct in6_addr *addr, const char *dnsbl) { ++ return (struct dns_rr_txt *) ++ dns_resolve(ctx, dns_submit_a6dnsbl_txt(ctx, addr, dnsbl, 0, 0)); ++} ++ ++static int ++dns_rhsbltodn(const char *name, const char *rhsbl, dnsc_t dn[DNS_MAXDN]) ++{ ++ int l = dns_sptodn(name, dn, DNS_MAXDN); ++ if (l <= 0) return 0; ++ l = dns_sptodn(rhsbl, dn+l-1, DNS_MAXDN-l+1); ++ if (l <= 0) return 0; ++ return 1; ++} ++ ++struct dns_query * ++dns_submit_rhsbl(struct dns_ctx *ctx, const char *name, const char *rhsbl, ++ dns_query_a4_fn *cbck, void *data) { ++ dnsc_t dn[DNS_MAXDN]; ++ if (!dns_rhsbltodn(name, rhsbl, dn)) { ++ dns_setstatus(ctx, DNS_E_BADQUERY); ++ return NULL; ++ } ++ return ++ dns_submit_dn(ctx, dn, DNS_C_IN, DNS_T_A, DNS_NOSRCH, ++ dns_parse_a4, (dns_query_fn*)cbck, data); ++} ++struct dns_query * ++dns_submit_rhsbl_txt(struct dns_ctx *ctx, const char *name, const char *rhsbl, ++ dns_query_txt_fn *cbck, void *data) { ++ dnsc_t dn[DNS_MAXDN]; ++ if (!dns_rhsbltodn(name, rhsbl, dn)) { ++ dns_setstatus(ctx, DNS_E_BADQUERY); ++ return NULL; ++ } ++ return ++ dns_submit_dn(ctx, dn, DNS_C_IN, DNS_T_TXT, DNS_NOSRCH, ++ dns_parse_txt, (dns_query_fn*)cbck, data); ++} ++ ++struct dns_rr_a4 * ++dns_resolve_rhsbl(struct dns_ctx *ctx, const char *name, const char *rhsbl) { ++ return (struct dns_rr_a4*) ++ dns_resolve(ctx, dns_submit_rhsbl(ctx, name, rhsbl, 0, 0)); ++} ++ ++struct dns_rr_txt * ++dns_resolve_rhsbl_txt(struct dns_ctx *ctx, const char *name, const char *rhsbl) ++{ ++ return (struct dns_rr_txt*) ++ dns_resolve(ctx, dns_submit_rhsbl_txt(ctx, name, rhsbl, 0, 0)); ++} +diff --git a/libudns/udns_codes.c b/libudns/udns_codes.c +new file mode 100644 +index 0000000..c637e98 +--- /dev/null ++++ b/libudns/udns_codes.c +@@ -0,0 +1,199 @@ ++/* Automatically generated. */ ++#include "udns.h" ++ ++const struct dns_nameval dns_typetab[] = { ++ {DNS_T_INVALID,"INVALID"}, ++ {DNS_T_A,"A"}, ++ {DNS_T_NS,"NS"}, ++ {DNS_T_MD,"MD"}, ++ {DNS_T_MF,"MF"}, ++ {DNS_T_CNAME,"CNAME"}, ++ {DNS_T_SOA,"SOA"}, ++ {DNS_T_MB,"MB"}, ++ {DNS_T_MG,"MG"}, ++ {DNS_T_MR,"MR"}, ++ {DNS_T_NULL,"NULL"}, ++ {DNS_T_WKS,"WKS"}, ++ {DNS_T_PTR,"PTR"}, ++ {DNS_T_HINFO,"HINFO"}, ++ {DNS_T_MINFO,"MINFO"}, ++ {DNS_T_MX,"MX"}, ++ {DNS_T_TXT,"TXT"}, ++ {DNS_T_RP,"RP"}, ++ {DNS_T_AFSDB,"AFSDB"}, ++ {DNS_T_X25,"X25"}, ++ {DNS_T_ISDN,"ISDN"}, ++ {DNS_T_RT,"RT"}, ++ {DNS_T_NSAP,"NSAP"}, ++ {DNS_T_NSAP_PTR,"NSAP_PTR"}, ++ {DNS_T_SIG,"SIG"}, ++ {DNS_T_KEY,"KEY"}, ++ {DNS_T_PX,"PX"}, ++ {DNS_T_GPOS,"GPOS"}, ++ {DNS_T_AAAA,"AAAA"}, ++ {DNS_T_LOC,"LOC"}, ++ {DNS_T_NXT,"NXT"}, ++ {DNS_T_EID,"EID"}, ++ {DNS_T_NIMLOC,"NIMLOC"}, ++ {DNS_T_SRV,"SRV"}, ++ {DNS_T_ATMA,"ATMA"}, ++ {DNS_T_NAPTR,"NAPTR"}, ++ {DNS_T_KX,"KX"}, ++ {DNS_T_CERT,"CERT"}, ++ {DNS_T_A6,"A6"}, ++ {DNS_T_DNAME,"DNAME"}, ++ {DNS_T_SINK,"SINK"}, ++ {DNS_T_OPT,"OPT"}, ++ {DNS_T_DS,"DS"}, ++ {DNS_T_SSHFP,"SSHFP"}, ++ {DNS_T_IPSECKEY,"IPSECKEY"}, ++ {DNS_T_RRSIG,"RRSIG"}, ++ {DNS_T_NSEC,"NSEC"}, ++ {DNS_T_DNSKEY,"DNSKEY"}, ++ {DNS_T_DHCID,"DHCID"}, ++ {DNS_T_NSEC3,"NSEC3"}, ++ {DNS_T_NSEC3PARAMS,"NSEC3PARAMS"}, ++ {DNS_T_TALINK,"TALINK"}, ++ {DNS_T_SPF,"SPF"}, ++ {DNS_T_UINFO,"UINFO"}, ++ {DNS_T_UID,"UID"}, ++ {DNS_T_GID,"GID"}, ++ {DNS_T_UNSPEC,"UNSPEC"}, ++ {DNS_T_TSIG,"TSIG"}, ++ {DNS_T_IXFR,"IXFR"}, ++ {DNS_T_AXFR,"AXFR"}, ++ {DNS_T_MAILB,"MAILB"}, ++ {DNS_T_MAILA,"MAILA"}, ++ {DNS_T_ANY,"ANY"}, ++ {DNS_T_ZXFR,"ZXFR"}, ++ {DNS_T_DLV,"DLV"}, ++ {DNS_T_MAX,"MAX"}, ++ {0,0}}; ++const char *dns_typename(enum dns_type code) { ++ static char nm[20]; ++ switch(code) { ++ case DNS_T_INVALID: return dns_typetab[0].name; ++ case DNS_T_A: return dns_typetab[1].name; ++ case DNS_T_NS: return dns_typetab[2].name; ++ case DNS_T_MD: return dns_typetab[3].name; ++ case DNS_T_MF: return dns_typetab[4].name; ++ case DNS_T_CNAME: return dns_typetab[5].name; ++ case DNS_T_SOA: return dns_typetab[6].name; ++ case DNS_T_MB: return dns_typetab[7].name; ++ case DNS_T_MG: return dns_typetab[8].name; ++ case DNS_T_MR: return dns_typetab[9].name; ++ case DNS_T_NULL: return dns_typetab[10].name; ++ case DNS_T_WKS: return dns_typetab[11].name; ++ case DNS_T_PTR: return dns_typetab[12].name; ++ case DNS_T_HINFO: return dns_typetab[13].name; ++ case DNS_T_MINFO: return dns_typetab[14].name; ++ case DNS_T_MX: return dns_typetab[15].name; ++ case DNS_T_TXT: return dns_typetab[16].name; ++ case DNS_T_RP: return dns_typetab[17].name; ++ case DNS_T_AFSDB: return dns_typetab[18].name; ++ case DNS_T_X25: return dns_typetab[19].name; ++ case DNS_T_ISDN: return dns_typetab[20].name; ++ case DNS_T_RT: return dns_typetab[21].name; ++ case DNS_T_NSAP: return dns_typetab[22].name; ++ case DNS_T_NSAP_PTR: return dns_typetab[23].name; ++ case DNS_T_SIG: return dns_typetab[24].name; ++ case DNS_T_KEY: return dns_typetab[25].name; ++ case DNS_T_PX: return dns_typetab[26].name; ++ case DNS_T_GPOS: return dns_typetab[27].name; ++ case DNS_T_AAAA: return dns_typetab[28].name; ++ case DNS_T_LOC: return dns_typetab[29].name; ++ case DNS_T_NXT: return dns_typetab[30].name; ++ case DNS_T_EID: return dns_typetab[31].name; ++ case DNS_T_NIMLOC: return dns_typetab[32].name; ++ case DNS_T_SRV: return dns_typetab[33].name; ++ case DNS_T_ATMA: return dns_typetab[34].name; ++ case DNS_T_NAPTR: return dns_typetab[35].name; ++ case DNS_T_KX: return dns_typetab[36].name; ++ case DNS_T_CERT: return dns_typetab[37].name; ++ case DNS_T_A6: return dns_typetab[38].name; ++ case DNS_T_DNAME: return dns_typetab[39].name; ++ case DNS_T_SINK: return dns_typetab[40].name; ++ case DNS_T_OPT: return dns_typetab[41].name; ++ case DNS_T_DS: return dns_typetab[42].name; ++ case DNS_T_SSHFP: return dns_typetab[43].name; ++ case DNS_T_IPSECKEY: return dns_typetab[44].name; ++ case DNS_T_RRSIG: return dns_typetab[45].name; ++ case DNS_T_NSEC: return dns_typetab[46].name; ++ case DNS_T_DNSKEY: return dns_typetab[47].name; ++ case DNS_T_DHCID: return dns_typetab[48].name; ++ case DNS_T_NSEC3: return dns_typetab[49].name; ++ case DNS_T_NSEC3PARAMS: return dns_typetab[50].name; ++ case DNS_T_TALINK: return dns_typetab[51].name; ++ case DNS_T_SPF: return dns_typetab[52].name; ++ case DNS_T_UINFO: return dns_typetab[53].name; ++ case DNS_T_UID: return dns_typetab[54].name; ++ case DNS_T_GID: return dns_typetab[55].name; ++ case DNS_T_UNSPEC: return dns_typetab[56].name; ++ case DNS_T_TSIG: return dns_typetab[57].name; ++ case DNS_T_IXFR: return dns_typetab[58].name; ++ case DNS_T_AXFR: return dns_typetab[59].name; ++ case DNS_T_MAILB: return dns_typetab[60].name; ++ case DNS_T_MAILA: return dns_typetab[61].name; ++ case DNS_T_ANY: return dns_typetab[62].name; ++ case DNS_T_ZXFR: return dns_typetab[63].name; ++ case DNS_T_DLV: return dns_typetab[64].name; ++ case DNS_T_MAX: return dns_typetab[65].name; ++ } ++ return _dns_format_code(nm,"type",code); ++} ++ ++const struct dns_nameval dns_classtab[] = { ++ {DNS_C_INVALID,"INVALID"}, ++ {DNS_C_IN,"IN"}, ++ {DNS_C_CH,"CH"}, ++ {DNS_C_HS,"HS"}, ++ {DNS_C_ANY,"ANY"}, ++ {0,0}}; ++const char *dns_classname(enum dns_class code) { ++ static char nm[20]; ++ switch(code) { ++ case DNS_C_INVALID: return dns_classtab[0].name; ++ case DNS_C_IN: return dns_classtab[1].name; ++ case DNS_C_CH: return dns_classtab[2].name; ++ case DNS_C_HS: return dns_classtab[3].name; ++ case DNS_C_ANY: return dns_classtab[4].name; ++ } ++ return _dns_format_code(nm,"class",code); ++} ++ ++const struct dns_nameval dns_rcodetab[] = { ++ {DNS_R_NOERROR,"NOERROR"}, ++ {DNS_R_FORMERR,"FORMERR"}, ++ {DNS_R_SERVFAIL,"SERVFAIL"}, ++ {DNS_R_NXDOMAIN,"NXDOMAIN"}, ++ {DNS_R_NOTIMPL,"NOTIMPL"}, ++ {DNS_R_REFUSED,"REFUSED"}, ++ {DNS_R_YXDOMAIN,"YXDOMAIN"}, ++ {DNS_R_YXRRSET,"YXRRSET"}, ++ {DNS_R_NXRRSET,"NXRRSET"}, ++ {DNS_R_NOTAUTH,"NOTAUTH"}, ++ {DNS_R_NOTZONE,"NOTZONE"}, ++ {DNS_R_BADSIG,"BADSIG"}, ++ {DNS_R_BADKEY,"BADKEY"}, ++ {DNS_R_BADTIME,"BADTIME"}, ++ {0,0}}; ++const char *dns_rcodename(enum dns_rcode code) { ++ static char nm[20]; ++ switch(code) { ++ case DNS_R_NOERROR: return dns_rcodetab[0].name; ++ case DNS_R_FORMERR: return dns_rcodetab[1].name; ++ case DNS_R_SERVFAIL: return dns_rcodetab[2].name; ++ case DNS_R_NXDOMAIN: return dns_rcodetab[3].name; ++ case DNS_R_NOTIMPL: return dns_rcodetab[4].name; ++ case DNS_R_REFUSED: return dns_rcodetab[5].name; ++ case DNS_R_YXDOMAIN: return dns_rcodetab[6].name; ++ case DNS_R_YXRRSET: return dns_rcodetab[7].name; ++ case DNS_R_NXRRSET: return dns_rcodetab[8].name; ++ case DNS_R_NOTAUTH: return dns_rcodetab[9].name; ++ case DNS_R_NOTZONE: return dns_rcodetab[10].name; ++ case DNS_R_BADSIG: return dns_rcodetab[11].name; ++ case DNS_R_BADKEY: return dns_rcodetab[12].name; ++ case DNS_R_BADTIME: return dns_rcodetab[13].name; ++ } ++ return _dns_format_code(nm,"rcode",code); ++} +diff --git a/libudns/udns_dn.c b/libudns/udns_dn.c +new file mode 100644 +index 0000000..7c5ff80 +--- /dev/null ++++ b/libudns/udns_dn.c +@@ -0,0 +1,379 @@ ++/* udns_dn.c ++ domain names manipulation routines ++ ++ Copyright (C) 2005 Michael Tokarev ++ This file is part of UDNS library, an async DNS stub resolver. ++ ++ This library is free software; you can redistribute it and/or ++ modify it under the terms of the GNU Lesser General Public ++ License as published by the Free Software Foundation; either ++ version 2.1 of the License, or (at your option) any later version. ++ ++ This library is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ Lesser General Public License for more details. ++ ++ You should have received a copy of the GNU Lesser General Public ++ License along with this library, in file named COPYING.LGPL; if not, ++ write to the Free Software Foundation, Inc., 59 Temple Place, ++ Suite 330, Boston, MA 02111-1307 USA ++ ++ */ ++ ++#include ++#include "udns.h" ++ ++unsigned dns_dnlen(dnscc_t *dn) { ++ register dnscc_t *d = dn; ++ while(*d) ++ d += 1 + *d; ++ return (unsigned)(d - dn) + 1; ++} ++ ++unsigned dns_dnlabels(register dnscc_t *dn) { ++ register unsigned l = 0; ++ while(*dn) ++ ++l, dn += 1 + *dn; ++ return l; ++} ++ ++unsigned dns_dnequal(register dnscc_t *dn1, register dnscc_t *dn2) { ++ register unsigned c; ++ dnscc_t *dn = dn1; ++ for(;;) { ++ if ((c = *dn1++) != *dn2++) ++ return 0; ++ if (!c) ++ return (unsigned)(dn1 - dn); ++ while(c--) { ++ if (DNS_DNLC(*dn1) != DNS_DNLC(*dn2)) ++ return 0; ++ ++dn1; ++dn2; ++ } ++ } ++} ++ ++unsigned ++dns_dntodn(dnscc_t *sdn, dnsc_t *ddn, unsigned ddnsiz) { ++ unsigned sdnlen = dns_dnlen(sdn); ++ if (ddnsiz < sdnlen) ++ return 0; ++ memcpy(ddn, sdn, sdnlen); ++ return sdnlen; ++} ++ ++int ++dns_ptodn(const char *name, unsigned namelen, ++ dnsc_t *dn, unsigned dnsiz, int *isabs) ++{ ++ dnsc_t *dp; /* current position in dn (len byte first) */ ++ dnsc_t *const de /* end of dn: last byte that can be filled up */ ++ = dn + (dnsiz >= DNS_MAXDN ? DNS_MAXDN : dnsiz) - 1; ++ dnscc_t *np = (dnscc_t *)name; ++ dnscc_t *ne = np + (namelen ? namelen : strlen((char*)np)); ++ dnsc_t *llab; /* start of last label (llab[-1] will be length) */ ++ unsigned c; /* next input character, or length of last label */ ++ ++ if (!dnsiz) ++ return 0; ++ dp = llab = dn + 1; ++ ++ while(np < ne) { ++ ++ if (*np == '.') { /* label delimiter */ ++ c = dp - llab; /* length of the label */ ++ if (!c) { /* empty label */ ++ if (np == (dnscc_t *)name && np + 1 == ne) { ++ /* special case for root dn, aka `.' */ ++ ++np; ++ break; ++ } ++ return -1; /* zero label */ ++ } ++ if (c > DNS_MAXLABEL) ++ return -1; /* label too long */ ++ llab[-1] = (dnsc_t)c; /* update len of last label */ ++ llab = ++dp; /* start new label, llab[-1] will be len of it */ ++ ++np; ++ continue; ++ } ++ ++ /* check whenever we may put out one more byte */ ++ if (dp >= de) /* too long? */ ++ return dnsiz >= DNS_MAXDN ? -1 : 0; ++ if (*np != '\\') { /* non-escape, simple case */ ++ *dp++ = *np++; ++ continue; ++ } ++ /* handle \-style escape */ ++ /* note that traditionally, domain names (gethostbyname etc) ++ * used decimal \dd notation, not octal \ooo (RFC1035), so ++ * we're following this tradition here. ++ */ ++ if (++np == ne) ++ return -1; /* bad escape */ ++ else if (*np >= '0' && *np <= '9') { /* decimal number */ ++ /* we allow not only exactly 3 digits as per RFC1035, ++ * but also 2 or 1, for better usability. */ ++ c = *np++ - '0'; ++ if (np < ne && *np >= '0' && *np <= '9') { /* 2digits */ ++ c = c * 10 + *np++ - '0'; ++ if (np < ne && *np >= '0' && *np <= '9') { ++ c = c * 10 + *np++ - '0'; ++ if (c > 255) ++ return -1; /* bad escape */ ++ } ++ } ++ } ++ else ++ c = *np++; ++ *dp++ = (dnsc_t)c; /* place next out byte */ ++ } ++ ++ if ((c = dp - llab) > DNS_MAXLABEL) ++ return -1; /* label too long */ ++ if ((llab[-1] = (dnsc_t)c) != 0) { ++ *dp++ = 0; ++ if (isabs) ++ *isabs = 0; ++ } ++ else if (isabs) ++ *isabs = 1; ++ ++ return dp - dn; ++} ++ ++dnscc_t dns_inaddr_arpa_dn[14] = "\07in-addr\04arpa"; ++ ++dnsc_t * ++dns_a4todn_(const struct in_addr *addr, dnsc_t *dn, dnsc_t *dne) { ++ const unsigned char *s = ((const unsigned char *)addr) + 4; ++ while(s > (const unsigned char *)addr) { ++ unsigned n = *--s; ++ dnsc_t *p = dn + 1; ++ if (n > 99) { ++ if (p + 2 > dne) return 0; ++ *p++ = n / 100 + '0'; ++ *p++ = (n % 100 / 10) + '0'; ++ *p = n % 10 + '0'; ++ } ++ else if (n > 9) { ++ if (p + 1 > dne) return 0; ++ *p++ = n / 10 + '0'; ++ *p = n % 10 + '0'; ++ } ++ else { ++ if (p > dne) return 0; ++ *p = n + '0'; ++ } ++ *dn = p - dn; ++ dn = p + 1; ++ } ++ return dn; ++} ++ ++int dns_a4todn(const struct in_addr *addr, dnscc_t *tdn, ++ dnsc_t *dn, unsigned dnsiz) { ++ dnsc_t *dne = dn + (dnsiz > DNS_MAXDN ? DNS_MAXDN : dnsiz); ++ dnsc_t *p; ++ unsigned l; ++ p = dns_a4todn_(addr, dn, dne); ++ if (!p) return 0; ++ if (!tdn) ++ tdn = dns_inaddr_arpa_dn; ++ l = dns_dnlen(tdn); ++ if (p + l > dne) return dnsiz >= DNS_MAXDN ? -1 : 0; ++ memcpy(p, tdn, l); ++ return (p + l) - dn; ++} ++ ++int dns_a4ptodn(const struct in_addr *addr, const char *tname, ++ dnsc_t *dn, unsigned dnsiz) { ++ dnsc_t *p; ++ int r; ++ if (!tname) ++ return dns_a4todn(addr, NULL, dn, dnsiz); ++ p = dns_a4todn_(addr, dn, dn + dnsiz); ++ if (!p) return 0; ++ r = dns_sptodn(tname, p, dnsiz - (p - dn)); ++ return r != 0 ? r : dnsiz >= DNS_MAXDN ? -1 : 0; ++} ++ ++dnscc_t dns_ip6_arpa_dn[10] = "\03ip6\04arpa"; ++ ++dnsc_t * ++dns_a6todn_(const struct in6_addr *addr, dnsc_t *dn, dnsc_t *dne) { ++ const unsigned char *s = ((const unsigned char *)addr) + 16; ++ if (dn + 64 > dne) return 0; ++ while(s > (const unsigned char *)addr) { ++ unsigned n = *--s & 0x0f; ++ *dn++ = 1; ++ *dn++ = n > 9 ? n + 'a' - 10 : n + '0'; ++ *dn++ = 1; ++ n = *s >> 4; ++ *dn++ = n > 9 ? n + 'a' - 10 : n + '0'; ++ } ++ return dn; ++} ++ ++int dns_a6todn(const struct in6_addr *addr, dnscc_t *tdn, ++ dnsc_t *dn, unsigned dnsiz) { ++ dnsc_t *dne = dn + (dnsiz > DNS_MAXDN ? DNS_MAXDN : dnsiz); ++ dnsc_t *p; ++ unsigned l; ++ p = dns_a6todn_(addr, dn, dne); ++ if (!p) return 0; ++ if (!tdn) ++ tdn = dns_ip6_arpa_dn; ++ l = dns_dnlen(tdn); ++ if (p + l > dne) return dnsiz >= DNS_MAXDN ? -1 : 0; ++ memcpy(p, tdn, l); ++ return (p + l) - dn; ++} ++ ++int dns_a6ptodn(const struct in6_addr *addr, const char *tname, ++ dnsc_t *dn, unsigned dnsiz) { ++ dnsc_t *p; ++ int r; ++ if (!tname) ++ return dns_a6todn(addr, NULL, dn, dnsiz); ++ p = dns_a6todn_(addr, dn, dn + dnsiz); ++ if (!p) return 0; ++ r = dns_sptodn(tname, p, dnsiz - (p - dn)); ++ return r != 0 ? r : dnsiz >= DNS_MAXDN ? -1 : 0; ++} ++ ++/* return size of buffer required to convert the dn into asciiz string. ++ * Keep in sync with dns_dntop() below. ++ */ ++unsigned dns_dntop_size(dnscc_t *dn) { ++ unsigned size = 0; /* the size reqd */ ++ dnscc_t *le; /* label end */ ++ ++ while(*dn) { ++ /* *dn is the length of the next label, non-zero */ ++ if (size) ++ ++size; /* for the dot */ ++ le = dn + *dn + 1; ++ ++dn; ++ do { ++ switch(*dn) { ++ case '.': ++ case '\\': ++ /* Special modifiers in zone files. */ ++ case '"': ++ case ';': ++ case '@': ++ case '$': ++ size += 2; ++ break; ++ default: ++ if (*dn <= 0x20 || *dn >= 0x7f) ++ /* \ddd decimal notation */ ++ size += 4; ++ else ++ size += 1; ++ } ++ } while(++dn < le); ++ } ++ size += 1; /* zero byte at the end - string terminator */ ++ return size > DNS_MAXNAME ? 0 : size; ++} ++ ++/* Convert the dn into asciiz string. ++ * Keep in sync with dns_dntop_size() above. ++ */ ++int dns_dntop(dnscc_t *dn, char *name, unsigned namesiz) { ++ char *np = name; /* current name ptr */ ++ char *const ne = name + namesiz; /* end of name */ ++ dnscc_t *le; /* label end */ ++ ++ while(*dn) { ++ /* *dn is the length of the next label, non-zero */ ++ if (np != name) { ++ if (np >= ne) goto toolong; ++ *np++ = '.'; ++ } ++ le = dn + *dn + 1; ++ ++dn; ++ do { ++ switch(*dn) { ++ case '.': ++ case '\\': ++ /* Special modifiers in zone files. */ ++ case '"': ++ case ';': ++ case '@': ++ case '$': ++ if (np + 2 > ne) goto toolong; ++ *np++ = '\\'; ++ *np++ = *dn; ++ break; ++ default: ++ if (*dn <= 0x20 || *dn >= 0x7f) { ++ /* \ddd decimal notation */ ++ if (np + 4 >= ne) goto toolong; ++ *np++ = '\\'; ++ *np++ = '0' + (*dn / 100); ++ *np++ = '0' + ((*dn % 100) / 10); ++ *np++ = '0' + (*dn % 10); ++ } ++ else { ++ if (np >= ne) goto toolong; ++ *np++ = *dn; ++ } ++ } ++ } while(++dn < le); ++ } ++ if (np >= ne) goto toolong; ++ *np++ = '\0'; ++ return np - name; ++toolong: ++ return namesiz >= DNS_MAXNAME ? -1 : 0; ++} ++ ++#if 0 ++#include ++#include ++ ++int main(int argc, char **argv) { ++ int i; ++ int sz; ++ dnsc_t dn[DNS_MAXDN+10]; ++ dnsc_t *dl, *dp; ++ int isabs; ++ ++ sz = (argc > 1) ? atoi(argv[1]) : 0; ++ ++ for(i = 2; i < argc; ++i) { ++ int r = dns_ptodn(argv[i], 0, dn, sz, &isabs); ++ printf("%s: ", argv[i]); ++ if (r < 0) printf("error\n"); ++ else if (!r) printf("buffer too small\n"); ++ else { ++ printf("len=%d dnlen=%d size=%d name:", ++ r, dns_dnlen(dn), dns_dntop_size(dn)); ++ dl = dn; ++ while(*dl) { ++ printf(" %d=", *dl); ++ dp = dl + 1; ++ dl = dp + *dl; ++ while(dp < dl) { ++ if (*dp <= ' ' || *dp >= 0x7f) ++ printf("\\%03d", *dp); ++ else if (*dp == '.' || *dp == '\\') ++ printf("\\%c", *dp); ++ else ++ putchar(*dp); ++ ++dp; ++ } ++ } ++ if (isabs) putchar('.'); ++ putchar('\n'); ++ } ++ } ++ return 0; ++} ++ ++#endif /* TEST */ +diff --git a/libudns/udns_dntosp.c b/libudns/udns_dntosp.c +new file mode 100644 +index 0000000..823fde2 +--- /dev/null ++++ b/libudns/udns_dntosp.c +@@ -0,0 +1,30 @@ ++/* udns_dntosp.c ++ dns_dntosp() = convert DN to asciiz string using static buffer ++ ++ Copyright (C) 2005 Michael Tokarev ++ This file is part of UDNS library, an async DNS stub resolver. ++ ++ This library is free software; you can redistribute it and/or ++ modify it under the terms of the GNU Lesser General Public ++ License as published by the Free Software Foundation; either ++ version 2.1 of the License, or (at your option) any later version. ++ ++ This library is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ Lesser General Public License for more details. ++ ++ You should have received a copy of the GNU Lesser General Public ++ License along with this library, in file named COPYING.LGPL; if not, ++ write to the Free Software Foundation, Inc., 59 Temple Place, ++ Suite 330, Boston, MA 02111-1307 USA ++ ++ */ ++ ++#include "udns.h" ++ ++static char name[DNS_MAXNAME]; ++ ++const char *dns_dntosp(dnscc_t *dn) { ++ return dns_dntop(dn, name, sizeof(name)) > 0 ? name : 0; ++} +diff --git a/libudns/udns_init.c b/libudns/udns_init.c +new file mode 100644 +index 0000000..5e24554 +--- /dev/null ++++ b/libudns/udns_init.c +@@ -0,0 +1,233 @@ ++/* udns_init.c ++ resolver initialisation stuff ++ ++ Copyright (C) 2006 Michael Tokarev ++ This file is part of UDNS library, an async DNS stub resolver. ++ ++ This library is free software; you can redistribute it and/or ++ modify it under the terms of the GNU Lesser General Public ++ License as published by the Free Software Foundation; either ++ version 2.1 of the License, or (at your option) any later version. ++ ++ This library is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ Lesser General Public License for more details. ++ ++ You should have received a copy of the GNU Lesser General Public ++ License along with this library, in file named COPYING.LGPL; if not, ++ write to the Free Software Foundation, Inc., 59 Temple Place, ++ Suite 330, Boston, MA 02111-1307 USA ++ ++ */ ++ ++#ifdef HAVE_CONFIG_H ++# include "config.h" ++#endif ++#ifdef __MINGW32__ ++# include /* includes */ ++# include /* for dns server addresses etc */ ++#else ++# include ++# include ++# include ++#endif /* !__MINGW32__ */ ++ ++#include ++#include ++#include "udns.h" ++ ++#define ISSPACE(x) (x == ' ' || x == '\t' || x == '\r' || x == '\n') ++ ++static const char space[] = " \t\r\n"; ++ ++static void dns_set_serv_internal(struct dns_ctx *ctx, char *serv) { ++ dns_add_serv(ctx, NULL); ++ for(serv = strtok(serv, space); serv; serv = strtok(NULL, space)) ++ dns_add_serv(ctx, serv); ++} ++ ++static void dns_set_srch_internal(struct dns_ctx *ctx, char *srch) { ++ dns_add_srch(ctx, NULL); ++ for(srch = strtok(srch, space); srch; srch = strtok(NULL, space)) ++ dns_add_srch(ctx, srch); ++} ++ ++#ifdef __MINGW32__ ++ ++#define NO_IPHLPAPI ++ ++#ifndef NO_IPHLPAPI ++/* Apparently, some systems does not have proper headers for IPHLPAIP to work. ++ * The best is to upgrade headers, but here's another, ugly workaround for ++ * this: compile with -DNO_IPHLPAPI. ++ */ ++ ++typedef DWORD (WINAPI *GetAdaptersAddressesFunc)( ++ ULONG Family, DWORD Flags, PVOID Reserved, ++ PIP_ADAPTER_ADDRESSES pAdapterAddresses, ++ PULONG pOutBufLen); ++ ++static int dns_initns_iphlpapi(struct dns_ctx *ctx) { ++ HANDLE h_iphlpapi; ++ GetAdaptersAddressesFunc pfnGetAdAddrs; ++ PIP_ADAPTER_ADDRESSES pAddr, pAddrBuf; ++ PIP_ADAPTER_DNS_SERVER_ADDRESS pDnsAddr; ++ ULONG ulOutBufLen; ++ DWORD dwRetVal; ++ int ret = -1; ++ ++ h_iphlpapi = LoadLibrary("iphlpapi.dll"); ++ if (!h_iphlpapi) ++ return -1; ++ pfnGetAdAddrs = (GetAdaptersAddressesFunc) ++ GetProcAddress(h_iphlpapi, "GetAdaptersAddresses"); ++ if (!pfnGetAdAddrs) goto freelib; ++ ulOutBufLen = 0; ++ dwRetVal = pfnGetAdAddrs(AF_UNSPEC, 0, NULL, NULL, &ulOutBufLen); ++ if (dwRetVal != ERROR_BUFFER_OVERFLOW) goto freelib; ++ pAddrBuf = malloc(ulOutBufLen); ++ if (!pAddrBuf) goto freelib; ++ dwRetVal = pfnGetAdAddrs(AF_UNSPEC, 0, NULL, pAddrBuf, &ulOutBufLen); ++ if (dwRetVal != ERROR_SUCCESS) goto freemem; ++ for (pAddr = pAddrBuf; pAddr; pAddr = pAddr->Next) ++ for (pDnsAddr = pAddr->FirstDnsServerAddress; ++ pDnsAddr; ++ pDnsAddr = pDnsAddr->Next) ++ dns_add_serv_s(ctx, pDnsAddr->Address.lpSockaddr); ++ ret = 0; ++freemem: ++ free(pAddrBuf); ++freelib: ++ FreeLibrary(h_iphlpapi); ++ return ret; ++} ++ ++#else /* NO_IPHLPAPI */ ++ ++#define dns_initns_iphlpapi(ctx) (-1) ++ ++#endif /* NO_IPHLPAPI */ ++ ++static int dns_initns_registry(struct dns_ctx *ctx) { ++ LONG res; ++ HKEY hk; ++ DWORD type = REG_EXPAND_SZ | REG_SZ; ++ DWORD len; ++ char valBuf[1024]; ++ ++#define REGKEY_WINNT "SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters" ++#define REGKEY_WIN9x "SYSTEM\\CurrentControlSet\\Services\\VxD\\MSTCP" ++ res = RegOpenKeyEx(HKEY_LOCAL_MACHINE, REGKEY_WINNT, 0, KEY_QUERY_VALUE, &hk); ++ if (res != ERROR_SUCCESS) ++ res = RegOpenKeyEx(HKEY_LOCAL_MACHINE, REGKEY_WIN9x, ++ 0, KEY_QUERY_VALUE, &hk); ++ if (res != ERROR_SUCCESS) ++ return -1; ++ len = sizeof(valBuf) - 1; ++ res = RegQueryValueEx(hk, "NameServer", NULL, &type, (BYTE*)valBuf, &len); ++ if (res != ERROR_SUCCESS || !len || !valBuf[0]) { ++ len = sizeof(valBuf) - 1; ++ res = RegQueryValueEx(hk, "DhcpNameServer", NULL, &type, ++ (BYTE*)valBuf, &len); ++ } ++ RegCloseKey(hk); ++ if (res != ERROR_SUCCESS || !len || !valBuf[0]) ++ return -1; ++ valBuf[len] = '\0'; ++ /* nameservers are stored as a whitespace-seperate list: ++ * "192.168.1.1 123.21.32.12" */ ++ dns_set_serv_internal(ctx, valBuf); ++ return 0; ++} ++ ++#else /* !__MINGW32__ */ ++ ++static int dns_init_resolvconf(struct dns_ctx *ctx) { ++ char *v; ++ char buf[2049]; /* this buffer is used to hold /etc/resolv.conf */ ++ int has_srch = 0; ++ ++ /* read resolv.conf... */ ++ { int fd = open("/etc/resolv.conf", O_RDONLY); ++ if (fd >= 0) { ++ int l = read(fd, buf, sizeof(buf) - 1); ++ close(fd); ++ buf[l < 0 ? 0 : l] = '\0'; ++ } ++ else ++ buf[0] = '\0'; ++ } ++ if (buf[0]) { /* ...and parse it */ ++ char *line, *nextline; ++ line = buf; ++ do { ++ nextline = strchr(line, '\n'); ++ if (nextline) *nextline++ = '\0'; ++ v = line; ++ while(*v && !ISSPACE(*v)) ++v; ++ if (!*v) continue; ++ *v++ = '\0'; ++ while(ISSPACE(*v)) ++v; ++ if (!*v) continue; ++ if (strcmp(line, "domain") == 0) { ++ dns_set_srch_internal(ctx, strtok(v, space)); ++ has_srch = 1; ++ } ++ else if (strcmp(line, "search") == 0) { ++ dns_set_srch_internal(ctx, v); ++ has_srch = 1; ++ } ++ else if (strcmp(line, "nameserver") == 0) ++ dns_add_serv(ctx, strtok(v, space)); ++ else if (strcmp(line, "options") == 0) ++ dns_set_opts(ctx, v); ++ } while((line = nextline) != NULL); ++ } ++ ++ buf[sizeof(buf)-1] = '\0'; ++ ++ /* get list of nameservers from env. vars. */ ++ if ((v = getenv("NSCACHEIP")) != NULL || ++ (v = getenv("NAMESERVERS")) != NULL) { ++ strncpy(buf, v, sizeof(buf) - 1); ++ dns_set_serv_internal(ctx, buf); ++ } ++ /* if $LOCALDOMAIN is set, use it for search list */ ++ if ((v = getenv("LOCALDOMAIN")) != NULL) { ++ strncpy(buf, v, sizeof(buf) - 1); ++ dns_set_srch_internal(ctx, buf); ++ has_srch = 1; ++ } ++ if ((v = getenv("RES_OPTIONS")) != NULL) ++ dns_set_opts(ctx, v); ++ ++ /* if still no search list, use local domain name */ ++ if (has_srch && ++ gethostname(buf, sizeof(buf) - 1) == 0 && ++ (v = strchr(buf, '.')) != NULL && ++ *++v != '\0') ++ dns_add_srch(ctx, v); ++ ++ return 0; ++} ++ ++#endif /* !__MINGW32__ */ ++ ++int dns_init(struct dns_ctx *ctx, int do_open) { ++ if (!ctx) ++ ctx = &dns_defctx; ++ dns_reset(ctx); ++ ++#ifdef __MINGW32__ ++ if (dns_initns_iphlpapi(ctx) != 0) ++ dns_initns_registry(ctx); ++ /*XXX __MINGW32__: probably good to get default domain and search list too... ++ * And options. Something is in registry. */ ++ /*XXX __MINGW32__: maybe environment variables are also useful? */ ++#else ++ dns_init_resolvconf(ctx); ++#endif ++ ++ return do_open ? dns_open(ctx) : 0; ++} +diff --git a/libudns/udns_jran.c b/libudns/udns_jran.c +new file mode 100644 +index 0000000..19f9d02 +--- /dev/null ++++ b/libudns/udns_jran.c +@@ -0,0 +1,52 @@ ++/* udns_jran.c: small non-cryptographic random number generator ++ * taken from http://burtleburtle.net/bob/rand/smallprng.html ++ * by Bob Jenkins, Public domain. ++ */ ++ ++#include "udns.h" ++ ++#define rot32(x,k) (((x) << (k)) | ((x) >> (32-(k)))) ++#define rot64(x,k) (((x) << (k)) | ((x) >> (64-(k)))) ++#define tr32(x) ((x)&0xffffffffu) ++ ++unsigned udns_jranval(struct udns_jranctx *x) { ++ /* This routine can be made to work with either 32 or 64bit words - ++ * if JRAN_32_64 is defined when compiling the file. ++ * We use if() instead of #if since there's no good ++ * portable way to check sizeof() in preprocessor without ++ * introducing some ugly configure-time checks. ++ * Most compilers will optimize the wrong branches away anyway. ++ * By default it assumes 32bit integers ++ */ ++#ifdef JRAN_32_64 ++ if (sizeof(unsigned) == 4) { ++#endif ++ unsigned e = tr32(x->a - rot32(x->b, 27)); ++ x->a = tr32(x->b ^ rot32(x->c, 17)); ++ x->b = tr32(x->c + x->d); ++ x->c = tr32(x->d + e); ++ x->d = tr32(e + x->a); ++#ifdef JRAN_32_64 ++ } ++ else if (sizeof(unsigned) == 8) { /* assuming it's 64bits */ ++ unsigned e = x->a - rot64(x->b, 7); ++ x->a = x->b ^ rot64(x->c, 13); ++ x->b = x->c + rot64(x->d, 37); ++ x->c = x->d + e; ++ x->d = e + x->a; ++ } ++ else { ++ unsigned e = 0; ++ x->d = 1/e; /* bail */ ++ } ++#endif ++ return x->d; ++} ++ ++void udns_jraninit(struct udns_jranctx *x, unsigned seed) { ++ unsigned i; ++ x->a = 0xf1ea5eed; ++ x->b = x->c = x->d = seed; ++ for (i = 0; i < 20; ++i) ++ (void)udns_jranval(x); ++} +diff --git a/libudns/udns_misc.c b/libudns/udns_misc.c +new file mode 100644 +index 0000000..c162e70 +--- /dev/null ++++ b/libudns/udns_misc.c +@@ -0,0 +1,67 @@ ++/* udns_misc.c ++ miscellaneous routines ++ ++ Copyright (C) 2005 Michael Tokarev ++ This file is part of UDNS library, an async DNS stub resolver. ++ ++ This library is free software; you can redistribute it and/or ++ modify it under the terms of the GNU Lesser General Public ++ License as published by the Free Software Foundation; either ++ version 2.1 of the License, or (at your option) any later version. ++ ++ This library is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ Lesser General Public License for more details. ++ ++ You should have received a copy of the GNU Lesser General Public ++ License along with this library, in file named COPYING.LGPL; if not, ++ write to the Free Software Foundation, Inc., 59 Temple Place, ++ Suite 330, Boston, MA 02111-1307 USA ++ ++ */ ++ ++#include "udns.h" ++ ++int dns_findname(const struct dns_nameval *nv, const char *name) { ++ register const char *a, *b; ++ for(; nv->name; ++nv) ++ for(a = name, b = nv->name; ; ++a, ++b) ++ if (DNS_DNUC(*a) != *b) break; ++ else if (!*a) return nv->val; ++ return -1; ++} ++ ++const char *_dns_format_code(char *buf, const char *prefix, int code) { ++ char *bp = buf; ++ unsigned c, n; ++ do *bp++ = DNS_DNUC(*prefix); ++ while(*++prefix); ++ *bp++ = '#'; ++ if (code < 0) code = -code, *bp++ = '-'; ++ n = 0; c = code; ++ do ++n; ++ while((c /= 10)); ++ c = code; ++ bp[n--] = '\0'; ++ do bp[n--] = c % 10 + '0'; ++ while((c /= 10)); ++ return buf; ++} ++ ++const char *dns_strerror(int err) { ++ if (err >= 0) return "successeful completion"; ++ switch(err) { ++ case DNS_E_TEMPFAIL: return "temporary failure in name resolution"; ++ case DNS_E_PROTOCOL: return "protocol error"; ++ case DNS_E_NXDOMAIN: return "domain name does not exist"; ++ case DNS_E_NODATA: return "valid domain but no data of requested type"; ++ case DNS_E_NOMEM: return "out of memory"; ++ case DNS_E_BADQUERY: return "malformed query"; ++ default: return "unknown error"; ++ } ++} ++ ++const char *dns_version(void) { ++ return UDNS_VERSION; ++} +diff --git a/libudns/udns_parse.c b/libudns/udns_parse.c +new file mode 100644 +index 0000000..8924b15 +--- /dev/null ++++ b/libudns/udns_parse.c +@@ -0,0 +1,169 @@ ++/* udns_parse.c ++ raw DNS packet parsing routines ++ ++ Copyright (C) 2005 Michael Tokarev ++ This file is part of UDNS library, an async DNS stub resolver. ++ ++ This library is free software; you can redistribute it and/or ++ modify it under the terms of the GNU Lesser General Public ++ License as published by the Free Software Foundation; either ++ version 2.1 of the License, or (at your option) any later version. ++ ++ This library is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ Lesser General Public License for more details. ++ ++ You should have received a copy of the GNU Lesser General Public ++ License along with this library, in file named COPYING.LGPL; if not, ++ write to the Free Software Foundation, Inc., 59 Temple Place, ++ Suite 330, Boston, MA 02111-1307 USA ++ ++ */ ++ ++#include ++#include ++#include "udns.h" ++ ++dnscc_t *dns_skipdn(dnscc_t *cur, dnscc_t *end) { ++ unsigned c; ++ for(;;) { ++ if (cur >= end) ++ return NULL; ++ c = *cur++; ++ if (!c) ++ return cur; ++ if (c & 192) /* jump */ ++ return cur + 1 >= end ? NULL : cur + 1; ++ cur += c; ++ } ++} ++ ++int ++dns_getdn(dnscc_t *pkt, dnscc_t **cur, dnscc_t *end, ++ register dnsc_t *dn, unsigned dnsiz) { ++ unsigned c; ++ dnscc_t *pp = *cur; /* current packet pointer */ ++ dnsc_t *dp = dn; /* current dn pointer */ ++ dnsc_t *const de /* end of the DN dest */ ++ = dn + (dnsiz < DNS_MAXDN ? dnsiz : DNS_MAXDN); ++ dnscc_t *jump = NULL; /* ptr after first jump if any */ ++ unsigned loop = 100; /* jump loop counter */ ++ ++ for(;;) { /* loop by labels */ ++ if (pp >= end) /* reached end of packet? */ ++ return -1; ++ c = *pp++; /* length of the label */ ++ if (!c) { /* empty label: terminate */ ++ if (dn >= de) /* can't fit terminator */ ++ goto noroom; ++ *dp++ = 0; ++ /* return next pos: either after the first jump or current */ ++ *cur = jump ? jump : pp; ++ return dp - dn; ++ } ++ if (c & 192) { /* jump */ ++ if (pp >= end) /* eop instead of jump pos */ ++ return -1; ++ if (!jump) jump = pp + 1; /* remember first jump */ ++ else if (!--loop) return -1; /* too many jumps */ ++ c = ((c & ~192) << 8) | *pp; /* new pos */ ++ if (c < DNS_HSIZE) /* don't allow jump into the header */ ++ return -1; ++ pp = pkt + c; ++ continue; ++ } ++ if (c > DNS_MAXLABEL) /* too long label? */ ++ return -1; ++ if (pp + c > end) /* label does not fit in packet? */ ++ return -1; ++ if (dp + c + 1 > de) /* if enouth room for the label */ ++ goto noroom; ++ *dp++ = c; /* label length */ ++ memcpy(dp, pp, c); /* and the label itself */ ++ dp += c; ++ pp += c; /* advance to the next label */ ++ } ++noroom: ++ return dnsiz < DNS_MAXDN ? 0 : -1; ++} ++ ++void dns_rewind(struct dns_parse *p, dnscc_t *qdn) { ++ p->dnsp_qdn = qdn; ++ p->dnsp_cur = p->dnsp_ans; ++ p->dnsp_rrl = dns_numan(p->dnsp_pkt); ++ p->dnsp_ttl = 0xffffffffu; ++ p->dnsp_nrr = 0; ++} ++ ++void ++dns_initparse(struct dns_parse *p, dnscc_t *qdn, ++ dnscc_t *pkt, dnscc_t *cur, dnscc_t *end) { ++ p->dnsp_pkt = pkt; ++ p->dnsp_end = end; ++ p->dnsp_rrl = dns_numan(pkt); ++ p->dnsp_qdn = qdn; ++ assert(cur + 4 <= end); ++ if ((p->dnsp_qtyp = dns_get16(cur+0)) == DNS_T_ANY) p->dnsp_qtyp = 0; ++ if ((p->dnsp_qcls = dns_get16(cur+2)) == DNS_C_ANY) p->dnsp_qcls = 0; ++ p->dnsp_cur = p->dnsp_ans = cur + 4; ++ p->dnsp_ttl = 0xffffffffu; ++ p->dnsp_nrr = 0; ++} ++ ++int dns_nextrr(struct dns_parse *p, struct dns_rr *rr) { ++ dnscc_t *cur = p->dnsp_cur; ++ while(p->dnsp_rrl > 0) { ++ --p->dnsp_rrl; ++ if (dns_getdn(p->dnsp_pkt, &cur, p->dnsp_end, ++ rr->dnsrr_dn, sizeof(rr->dnsrr_dn)) <= 0) ++ return -1; ++ if (cur + 10 > p->dnsp_end) ++ return -1; ++ rr->dnsrr_typ = dns_get16(cur); ++ rr->dnsrr_cls = dns_get16(cur+2); ++ rr->dnsrr_ttl = dns_get32(cur+4); ++ rr->dnsrr_dsz = dns_get16(cur+8); ++ rr->dnsrr_dptr = cur = cur + 10; ++ rr->dnsrr_dend = cur = cur + rr->dnsrr_dsz; ++ if (cur > p->dnsp_end) ++ return -1; ++ if (p->dnsp_qdn && !dns_dnequal(p->dnsp_qdn, rr->dnsrr_dn)) ++ continue; ++ if ((!p->dnsp_qcls || p->dnsp_qcls == rr->dnsrr_cls) && ++ (!p->dnsp_qtyp || p->dnsp_qtyp == rr->dnsrr_typ)) { ++ p->dnsp_cur = cur; ++ ++p->dnsp_nrr; ++ if (p->dnsp_ttl > rr->dnsrr_ttl) p->dnsp_ttl = rr->dnsrr_ttl; ++ return 1; ++ } ++ if (p->dnsp_qdn && rr->dnsrr_typ == DNS_T_CNAME && !p->dnsp_nrr) { ++ if (dns_getdn(p->dnsp_pkt, &rr->dnsrr_dptr, p->dnsp_end, ++ p->dnsp_dnbuf, sizeof(p->dnsp_dnbuf)) <= 0 || ++ rr->dnsrr_dptr != rr->dnsrr_dend) ++ return -1; ++ p->dnsp_qdn = p->dnsp_dnbuf; ++ if (p->dnsp_ttl > rr->dnsrr_ttl) p->dnsp_ttl = rr->dnsrr_ttl; ++ } ++ } ++ p->dnsp_cur = cur; ++ return 0; ++} ++ ++int dns_stdrr_size(const struct dns_parse *p) { ++ return ++ dns_dntop_size(p->dnsp_qdn) + ++ (p->dnsp_qdn == dns_payload(p->dnsp_pkt) ? 0 : ++ dns_dntop_size(dns_payload(p->dnsp_pkt))); ++} ++ ++void *dns_stdrr_finish(struct dns_rr_null *ret, char *cp, ++ const struct dns_parse *p) { ++ cp += dns_dntop(p->dnsp_qdn, (ret->dnsn_cname = cp), DNS_MAXNAME); ++ if (p->dnsp_qdn == dns_payload(p->dnsp_pkt)) ++ ret->dnsn_qname = ret->dnsn_cname; ++ else ++ dns_dntop(dns_payload(p->dnsp_pkt), (ret->dnsn_qname = cp), DNS_MAXNAME); ++ ret->dnsn_ttl = p->dnsp_ttl; ++ return ret; ++} +diff --git a/libudns/udns_resolver.c b/libudns/udns_resolver.c +new file mode 100644 +index 0000000..72b7738 +--- /dev/null ++++ b/libudns/udns_resolver.c +@@ -0,0 +1,1325 @@ ++/* udns_resolver.c ++ resolver stuff (main module) ++ ++ Copyright (C) 2005 Michael Tokarev ++ This file is part of UDNS library, an async DNS stub resolver. ++ ++ This library is free software; you can redistribute it and/or ++ modify it under the terms of the GNU Lesser General Public ++ License as published by the Free Software Foundation; either ++ version 2.1 of the License, or (at your option) any later version. ++ ++ This library is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ Lesser General Public License for more details. ++ ++ You should have received a copy of the GNU Lesser General Public ++ License along with this library, in file named COPYING.LGPL; if not, ++ write to the Free Software Foundation, Inc., 59 Temple Place, ++ Suite 330, Boston, MA 02111-1307 USA ++ ++ */ ++ ++#ifdef HAVE_CONFIG_H ++# include "config.h" ++#endif ++#ifdef __MINGW32__ ++# include /* includes */ ++# include /* needed for struct in6_addr */ ++#else ++# include ++# include ++# include ++# include ++# include ++# include ++# ifdef HAVE_POLL ++# include ++# else ++# ifdef HAVE_SYS_SELECT_H ++# include ++# endif ++# endif ++# ifdef HAVE_TIMES ++# include ++# endif ++# define closesocket(sock) close(sock) ++#endif /* !__MINGW32__ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include "udns.h" ++ ++#ifndef EAFNOSUPPORT ++# define EAFNOSUPPORT EINVAL ++#endif ++#ifndef MSG_DONTWAIT ++# define MSG_DONTWAIT 0 ++#endif ++ ++struct dns_qlist { ++ struct dns_query *head, *tail; ++}; ++ ++struct dns_query { ++ struct dns_query *dnsq_next; /* double-linked list */ ++ struct dns_query *dnsq_prev; ++ unsigned dnsq_origdnl0; /* original query DN len w/o last 0 */ ++ unsigned dnsq_flags; /* control flags for this query */ ++ unsigned dnsq_servi; /* index of next server to try */ ++ unsigned dnsq_servwait; /* bitmask: servers left to wait */ ++ unsigned dnsq_servskip; /* bitmask: servers to skip */ ++ unsigned dnsq_servnEDNS0; /* bitmask: servers refusing EDNS0 */ ++ unsigned dnsq_try; /* number of tries made so far */ ++ dnscc_t *dnsq_nxtsrch; /* next search pointer @dnsc_srchbuf */ ++ time_t dnsq_deadline; /* when current try will expire */ ++ dns_parse_fn *dnsq_parse; /* parse: raw => application */ ++ dns_query_fn *dnsq_cbck; /* the callback to call when done */ ++ void *dnsq_cbdata; /* user data for the callback */ ++#ifndef NDEBUG ++ struct dns_ctx *dnsq_ctx; /* the resolver context */ ++#endif ++ /* char fields at the end to avoid padding */ ++ dnsc_t dnsq_id[2]; /* query ID */ ++ dnsc_t dnsq_typcls[4]; /* requested RR type+class */ ++ dnsc_t dnsq_dn[DNS_MAXDN+DNS_DNPAD]; /* the query DN +alignment */ ++}; ++ ++/* working with dns_query lists */ ++ ++static __inline void qlist_init(struct dns_qlist *list) { ++ list->head = list->tail = NULL; ++} ++ ++static __inline void qlist_remove(struct dns_qlist *list, struct dns_query *q) { ++ if (q->dnsq_prev) q->dnsq_prev->dnsq_next = q->dnsq_next; ++ else list->head = q->dnsq_next; ++ if (q->dnsq_next) q->dnsq_next->dnsq_prev = q->dnsq_prev; ++ else list->tail = q->dnsq_prev; ++} ++ ++static __inline void ++qlist_add_head(struct dns_qlist *list, struct dns_query *q) { ++ q->dnsq_next = list->head; ++ if (list->head) list->head->dnsq_prev = q; ++ else list->tail = q; ++ list->head = q; ++ q->dnsq_prev = NULL; ++} ++ ++static __inline void ++qlist_insert_after(struct dns_qlist *list, ++ struct dns_query *q, struct dns_query *prev) { ++ if ((q->dnsq_prev = prev) != NULL) { ++ if ((q->dnsq_next = prev->dnsq_next) != NULL) ++ q->dnsq_next->dnsq_prev = q; ++ else ++ list->tail = q; ++ prev->dnsq_next = q; ++ } ++ else ++ qlist_add_head(list, q); ++} ++ ++struct sockaddr_ns { ++ union { ++ struct sockaddr sa; ++ struct sockaddr_in sin; ++#ifdef HAVE_IPv6 ++ struct sockaddr_in6 sin6; ++#endif ++ }; ++}; ++ ++#define sin_eq(a,b) \ ++ ((a).sin_port == (b).sin_port && \ ++ (a).sin_addr.s_addr == (b).sin_addr.s_addr) ++#define sin6_eq(a,b) \ ++ ((a).sin6_port == (b).sin6_port && \ ++ memcmp(&(a).sin6_addr, &(b).sin6_addr, sizeof(struct in6_addr)) == 0) ++ ++struct dns_ctx { /* resolver context */ ++ /* settings */ ++ unsigned dnsc_flags; /* various flags */ ++ unsigned dnsc_timeout; /* timeout (base value) for queries */ ++ unsigned dnsc_ntries; /* number of retries */ ++ unsigned dnsc_ndots; /* ndots to assume absolute name */ ++ unsigned dnsc_port; /* default port (DNS_PORT) */ ++ unsigned dnsc_udpbuf; /* size of UDP buffer */ ++ /* array of nameserver addresses */ ++ struct sockaddr_ns dnsc_serv[DNS_MAXSERV]; ++ unsigned dnsc_nserv; /* number of nameservers */ ++ unsigned dnsc_salen; /* length of socket addresses */ ++ dnsc_t dnsc_srchbuf[1024]; /* buffer for searchlist */ ++ dnsc_t *dnsc_srchend; /* current end of srchbuf */ ++ ++ dns_utm_fn *dnsc_utmfn; /* register/cancel timer events */ ++ void *dnsc_utmctx; /* user timer context for utmfn() */ ++ time_t dnsc_utmexp; /* when user timer expires */ ++ ++ dns_dbgfn *dnsc_udbgfn; /* debugging function */ ++ ++ /* dynamic data */ ++ struct udns_jranctx dnsc_jran; /* random number generator state */ ++ unsigned dnsc_nextid; /* next queue ID to use if !0 */ ++ int dnsc_udpsock; /* UDP socket */ ++ struct dns_qlist dnsc_qactive; /* active list sorted by deadline */ ++ int dnsc_nactive; /* number entries in dnsc_qactive */ ++ dnsc_t *dnsc_pbuf; /* packet buffer (udpbuf size) */ ++ int dnsc_qstatus; /* last query status value */ ++}; ++ ++static const struct { ++ const char *name; ++ enum dns_opt opt; ++ unsigned offset; ++ unsigned min, max; ++} dns_opts[] = { ++#define opt(name,opt,field,min,max) \ ++ {name,opt,offsetof(struct dns_ctx,field),min,max} ++ opt("retrans", DNS_OPT_TIMEOUT, dnsc_timeout, 1,300), ++ opt("timeout", DNS_OPT_TIMEOUT, dnsc_timeout, 1,300), ++ opt("retry", DNS_OPT_NTRIES, dnsc_ntries, 1,50), ++ opt("attempts", DNS_OPT_NTRIES, dnsc_ntries, 1,50), ++ opt("ndots", DNS_OPT_NDOTS, dnsc_ndots, 0,1000), ++ opt("port", DNS_OPT_PORT, dnsc_port, 1,0xffff), ++ opt("udpbuf", DNS_OPT_UDPSIZE, dnsc_udpbuf, DNS_MAXPACKET,65536), ++#undef opt ++}; ++#define dns_ctxopt(ctx,idx) (*((unsigned*)(((char*)ctx)+dns_opts[idx].offset))) ++ ++#define ISSPACE(x) (x == ' ' || x == '\t' || x == '\r' || x == '\n') ++ ++struct dns_ctx dns_defctx; ++ ++#define SETCTX(ctx) if (!ctx) ctx = &dns_defctx ++#define SETCTXINITED(ctx) SETCTX(ctx); assert(CTXINITED(ctx)) ++#define CTXINITED(ctx) (ctx->dnsc_flags & DNS_INITED) ++#define SETCTXFRESH(ctx) SETCTXINITED(ctx); assert(!CTXOPEN(ctx)) ++#define SETCTXINACTIVE(ctx) \ ++ SETCTXINITED(ctx); assert(!ctx->dnsc_nactive) ++#define SETCTXOPEN(ctx) SETCTXINITED(ctx); assert(CTXOPEN(ctx)) ++#define CTXOPEN(ctx) (ctx->dnsc_udpsock >= 0) ++ ++#if defined(NDEBUG) || !defined(DEBUG) ++#define dns_assert_ctx(ctx) ++#else ++static void dns_assert_ctx(const struct dns_ctx *ctx) { ++ int nactive = 0; ++ const struct dns_query *q; ++ for(q = ctx->dnsc_qactive.head; q; q = q->dnsq_next) { ++ assert(q->dnsq_ctx == ctx); ++ assert(q == (q->dnsq_next ? ++ q->dnsq_next->dnsq_prev : ctx->dnsc_qactive.tail)); ++ assert(q == (q->dnsq_prev ? ++ q->dnsq_prev->dnsq_next : ctx->dnsc_qactive.head)); ++ ++nactive; ++ } ++ assert(nactive == ctx->dnsc_nactive); ++} ++#endif ++ ++enum { ++ DNS_INTERNAL = 0xffff, /* internal flags mask */ ++ DNS_INITED = 0x0001, /* the context is initialized */ ++ DNS_ASIS_DONE = 0x0002, /* search: skip the last as-is query */ ++ DNS_SEEN_NODATA = 0x0004, /* search: NODATA has been received */ ++}; ++ ++int dns_add_serv(struct dns_ctx *ctx, const char *serv) { ++ struct sockaddr_ns *sns; ++ SETCTXFRESH(ctx); ++ if (!serv) ++ return (ctx->dnsc_nserv = 0); ++ if (ctx->dnsc_nserv >= DNS_MAXSERV) ++ return errno = ENFILE, -1; ++ sns = &ctx->dnsc_serv[ctx->dnsc_nserv]; ++ memset(sns, 0, sizeof(*sns)); ++ if (dns_pton(AF_INET, serv, &sns->sin.sin_addr) > 0) { ++ sns->sin.sin_family = AF_INET; ++ return ++ctx->dnsc_nserv; ++ } ++#ifdef HAVE_IPv6 ++ if (dns_pton(AF_INET6, serv, &sns->sin6.sin6_addr) > 0) { ++ sns->sin6.sin6_family = AF_INET6; ++ return ++ctx->dnsc_nserv; ++ } ++#endif ++ errno = EINVAL; ++ return -1; ++} ++ ++int dns_add_serv_s(struct dns_ctx *ctx, const struct sockaddr *sa) { ++ SETCTXFRESH(ctx); ++ if (!sa) ++ return (ctx->dnsc_nserv = 0); ++ if (ctx->dnsc_nserv >= DNS_MAXSERV) ++ return errno = ENFILE, -1; ++#ifdef HAVE_IPv6 ++ else if (sa->sa_family == AF_INET6) ++ ctx->dnsc_serv[ctx->dnsc_nserv].sin6 = *(struct sockaddr_in6*)sa; ++#endif ++ else if (sa->sa_family == AF_INET) ++ ctx->dnsc_serv[ctx->dnsc_nserv].sin = *(struct sockaddr_in*)sa; ++ else ++ return errno = EAFNOSUPPORT, -1; ++ return ++ctx->dnsc_nserv; ++} ++ ++int dns_set_opts(struct dns_ctx *ctx, const char *opts) { ++ unsigned i, v; ++ int err = 0; ++ SETCTXINACTIVE(ctx); ++ for(;;) { ++ while(ISSPACE(*opts)) ++opts; ++ if (!*opts) break; ++ for(i = 0; ; ++i) { ++ if (i >= sizeof(dns_opts)/sizeof(dns_opts[0])) { ++err; break; } ++ v = strlen(dns_opts[i].name); ++ if (strncmp(dns_opts[i].name, opts, v) != 0 || ++ (opts[v] != ':' && opts[v] != '=')) ++ continue; ++ opts += v + 1; ++ v = 0; ++ if (*opts < '0' || *opts > '9') { ++err; break; } ++ do v = v * 10 + (*opts++ - '0'); ++ while (*opts >= '0' && *opts <= '9'); ++ if (v < dns_opts[i].min) v = dns_opts[i].min; ++ if (v > dns_opts[i].max) v = dns_opts[i].max; ++ dns_ctxopt(ctx, i) = v; ++ break; ++ } ++ while(*opts && !ISSPACE(*opts)) ++opts; ++ } ++ return err; ++} ++ ++int dns_set_opt(struct dns_ctx *ctx, enum dns_opt opt, int val) { ++ int prev; ++ unsigned i; ++ SETCTXINACTIVE(ctx); ++ for(i = 0; i < sizeof(dns_opts)/sizeof(dns_opts[0]); ++i) { ++ if (dns_opts[i].opt != opt) continue; ++ prev = dns_ctxopt(ctx, i); ++ if (val >= 0) { ++ unsigned v = val; ++ if (v < dns_opts[i].min || v > dns_opts[i].max) { ++ errno = EINVAL; ++ return -1; ++ } ++ dns_ctxopt(ctx, i) = v; ++ } ++ return prev; ++ } ++ if (opt == DNS_OPT_FLAGS) { ++ prev = ctx->dnsc_flags & ~DNS_INTERNAL; ++ if (val >= 0) ++ ctx->dnsc_flags = ++ (ctx->dnsc_flags & DNS_INTERNAL) | (val & ~DNS_INTERNAL); ++ return prev; ++ } ++ errno = ENOSYS; ++ return -1; ++} ++ ++int dns_add_srch(struct dns_ctx *ctx, const char *srch) { ++ int dnl; ++ SETCTXINACTIVE(ctx); ++ if (!srch) { ++ memset(ctx->dnsc_srchbuf, 0, sizeof(ctx->dnsc_srchbuf)); ++ ctx->dnsc_srchend = ctx->dnsc_srchbuf; ++ return 0; ++ } ++ dnl = ++ sizeof(ctx->dnsc_srchbuf) - (ctx->dnsc_srchend - ctx->dnsc_srchbuf) - 1; ++ dnl = dns_sptodn(srch, ctx->dnsc_srchend, dnl); ++ if (dnl > 0) ++ ctx->dnsc_srchend += dnl; ++ ctx->dnsc_srchend[0] = '\0'; /* we ensure the list is always ends at . */ ++ if (dnl > 0) ++ return 0; ++ errno = EINVAL; ++ return -1; ++} ++ ++static void dns_drop_utm(struct dns_ctx *ctx) { ++ if (ctx->dnsc_utmfn) ++ ctx->dnsc_utmfn(NULL, -1, ctx->dnsc_utmctx); ++ ctx->dnsc_utmctx = NULL; ++ ctx->dnsc_utmexp = -1; ++} ++ ++static void ++_dns_request_utm(struct dns_ctx *ctx, time_t now) { ++ struct dns_query *q; ++ time_t deadline; ++ int timeout; ++ q = ctx->dnsc_qactive.head; ++ if (!q) ++ deadline = -1, timeout = -1; ++ else if (!now || q->dnsq_deadline <= now) ++ deadline = 0, timeout = 0; ++ else ++ deadline = q->dnsq_deadline, timeout = (int)(deadline - now); ++ if (ctx->dnsc_utmexp == deadline) ++ return; ++ ctx->dnsc_utmfn(ctx, timeout, ctx->dnsc_utmctx); ++ ctx->dnsc_utmexp = deadline; ++} ++ ++static __inline void ++dns_request_utm(struct dns_ctx *ctx, time_t now) { ++ if (ctx->dnsc_utmfn) ++ _dns_request_utm(ctx, now); ++} ++ ++void dns_set_dbgfn(struct dns_ctx *ctx, dns_dbgfn *dbgfn) { ++ SETCTXINITED(ctx); ++ ctx->dnsc_udbgfn = dbgfn; ++} ++ ++void ++dns_set_tmcbck(struct dns_ctx *ctx, dns_utm_fn *fn, void *data) { ++ SETCTXINITED(ctx); ++ dns_drop_utm(ctx); ++ ctx->dnsc_utmfn = fn; ++ ctx->dnsc_utmctx = data; ++ if (CTXOPEN(ctx)) ++ dns_request_utm(ctx, 0); ++} ++ ++static unsigned dns_nonrandom_32(void) { ++#ifdef __MINGW32__ ++ FILETIME ft; ++ GetSystemTimeAsFileTime(&ft); ++ return ft.dwLowDateTime; ++#else ++ struct timeval tv; ++ gettimeofday(&tv, NULL); ++ return tv.tv_usec; ++#endif ++} ++ ++/* This is historic deprecated API */ ++UDNS_API unsigned dns_random16(void); ++unsigned dns_random16(void) { ++ unsigned x = dns_nonrandom_32(); ++ return (x ^ (x >> 16)) & 0xffff; ++} ++ ++static void dns_init_rng(struct dns_ctx *ctx) { ++ udns_jraninit(&ctx->dnsc_jran, dns_nonrandom_32()); ++ ctx->dnsc_nextid = 0; ++} ++ ++void dns_close(struct dns_ctx *ctx) { ++ struct dns_query *q, *p; ++ SETCTX(ctx); ++ if (CTXINITED(ctx)) { ++ if (ctx->dnsc_udpsock >= 0) ++ closesocket(ctx->dnsc_udpsock); ++ ctx->dnsc_udpsock = -1; ++ if (ctx->dnsc_pbuf) ++ free(ctx->dnsc_pbuf); ++ ctx->dnsc_pbuf = NULL; ++ q = ctx->dnsc_qactive.head; ++ while((p = q) != NULL) { ++ q = q->dnsq_next; ++ free(p); ++ } ++ qlist_init(&ctx->dnsc_qactive); ++ ctx->dnsc_nactive = 0; ++ dns_drop_utm(ctx); ++ } ++} ++ ++void dns_reset(struct dns_ctx *ctx) { ++ SETCTX(ctx); ++ dns_close(ctx); ++ memset(ctx, 0, sizeof(*ctx)); ++ ctx->dnsc_timeout = 4; ++ ctx->dnsc_ntries = 3; ++ ctx->dnsc_ndots = 1; ++ ctx->dnsc_udpbuf = DNS_EDNS0PACKET; ++ ctx->dnsc_port = DNS_PORT; ++ ctx->dnsc_udpsock = -1; ++ ctx->dnsc_srchend = ctx->dnsc_srchbuf; ++ qlist_init(&ctx->dnsc_qactive); ++ dns_init_rng(ctx); ++ ctx->dnsc_flags = DNS_INITED; ++} ++ ++struct dns_ctx *dns_new(const struct dns_ctx *copy) { ++ struct dns_ctx *ctx; ++ SETCTXINITED(copy); ++ dns_assert_ctx(copy); ++ ctx = malloc(sizeof(*ctx)); ++ if (!ctx) ++ return NULL; ++ *ctx = *copy; ++ ctx->dnsc_udpsock = -1; ++ qlist_init(&ctx->dnsc_qactive); ++ ctx->dnsc_nactive = 0; ++ ctx->dnsc_pbuf = NULL; ++ ctx->dnsc_qstatus = 0; ++ ctx->dnsc_srchend = ctx->dnsc_srchbuf + ++ (copy->dnsc_srchend - copy->dnsc_srchbuf); ++ ctx->dnsc_utmfn = NULL; ++ ctx->dnsc_utmctx = NULL; ++ dns_init_rng(ctx); ++ return ctx; ++} ++ ++void dns_free(struct dns_ctx *ctx) { ++ assert(ctx != NULL && ctx != &dns_defctx); ++ dns_reset(ctx); ++ free(ctx); ++} ++ ++int dns_open(struct dns_ctx *ctx) { ++ int sock; ++ unsigned i; ++ int port; ++ struct sockaddr_ns *sns; ++#ifdef HAVE_IPv6 ++ unsigned have_inet6 = 0; ++#endif ++ ++ SETCTXINITED(ctx); ++ assert(!CTXOPEN(ctx)); ++ ++ port = htons((unsigned short)ctx->dnsc_port); ++ /* ensure we have at least one server */ ++ if (!ctx->dnsc_nserv) { ++ sns = ctx->dnsc_serv; ++ sns->sin.sin_family = AF_INET; ++ sns->sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); ++ ctx->dnsc_nserv = 1; ++ } ++ ++ for (i = 0; i < ctx->dnsc_nserv; ++i) { ++ sns = &ctx->dnsc_serv[i]; ++ /* set port for each sockaddr */ ++#ifdef HAVE_IPv6 ++ if (sns->sa.sa_family == AF_INET6) { ++ if (!sns->sin6.sin6_port) sns->sin6.sin6_port = (unsigned short)port; ++ ++have_inet6; ++ } ++ else ++#endif ++ { ++ assert(sns->sa.sa_family == AF_INET); ++ if (!sns->sin.sin_port) sns->sin.sin_port = (unsigned short)port; ++ } ++ } ++ ++#ifdef HAVE_IPv6 ++ if (have_inet6 && have_inet6 < ctx->dnsc_nserv) { ++ /* convert all IPv4 addresses to IPv6 V4MAPPED */ ++ struct sockaddr_in6 sin6; ++ memset(&sin6, 0, sizeof(sin6)); ++ sin6.sin6_family = AF_INET6; ++ /* V4MAPPED: ::ffff:1.2.3.4 */ ++ sin6.sin6_addr.s6_addr[10] = 0xff; ++ sin6.sin6_addr.s6_addr[11] = 0xff; ++ for(i = 0; i < ctx->dnsc_nserv; ++i) { ++ sns = &ctx->dnsc_serv[i]; ++ if (sns->sa.sa_family == AF_INET) { ++ sin6.sin6_port = sns->sin.sin_port; ++ memcpy(sin6.sin6_addr.s6_addr + 4*3, &sns->sin.sin_addr, 4); ++ sns->sin6 = sin6; ++ } ++ } ++ } ++ ++ ctx->dnsc_salen = have_inet6 ? ++ sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in); ++ ++ if (have_inet6) ++ sock = socket(PF_INET6, SOCK_DGRAM, IPPROTO_UDP); ++ else ++ sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); ++#else /* !HAVE_IPv6 */ ++ sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); ++ ctx->dnsc_salen = sizeof(struct sockaddr_in); ++#endif /* HAVE_IPv6 */ ++ ++ if (sock < 0) { ++ ctx->dnsc_qstatus = DNS_E_TEMPFAIL; ++ return -1; ++ } ++#ifdef __MINGW32__ ++ { unsigned long on = 1; ++ if (ioctlsocket(sock, FIONBIO, &on) == SOCKET_ERROR) { ++ closesocket(sock); ++ ctx->dnsc_qstatus = DNS_E_TEMPFAIL; ++ return -1; ++ } ++ } ++#else /* !__MINGW32__ */ ++ if (fcntl(sock, F_SETFL, fcntl(sock, F_GETFL) | O_NONBLOCK) < 0 || ++ fcntl(sock, F_SETFD, FD_CLOEXEC) < 0) { ++ closesocket(sock); ++ ctx->dnsc_qstatus = DNS_E_TEMPFAIL; ++ return -1; ++ } ++#endif /* __MINGW32__ */ ++ /* allocate the packet buffer */ ++ if ((ctx->dnsc_pbuf = malloc(ctx->dnsc_udpbuf)) == NULL) { ++ closesocket(sock); ++ ctx->dnsc_qstatus = DNS_E_NOMEM; ++ errno = ENOMEM; ++ return -1; ++ } ++ ++ ctx->dnsc_udpsock = sock; ++ dns_request_utm(ctx, 0); ++ return sock; ++} ++ ++int dns_sock(const struct dns_ctx *ctx) { ++ SETCTXINITED(ctx); ++ return ctx->dnsc_udpsock; ++} ++ ++int dns_active(const struct dns_ctx *ctx) { ++ SETCTXINITED(ctx); ++ dns_assert_ctx(ctx); ++ return ctx->dnsc_nactive; ++} ++ ++int dns_status(const struct dns_ctx *ctx) { ++ SETCTX(ctx); ++ return ctx->dnsc_qstatus; ++} ++void dns_setstatus(struct dns_ctx *ctx, int status) { ++ SETCTX(ctx); ++ ctx->dnsc_qstatus = status; ++} ++ ++/* End the query: disconnect it from the active list, free it, ++ * and return the result to the caller. ++ */ ++static void ++dns_end_query(struct dns_ctx *ctx, struct dns_query *q, ++ int status, void *result) { ++ dns_query_fn *cbck = q->dnsq_cbck; ++ void *cbdata = q->dnsq_cbdata; ++ ctx->dnsc_qstatus = status; ++ assert((status < 0 && result == 0) || (status >= 0 && result != 0)); ++ assert(cbck != 0); /*XXX callback may be NULL */ ++ assert(ctx->dnsc_nactive > 0); ++ --ctx->dnsc_nactive; ++ qlist_remove(&ctx->dnsc_qactive, q); ++ /* force the query to be unconnected */ ++ /*memset(q, 0, sizeof(*q));*/ ++#ifndef NDEBUG ++ q->dnsq_ctx = NULL; ++#endif ++ free(q); ++ cbck(ctx, result, cbdata); ++} ++ ++#define DNS_DBG(ctx, code, sa, slen, pkt, plen) \ ++ do { \ ++ if (ctx->dnsc_udbgfn) \ ++ ctx->dnsc_udbgfn(code, (sa), slen, pkt, plen, 0, 0); \ ++ } while(0) ++#define DNS_DBGQ(ctx, q, code, sa, slen, pkt, plen) \ ++ do { \ ++ if (ctx->dnsc_udbgfn) \ ++ ctx->dnsc_udbgfn(code, (sa), slen, pkt, plen, q, q->dnsq_cbdata); \ ++ } while(0) ++ ++static void dns_newid(struct dns_ctx *ctx, struct dns_query *q) { ++ /* this is how we choose an identifier for a new query (qID). ++ * For now, it's just sequential number, incremented for every query, and ++ * thus obviously trivial to guess. ++ * There are two choices: ++ * a) use sequential numbers. It is plain insecure. In DNS, there are two ++ * places where random numbers are (or can) be used to increase security: ++ * random qID and random source port number. Without this randomness ++ * (udns uses fixed port for all queries), or when the randomness is weak, ++ * it's trivial to spoof query replies. With randomness however, it ++ * becomes a bit more difficult task. Too bad we only have 16 bits for ++ * our security, as qID is only two bytes. It isn't a security per se, ++ * to rely on those 16 bits - an attacker can just flood us with fake ++ * replies with all possible qIDs (only 65536 of them), and in this case, ++ * even if we'll use true random qIDs, we'll be in trouble (not protected ++ * against spoofing). Yes, this is only possible on a high-speed network ++ * (probably on the LAN only, since usually a border router for a LAN ++ * protects internal machines from packets with spoofed local addresses ++ * from outside, and usually a nameserver resides on LAN), but it's ++ * still very well possible to send us fake replies. ++ * In other words: there's nothing a DNS (stub) resolver can do against ++ * spoofing attacks, unless DNSSEC is in use, which helps here alot. ++ * Too bad that DNSSEC isn't widespread, so relying on it isn't an ++ * option in almost all cases... ++ * b) use random qID, based on some random-number generation mechanism. ++ * This way, we increase our protection a bit (see above - it's very weak ++ * still), but we also increase risk of qID reuse and matching late replies ++ * that comes to queries we've sent before against new queries. There are ++ * some more corner cases around that, as well - for example, normally, ++ * udns tries to find the query for a given reply by qID, *and* by ++ * verifying that the query DN and other parameters are also the same ++ * (so if the new query is against another domain name, old reply will ++ * be ignored automatically). But certain types of replies which we now ++ * handle - for example, FORMERR reply from servers which refuses to ++ * process EDNS0-enabled packets - comes without all the query parameters ++ * but the qID - so we're forced to use qID only when determining which ++ * query the given reply corresponds to. This makes us even more ++ * vulnerable to spoofing attacks, because an attacker don't even need to ++ * know which queries we perform to spoof the replies - he only needs to ++ * flood us with fake FORMERR "replies". ++ * ++ * That all to say: using sequential (or any other trivially guessable) ++ * numbers for qIDs is insecure, but the whole thing is inherently insecure ++ * as well, and this "extra weakness" that comes from weak qID choosing ++ * algorithm adds almost nothing to the underlying problem. ++ * ++ * It CAN NOT be made secure. Period. That's it. ++ * Unless we choose to implement DNSSEC, which is a whole different story. ++ * Forcing TCP mode makes it better, but who uses TCP for DNS anyway? ++ * (and it's hardly possible because of huge impact on the recursive ++ * nameservers). ++ * ++ * Note that ALL stub resolvers (again, unless they implement and enforce ++ * DNSSEC) suffers from this same problem. ++ * ++ * Here, I use a pseudo-random number generator for qIDs, instead of a ++ * simpler sequential IDs. This is _not_ more secure than sequential ++ * ID, but some found random IDs more enjoyeable for some reason. So ++ * here it goes. ++ */ ++ ++ /* Use random number and check if it's unique. ++ * If it's not, try again up to 5 times. ++ */ ++ unsigned loop; ++ dnsc_t c0, c1; ++ for(loop = 0; loop < 5; ++loop) { ++ const struct dns_query *c; ++ if (!ctx->dnsc_nextid) ++ ctx->dnsc_nextid = udns_jranval(&ctx->dnsc_jran); ++ c0 = ctx->dnsc_nextid & 0xff; ++ c1 = (ctx->dnsc_nextid >> 8) & 0xff; ++ ctx->dnsc_nextid >>= 16; ++ for(c = ctx->dnsc_qactive.head; c; c = c->dnsq_next) ++ if (c->dnsq_id[0] == c0 && c->dnsq_id[1] == c1) ++ break; /* found such entry, try again */ ++ if (!c) ++ break; ++ } ++ q->dnsq_id[0] = c0; q->dnsq_id[1] = c1; ++ ++ /* reset all parameters relevant for previous query lifetime */ ++ q->dnsq_try = 0; ++ q->dnsq_servi = 0; ++ /*XXX probably should keep dnsq_servnEDNS0 bits? ++ * See also comments in dns_ioevent() about FORMERR case */ ++ q->dnsq_servwait = q->dnsq_servskip = q->dnsq_servnEDNS0 = 0; ++} ++ ++/* Find next search suffix and fills in q->dnsq_dn. ++ * Return 0 if no more to try. */ ++static int dns_next_srch(struct dns_ctx *ctx, struct dns_query *q) { ++ unsigned dnl; ++ ++ for(;;) { ++ if (q->dnsq_nxtsrch > ctx->dnsc_srchend) ++ return 0; ++ dnl = dns_dnlen(q->dnsq_nxtsrch); ++ if (dnl + q->dnsq_origdnl0 <= DNS_MAXDN && ++ (*q->dnsq_nxtsrch || !(q->dnsq_flags & DNS_ASIS_DONE))) ++ break; ++ q->dnsq_nxtsrch += dnl; ++ } ++ memcpy(q->dnsq_dn + q->dnsq_origdnl0, q->dnsq_nxtsrch, dnl); ++ if (!*q->dnsq_nxtsrch) ++ q->dnsq_flags |= DNS_ASIS_DONE; ++ q->dnsq_nxtsrch += dnl; ++ dns_newid(ctx, q); /* new ID for new qDN */ ++ return 1; ++} ++ ++/* find the server to try for current iteration. ++ * Note that current dnsq_servi may point to a server we should skip -- ++ * in that case advance to the next server. ++ * Return true if found, false if all tried. ++ */ ++static int dns_find_serv(const struct dns_ctx *ctx, struct dns_query *q) { ++ while(q->dnsq_servi < ctx->dnsc_nserv) { ++ if (!(q->dnsq_servskip & (1 << q->dnsq_servi))) ++ return 1; ++ ++q->dnsq_servi; ++ } ++ return 0; ++} ++ ++/* format and send the query to a given server. ++ * In case of network problem (sendto() fails), return -1, ++ * else return 0. ++ */ ++static int ++dns_send_this(struct dns_ctx *ctx, struct dns_query *q, ++ unsigned servi, time_t now) { ++ unsigned qlen; ++ unsigned tries; ++ ++ { /* format the query buffer */ ++ dnsc_t *p = ctx->dnsc_pbuf; ++ memset(p, 0, DNS_HSIZE); ++ if (!(q->dnsq_flags & DNS_NORD)) p[DNS_H_F1] |= DNS_HF1_RD; ++ if (q->dnsq_flags & DNS_AAONLY) p[DNS_H_F1] |= DNS_HF1_AA; ++ if (q->dnsq_flags & DNS_SET_CD) p[DNS_H_F2] |= DNS_HF2_CD; ++ p[DNS_H_QDCNT2] = 1; ++ memcpy(p + DNS_H_QID, q->dnsq_id, 2); ++ p = dns_payload(p); ++ /* copy query dn */ ++ p += dns_dntodn(q->dnsq_dn, p, DNS_MAXDN); ++ /* query type and class */ ++ memcpy(p, q->dnsq_typcls, 4); p += 4; ++ /* add EDNS0 record. DO flag requires it */ ++ if (q->dnsq_flags & DNS_SET_DO || ++ (ctx->dnsc_udpbuf > DNS_MAXPACKET && ++ !(q->dnsq_servnEDNS0 & (1 << servi)))) { ++ *p++ = 0; /* empty (root) DN */ ++ p = dns_put16(p, DNS_T_OPT); ++ p = dns_put16(p, ctx->dnsc_udpbuf); ++ /* EDNS0 RCODE & VERSION; rest of the TTL field; RDLEN */ ++ memset(p, 0, 2+2+2); ++ if (q->dnsq_flags & DNS_SET_DO) p[2] |= DNS_EF1_DO; ++ p += 2+2+2; ++ ctx->dnsc_pbuf[DNS_H_ARCNT2] = 1; ++ } ++ qlen = p - ctx->dnsc_pbuf; ++ assert(qlen <= ctx->dnsc_udpbuf); ++ } ++ ++ /* send the query */ ++ tries = 10; ++ while (sendto(ctx->dnsc_udpsock, (void*)ctx->dnsc_pbuf, qlen, 0, ++ &ctx->dnsc_serv[servi].sa, ctx->dnsc_salen) < 0) { ++ /*XXX just ignore the sendto() error for now and try again. ++ * In the future, it may be possible to retrieve the error code ++ * and find which operation/query failed. ++ *XXX try the next server too? (if ENETUNREACH is returned immediately) ++ */ ++ if (--tries) continue; ++ /* if we can't send the query, fail it. */ ++ dns_end_query(ctx, q, DNS_E_TEMPFAIL, 0); ++ return -1; ++ } ++ DNS_DBGQ(ctx, q, 1, ++ &ctx->dnsc_serv[servi].sa, sizeof(struct sockaddr_ns), ++ ctx->dnsc_pbuf, qlen); ++ q->dnsq_servwait |= 1 << servi; /* expect reply from this ns */ ++ ++ q->dnsq_deadline = now + ++ (dns_find_serv(ctx, q) ? 1 : ctx->dnsc_timeout << q->dnsq_try); ++ ++ /* move the query to the proper place, according to the new deadline */ ++ qlist_remove(&ctx->dnsc_qactive, q); ++ { /* insert from the tail */ ++ struct dns_query *p; ++ for(p = ctx->dnsc_qactive.tail; p; p = p->dnsq_prev) ++ if (p->dnsq_deadline <= q->dnsq_deadline) ++ break; ++ qlist_insert_after(&ctx->dnsc_qactive, q, p); ++ } ++ ++ return 0; ++} ++ ++/* send the query out using next available server ++ * and add it to the active list, or, if no servers available, ++ * end it. ++ */ ++static void ++dns_send(struct dns_ctx *ctx, struct dns_query *q, time_t now) { ++ ++ /* if we can't send the query, return TEMPFAIL even when searching: ++ * we can't be sure whenever the name we tried to search exists or not, ++ * so don't continue searching, or we may find the wrong name. */ ++ ++ if (!dns_find_serv(ctx, q)) { ++ /* no more servers in this iteration. Try the next cycle */ ++ q->dnsq_servi = 0; /* reset */ ++ q->dnsq_try++; /* next try */ ++ if (q->dnsq_try >= ctx->dnsc_ntries || ++ !dns_find_serv(ctx, q)) { ++ /* no more servers and tries, fail the query */ ++ /* return TEMPFAIL even when searching: no more tries for this ++ * searchlist, and no single definitive reply (handled in dns_ioevent() ++ * in NOERROR or NXDOMAIN cases) => all nameservers failed to process ++ * current search list element, so we don't know whenever the name exists. ++ */ ++ dns_end_query(ctx, q, DNS_E_TEMPFAIL, 0); ++ return; ++ } ++ } ++ ++ dns_send_this(ctx, q, q->dnsq_servi++, now); ++} ++ ++static void dns_dummy_cb(struct dns_ctx *ctx, void *result, void *data) { ++ if (result) free(result); ++ data = ctx = 0; /* used */ ++} ++ ++/* The (only, main, real) query submission routine. ++ * Allocate new query structure, initialize it, check validity of ++ * parameters, and add it to the head of the active list, without ++ * trying to send it (to be picked up on next event). ++ * Error return (without calling the callback routine) - ++ * no memory or wrong parameters. ++ *XXX The `no memory' case probably should go to the callback anyway... ++ */ ++struct dns_query * ++dns_submit_dn(struct dns_ctx *ctx, ++ dnscc_t *dn, int qcls, int qtyp, int flags, ++ dns_parse_fn *parse, dns_query_fn *cbck, void *data) { ++ struct dns_query *q; ++ SETCTXOPEN(ctx); ++ dns_assert_ctx(ctx); ++ ++ q = calloc(sizeof(*q), 1); ++ if (!q) { ++ ctx->dnsc_qstatus = DNS_E_NOMEM; ++ return NULL; ++ } ++ ++#ifndef NDEBUG ++ q->dnsq_ctx = ctx; ++#endif ++ q->dnsq_parse = parse; ++ q->dnsq_cbck = cbck ? cbck : dns_dummy_cb; ++ q->dnsq_cbdata = data; ++ ++ q->dnsq_origdnl0 = dns_dntodn(dn, q->dnsq_dn, sizeof(q->dnsq_dn)); ++ assert(q->dnsq_origdnl0 > 0); ++ --q->dnsq_origdnl0; /* w/o the trailing 0 */ ++ dns_put16(q->dnsq_typcls+0, qtyp); ++ dns_put16(q->dnsq_typcls+2, qcls); ++ q->dnsq_flags = (flags | ctx->dnsc_flags) & ~DNS_INTERNAL; ++ ++ if (flags & DNS_NOSRCH || ++ dns_dnlabels(q->dnsq_dn) > ctx->dnsc_ndots) { ++ q->dnsq_nxtsrch = flags & DNS_NOSRCH ? ++ ctx->dnsc_srchend /* end of the search list if no search requested */ : ++ ctx->dnsc_srchbuf /* beginning of the list, but try as-is first */; ++ q->dnsq_flags |= DNS_ASIS_DONE; ++ dns_newid(ctx, q); ++ } ++ else { ++ q->dnsq_nxtsrch = ctx->dnsc_srchbuf; ++ dns_next_srch(ctx, q); ++ } ++ ++ /* q->dnsq_deadline is set to 0 (calloc above): the new query is ++ * "already expired" when first inserted into queue, so it's safe ++ * to insert it into the head of the list. Next call to dns_timeouts() ++ * will actually send it. ++ */ ++ qlist_add_head(&ctx->dnsc_qactive, q); ++ ++ctx->dnsc_nactive; ++ dns_request_utm(ctx, 0); ++ ++ return q; ++} ++ ++struct dns_query * ++dns_submit_p(struct dns_ctx *ctx, ++ const char *name, int qcls, int qtyp, int flags, ++ dns_parse_fn *parse, dns_query_fn *cbck, void *data) { ++ int isabs; ++ SETCTXOPEN(ctx); ++ if (dns_ptodn(name, 0, ctx->dnsc_pbuf, DNS_MAXDN, &isabs) <= 0) { ++ ctx->dnsc_qstatus = DNS_E_BADQUERY; ++ return NULL; ++ } ++ if (isabs) ++ flags |= DNS_NOSRCH; ++ return ++ dns_submit_dn(ctx, ctx->dnsc_pbuf, qcls, qtyp, flags, parse, cbck, data); ++} ++ ++/* process readable fd condition. ++ * To be usable in edge-triggered environment, the routine ++ * should consume all input so it should loop over. ++ * Note it isn't really necessary to loop here, because ++ * an application may perform the loop just fine by it's own, ++ * but in this case we should return some sensitive result, ++ * to indicate when to stop calling and error conditions. ++ * Note also we may encounter all sorts of recvfrom() ++ * errors which aren't fatal, and at the same time we may ++ * loop forever if an error IS fatal. ++ */ ++void dns_ioevent(struct dns_ctx *ctx, time_t now) { ++ int r; ++ unsigned servi; ++ struct dns_query *q; ++ dnsc_t *pbuf; ++ dnscc_t *pend, *pcur; ++ void *result; ++ struct sockaddr_ns sns; ++ socklen_t slen; ++ ++ SETCTX(ctx); ++ if (!CTXOPEN(ctx)) ++ return; ++ dns_assert_ctx(ctx); ++ pbuf = ctx->dnsc_pbuf; ++ ++ if (!now) now = time(NULL); ++ ++again: /* receive the reply */ ++ ++ slen = sizeof(sns); ++ r = recvfrom(ctx->dnsc_udpsock, (void*)pbuf, ctx->dnsc_udpbuf, ++ MSG_DONTWAIT, &sns.sa, &slen); ++ if (r < 0) { ++ /*XXX just ignore recvfrom() errors for now. ++ * in the future it may be possible to determine which ++ * query failed and requeue it. ++ * Note there may be various error conditions, triggered ++ * by both local problems and remote problems. It isn't ++ * quite trivial to determine whenever an error is local ++ * or remote. On local errors, we should stop, while ++ * remote errors should be ignored (for now anyway). ++ */ ++#ifdef __MINGW32__ ++ if (WSAGetLastError() == WSAEWOULDBLOCK) ++#else ++ if (errno == EAGAIN) ++#endif ++ { ++ dns_request_utm(ctx, now); ++ return; ++ } ++ goto again; ++ } ++ ++ pend = pbuf + r; ++ pcur = dns_payload(pbuf); ++ ++ /* check reply header */ ++ if (pcur > pend || dns_numqd(pbuf) > 1 || dns_opcode(pbuf) != 0) { ++ DNS_DBG(ctx, -1/*bad reply*/, &sns.sa, slen, pbuf, r); ++ goto again; ++ } ++ ++ /* find the matching query, by qID */ ++ for (q = ctx->dnsc_qactive.head; ; q = q->dnsq_next) { ++ if (!q) { ++ /* no more requests: old reply? */ ++ DNS_DBG(ctx, -5/*no matching query*/, &sns.sa, slen, pbuf, r); ++ goto again; ++ } ++ if (pbuf[DNS_H_QID1] == q->dnsq_id[0] && ++ pbuf[DNS_H_QID2] == q->dnsq_id[1]) ++ break; ++ } ++ ++ /* if we have numqd, compare with our query qDN */ ++ if (dns_numqd(pbuf)) { ++ /* decode the qDN */ ++ dnsc_t dn[DNS_MAXDN]; ++ if (dns_getdn(pbuf, &pcur, pend, dn, sizeof(dn)) < 0 || ++ pcur + 4 > pend) { ++ DNS_DBG(ctx, -1/*bad reply*/, &sns.sa, slen, pbuf, r); ++ goto again; ++ } ++ if (!dns_dnequal(dn, q->dnsq_dn) || ++ memcmp(pcur, q->dnsq_typcls, 4) != 0) { ++ /* not this query */ ++ DNS_DBG(ctx, -5/*no matching query*/, &sns.sa, slen, pbuf, r); ++ goto again; ++ } ++ /* here, query match, and pcur points past qDN in query section in pbuf */ ++ } ++ /* if no numqd, we only allow FORMERR rcode */ ++ else if (dns_rcode(pbuf) != DNS_R_FORMERR) { ++ /* treat it as bad reply if !FORMERR */ ++ DNS_DBG(ctx, -1/*bad reply*/, &sns.sa, slen, pbuf, r); ++ goto again; ++ } ++ else { ++ /* else it's FORMERR, handled below */ ++ } ++ ++ /* find server */ ++#ifdef HAVE_IPv6 ++ if (sns.sa.sa_family == AF_INET6 && slen >= sizeof(sns.sin6)) { ++ for(servi = 0; servi < ctx->dnsc_nserv; ++servi) ++ if (sin6_eq(ctx->dnsc_serv[servi].sin6, sns.sin6)) ++ break; ++ } ++ else ++#endif ++ if (sns.sa.sa_family == AF_INET && slen >= sizeof(sns.sin)) { ++ for(servi = 0; servi < ctx->dnsc_nserv; ++servi) ++ if (sin_eq(ctx->dnsc_serv[servi].sin, sns.sin)) ++ break; ++ } ++ else ++ servi = ctx->dnsc_nserv; ++ ++ /* check if we expect reply from this server. ++ * Note we can receive reply from first try if we're already at next */ ++ if (!(q->dnsq_servwait & (1 << servi))) { /* if ever asked this NS */ ++ DNS_DBG(ctx, -2/*wrong server*/, &sns.sa, slen, pbuf, r); ++ goto again; ++ } ++ ++ /* we got (some) reply for our query */ ++ ++ DNS_DBGQ(ctx, q, 0, &sns.sa, slen, pbuf, r); ++ q->dnsq_servwait &= ~(1 << servi); /* don't expect reply from this serv */ ++ ++ /* process the RCODE */ ++ switch(dns_rcode(pbuf)) { ++ ++ case DNS_R_NOERROR: ++ if (dns_tc(pbuf)) { ++ /* possible truncation. We can't deal with it. */ ++ /*XXX for now, treat TC bit the same as SERVFAIL. ++ * It is possible to: ++ * a) try to decode the reply - may be ANSWER section is ok; ++ * b) check if server understands EDNS0, and if it is, and ++ * answer still don't fit, end query. ++ */ ++ break; ++ } ++ if (!dns_numan(pbuf)) { /* no data of requested type */ ++ if (dns_next_srch(ctx, q)) { ++ /* if we're searching, try next searchlist element, ++ * but remember NODATA reply. */ ++ q->dnsq_flags |= DNS_SEEN_NODATA; ++ dns_send(ctx, q, now); ++ } ++ else ++ /* else - nothing to search any more - finish the query. ++ * It will be NODATA since we've seen a NODATA reply. */ ++ dns_end_query(ctx, q, DNS_E_NODATA, 0); ++ } ++ /* we've got a positive reply here */ ++ else if (q->dnsq_parse) { ++ /* if we have parsing routine, call it and return whatever it returned */ ++ /* don't try to re-search if NODATA here. For example, ++ * if we asked for A but only received CNAME. Unless we'll ++ * someday do recursive queries. And that's problematic too, since ++ * we may be dealing with specific AA-only nameservers for a given ++ * domain, but CNAME points elsewhere... ++ */ ++ r = q->dnsq_parse(q->dnsq_dn, pbuf, pcur, pend, &result); ++ dns_end_query(ctx, q, r, r < 0 ? NULL : result); ++ } ++ /* else just malloc+copy the raw DNS reply */ ++ else if ((result = malloc(r)) == NULL) ++ dns_end_query(ctx, q, DNS_E_NOMEM, NULL); ++ else { ++ memcpy(result, pbuf, r); ++ dns_end_query(ctx, q, r, result); ++ } ++ goto again; ++ ++ case DNS_R_NXDOMAIN: /* Non-existing domain. */ ++ if (dns_next_srch(ctx, q)) ++ /* more search entries exists, try them. */ ++ dns_send(ctx, q, now); ++ else ++ /* nothing to search anymore. End the query, returning either NODATA ++ * if we've seen it before, or NXDOMAIN if not. */ ++ dns_end_query(ctx, q, ++ q->dnsq_flags & DNS_SEEN_NODATA ? DNS_E_NODATA : DNS_E_NXDOMAIN, 0); ++ goto again; ++ ++ case DNS_R_FORMERR: ++ case DNS_R_NOTIMPL: ++ /* for FORMERR and NOTIMPL rcodes, if we tried EDNS0-enabled query, ++ * try w/o EDNS0. */ ++ if (ctx->dnsc_udpbuf > DNS_MAXPACKET && ++ !(q->dnsq_servnEDNS0 & (1 << servi))) { ++ /* we always trying EDNS0 first if enabled, and retry a given query ++ * if not available. Maybe it's better to remember inavailability of ++ * EDNS0 in ctx as a per-NS flag, and never try again for this NS. ++ * For long-running applications.. maybe they will change the nameserver ++ * while we're running? :) Also, since FORMERR is the only rcode we ++ * allow to be header-only, and in this case the only check we do to ++ * find a query it belongs to is qID (not qDN+qCLS+qTYP), it's much ++ * easier to spoof and to force us to perform non-EDNS0 queries only... ++ */ ++ q->dnsq_servnEDNS0 |= 1 << servi; ++ dns_send_this(ctx, q, servi, now); ++ goto again; ++ } ++ /* else we handle it the same as SERVFAIL etc */ ++ ++ case DNS_R_SERVFAIL: ++ case DNS_R_REFUSED: ++ /* for these rcodes, advance this request ++ * to the next server and reschedule */ ++ default: /* unknown rcode? hmmm... */ ++ break; ++ } ++ ++ /* here, we received unexpected reply */ ++ q->dnsq_servskip |= (1 << servi); /* don't retry this server */ ++ ++ /* we don't expect replies from this server anymore. ++ * But there may be other servers. Some may be still processing our ++ * query, and some may be left to try. ++ * We just ignore this reply and wait a bit more if some NSes haven't ++ * replied yet (dnsq_servwait != 0), and let the situation to be handled ++ * on next event processing. Timeout for this query is set correctly, ++ * if not taking into account the one-second difference - we can try ++ * next server in the same iteration sooner. ++ */ ++ ++ /* try next server */ ++ if (!q->dnsq_servwait) { ++ /* next retry: maybe some other servers will reply next time. ++ * dns_send() will end the query for us if no more servers to try. ++ * Note we can't continue with the next searchlist element here: ++ * we don't know if the current qdn exists or not, there's no definitive ++ * answer yet (which is seen in cases above). ++ *XXX standard resolver also tries as-is query in case all nameservers ++ * failed to process our query and if not tried before. We don't do it. ++ */ ++ dns_send(ctx, q, now); ++ } ++ else { ++ /* else don't do anything - not all servers replied yet */ ++ } ++ goto again; ++ ++} ++ ++/* handle all timeouts */ ++int dns_timeouts(struct dns_ctx *ctx, int maxwait, time_t now) { ++ /* this is a hot routine */ ++ struct dns_query *q; ++ ++ SETCTX(ctx); ++ dns_assert_ctx(ctx); ++ ++ /* Pick up first entry from query list. ++ * If its deadline has passed, (re)send it ++ * (dns_send() will move it next in the list). ++ * If not, this is the query which determines the closest deadline. ++ */ ++ ++ q = ctx->dnsc_qactive.head; ++ if (!q) ++ return maxwait; ++ if (!now) ++ now = time(NULL); ++ do { ++ if (q->dnsq_deadline > now) { /* first non-expired query */ ++ int w = (int)(q->dnsq_deadline - now); ++ if (maxwait < 0 || maxwait > w) ++ maxwait = w; ++ break; ++ } ++ else { ++ /* process expired deadline */ ++ dns_send(ctx, q, now); ++ } ++ } while((q = ctx->dnsc_qactive.head) != NULL); ++ ++ dns_request_utm(ctx, now); /* update timer with new deadline */ ++ return maxwait; ++} ++ ++struct dns_resolve_data { ++ int dnsrd_done; ++ void *dnsrd_result; ++}; ++ ++static void dns_resolve_cb(struct dns_ctx *ctx, void *result, void *data) { ++ struct dns_resolve_data *d = data; ++ d->dnsrd_result = result; ++ d->dnsrd_done = 1; ++ ctx = ctx; ++} ++ ++void *dns_resolve(struct dns_ctx *ctx, struct dns_query *q) { ++ time_t now; ++ struct dns_resolve_data d; ++ int n; ++ SETCTXOPEN(ctx); ++ ++ if (!q) ++ return NULL; ++ ++ assert(ctx == q->dnsq_ctx); ++ dns_assert_ctx(ctx); ++ /* do not allow re-resolving syncronous queries */ ++ assert(q->dnsq_cbck != dns_resolve_cb && "can't resolve syncronous query"); ++ if (q->dnsq_cbck == dns_resolve_cb) { ++ ctx->dnsc_qstatus = DNS_E_BADQUERY; ++ return NULL; ++ } ++ q->dnsq_cbck = dns_resolve_cb; ++ q->dnsq_cbdata = &d; ++ d.dnsrd_done = 0; ++ ++ now = time(NULL); ++ while(!d.dnsrd_done && (n = dns_timeouts(ctx, -1, now)) >= 0) { ++#ifdef HAVE_POLL ++ struct pollfd pfd; ++ pfd.fd = ctx->dnsc_udpsock; ++ pfd.events = POLLIN; ++ n = poll(&pfd, 1, n * 1000); ++#else ++ fd_set rfd; ++ struct timeval tv; ++ FD_ZERO(&rfd); ++ FD_SET(ctx->dnsc_udpsock, &rfd); ++ tv.tv_sec = n; tv.tv_usec = 0; ++ n = select(ctx->dnsc_udpsock + 1, &rfd, NULL, NULL, &tv); ++#endif ++ now = time(NULL); ++ if (n > 0) ++ dns_ioevent(ctx, now); ++ } ++ ++ return d.dnsrd_result; ++} ++ ++void *dns_resolve_dn(struct dns_ctx *ctx, ++ dnscc_t *dn, int qcls, int qtyp, int flags, ++ dns_parse_fn *parse) { ++ return ++ dns_resolve(ctx, ++ dns_submit_dn(ctx, dn, qcls, qtyp, flags, parse, NULL, NULL)); ++} ++ ++void *dns_resolve_p(struct dns_ctx *ctx, ++ const char *name, int qcls, int qtyp, int flags, ++ dns_parse_fn *parse) { ++ return ++ dns_resolve(ctx, ++ dns_submit_p(ctx, name, qcls, qtyp, flags, parse, NULL, NULL)); ++} ++ ++int dns_cancel(struct dns_ctx *ctx, struct dns_query *q) { ++ SETCTX(ctx); ++ dns_assert_ctx(ctx); ++ assert(q->dnsq_ctx == ctx); ++ /* do not allow cancelling syncronous queries */ ++ assert(q->dnsq_cbck != dns_resolve_cb && "can't cancel syncronous query"); ++ if (q->dnsq_cbck == dns_resolve_cb) ++ return (ctx->dnsc_qstatus = DNS_E_BADQUERY); ++ qlist_remove(&ctx->dnsc_qactive, q); ++ --ctx->dnsc_nactive; ++ dns_request_utm(ctx, 0); ++ return 0; ++} ++ +diff --git a/libudns/udns_rr_a.c b/libudns/udns_rr_a.c +new file mode 100644 +index 0000000..731c3ca +--- /dev/null ++++ b/libudns/udns_rr_a.c +@@ -0,0 +1,123 @@ ++/* udns_rr_a.c ++ parse/query A/AAAA IN records ++ ++ Copyright (C) 2005 Michael Tokarev ++ This file is part of UDNS library, an async DNS stub resolver. ++ ++ This library is free software; you can redistribute it and/or ++ modify it under the terms of the GNU Lesser General Public ++ License as published by the Free Software Foundation; either ++ version 2.1 of the License, or (at your option) any later version. ++ ++ This library is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ Lesser General Public License for more details. ++ ++ You should have received a copy of the GNU Lesser General Public ++ License along with this library, in file named COPYING.LGPL; if not, ++ write to the Free Software Foundation, Inc., 59 Temple Place, ++ Suite 330, Boston, MA 02111-1307 USA ++ ++ */ ++ ++#include ++#include ++#include ++#ifndef __MINGW32__ ++# include ++# include ++#endif ++#include "udns.h" ++ ++/* here, we use common routine to parse both IPv4 and IPv6 addresses. ++ */ ++ ++/* this structure should match dns_rr_a[46] */ ++struct dns_rr_a { ++ dns_rr_common(dnsa); ++ unsigned char *dnsa_addr; ++}; ++ ++static int ++dns_parse_a(dnscc_t *qdn, dnscc_t *pkt, dnscc_t *cur, dnscc_t *end, ++ void **result, unsigned dsize) { ++ struct dns_rr_a *ret; ++ struct dns_parse p; ++ struct dns_rr rr; ++ int r; ++ ++ /* first, validate and count number of addresses */ ++ dns_initparse(&p, qdn, pkt, cur, end); ++ while((r = dns_nextrr(&p, &rr)) > 0) ++ if (rr.dnsrr_dsz != dsize) ++ return DNS_E_PROTOCOL; ++ if (r < 0) ++ return DNS_E_PROTOCOL; ++ else if (!p.dnsp_nrr) ++ return DNS_E_NODATA; ++ ++ ret = malloc(sizeof(*ret) + dsize * p.dnsp_nrr + dns_stdrr_size(&p)); ++ if (!ret) ++ return DNS_E_NOMEM; ++ ++ ret->dnsa_nrr = p.dnsp_nrr; ++ ret->dnsa_addr = (unsigned char*)(ret+1); ++ ++ /* copy the RRs */ ++ for (dns_rewind(&p, qdn), r = 0; dns_nextrr(&p, &rr); ++r) ++ memcpy(ret->dnsa_addr + dsize * r, rr.dnsrr_dptr, dsize); ++ ++ dns_stdrr_finish((struct dns_rr_null *)ret, ++ (char *)(ret->dnsa_addr + dsize * p.dnsp_nrr), &p); ++ *result = ret; ++ return 0; ++} ++ ++int ++dns_parse_a4(dnscc_t *qdn, dnscc_t *pkt, dnscc_t *cur, dnscc_t *end, ++ void **result) { ++#ifdef AF_INET ++ assert(sizeof(struct in_addr) == 4); ++#endif ++ assert(dns_get16(cur+2) == DNS_C_IN && dns_get16(cur+0) == DNS_T_A); ++ return dns_parse_a(qdn, pkt, cur, end, result, 4); ++} ++ ++struct dns_query * ++dns_submit_a4(struct dns_ctx *ctx, const char *name, int flags, ++ dns_query_a4_fn *cbck, void *data) { ++ return ++ dns_submit_p(ctx, name, DNS_C_IN, DNS_T_A, flags, ++ dns_parse_a4, (dns_query_fn*)cbck, data); ++} ++ ++struct dns_rr_a4 * ++dns_resolve_a4(struct dns_ctx *ctx, const char *name, int flags) { ++ return (struct dns_rr_a4 *) ++ dns_resolve_p(ctx, name, DNS_C_IN, DNS_T_A, flags, dns_parse_a4); ++} ++ ++int ++dns_parse_a6(dnscc_t *qdn, dnscc_t *pkt, dnscc_t *cur, dnscc_t *end, ++ void **result) { ++#ifdef AF_INET6 ++ assert(sizeof(struct in6_addr) == 16); ++#endif ++ assert(dns_get16(cur+2) == DNS_C_IN && dns_get16(cur+0) == DNS_T_AAAA); ++ return dns_parse_a(qdn, pkt, cur, end, result, 16); ++} ++ ++struct dns_query * ++dns_submit_a6(struct dns_ctx *ctx, const char *name, int flags, ++ dns_query_a6_fn *cbck, void *data) { ++ return ++ dns_submit_p(ctx, name, DNS_C_IN, DNS_T_AAAA, flags, ++ dns_parse_a6, (dns_query_fn*)cbck, data); ++} ++ ++struct dns_rr_a6 * ++dns_resolve_a6(struct dns_ctx *ctx, const char *name, int flags) { ++ return (struct dns_rr_a6 *) ++ dns_resolve_p(ctx, name, DNS_C_IN, DNS_T_AAAA, flags, dns_parse_a6); ++} +diff --git a/libudns/udns_rr_mx.c b/libudns/udns_rr_mx.c +new file mode 100644 +index 0000000..0904e01 +--- /dev/null ++++ b/libudns/udns_rr_mx.c +@@ -0,0 +1,91 @@ ++/* udns_rr_mx.c ++ parse/query MX IN records ++ ++ Copyright (C) 2005 Michael Tokarev ++ This file is part of UDNS library, an async DNS stub resolver. ++ ++ This library is free software; you can redistribute it and/or ++ modify it under the terms of the GNU Lesser General Public ++ License as published by the Free Software Foundation; either ++ version 2.1 of the License, or (at your option) any later version. ++ ++ This library is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ Lesser General Public License for more details. ++ ++ You should have received a copy of the GNU Lesser General Public ++ License along with this library, in file named COPYING.LGPL; if not, ++ write to the Free Software Foundation, Inc., 59 Temple Place, ++ Suite 330, Boston, MA 02111-1307 USA ++ ++ */ ++ ++#include ++#include ++#include ++#include "udns.h" ++ ++int ++dns_parse_mx(dnscc_t *qdn, dnscc_t *pkt, dnscc_t *cur, dnscc_t *end, ++ void **result) { ++ struct dns_rr_mx *ret; ++ struct dns_parse p; ++ struct dns_rr rr; ++ int r, l; ++ char *sp; ++ dnsc_t mx[DNS_MAXDN]; ++ ++ assert(dns_get16(cur+2) == DNS_C_IN && dns_get16(cur+0) == DNS_T_MX); ++ ++ /* first, validate the answer and count size of the result */ ++ l = 0; ++ dns_initparse(&p, qdn, pkt, cur, end); ++ while((r = dns_nextrr(&p, &rr)) > 0) { ++ cur = rr.dnsrr_dptr + 2; ++ r = dns_getdn(pkt, &cur, end, mx, sizeof(mx)); ++ if (r <= 0 || cur != rr.dnsrr_dend) ++ return DNS_E_PROTOCOL; ++ l += dns_dntop_size(mx); ++ } ++ if (r < 0) ++ return DNS_E_PROTOCOL; ++ if (!p.dnsp_nrr) ++ return DNS_E_NODATA; ++ ++ /* next, allocate and set up result */ ++ l += dns_stdrr_size(&p); ++ ret = malloc(sizeof(*ret) + sizeof(struct dns_mx) * p.dnsp_nrr + l); ++ if (!ret) ++ return DNS_E_NOMEM; ++ ret->dnsmx_nrr = p.dnsp_nrr; ++ ret->dnsmx_mx = (struct dns_mx *)(ret+1); ++ ++ /* and 3rd, fill in result, finally */ ++ sp = (char*)(ret->dnsmx_mx + p.dnsp_nrr); ++ for (dns_rewind(&p, qdn), r = 0; dns_nextrr(&p, &rr); ++r) { ++ ret->dnsmx_mx[r].name = sp; ++ cur = rr.dnsrr_dptr; ++ ret->dnsmx_mx[r].priority = dns_get16(cur); ++ cur += 2; ++ dns_getdn(pkt, &cur, end, mx, sizeof(mx)); ++ sp += dns_dntop(mx, sp, DNS_MAXNAME); ++ } ++ dns_stdrr_finish((struct dns_rr_null *)ret, sp, &p); ++ *result = ret; ++ return 0; ++} ++ ++struct dns_query * ++dns_submit_mx(struct dns_ctx *ctx, const char *name, int flags, ++ dns_query_mx_fn *cbck, void *data) { ++ return ++ dns_submit_p(ctx, name, DNS_C_IN, DNS_T_MX, flags, ++ dns_parse_mx, (dns_query_fn *)cbck, data); ++} ++ ++struct dns_rr_mx * ++dns_resolve_mx(struct dns_ctx *ctx, const char *name, int flags) { ++ return (struct dns_rr_mx *) ++ dns_resolve_p(ctx, name, DNS_C_IN, DNS_T_MX, flags, dns_parse_mx); ++} +diff --git a/libudns/udns_rr_naptr.c b/libudns/udns_rr_naptr.c +new file mode 100644 +index 0000000..da30069 +--- /dev/null ++++ b/libudns/udns_rr_naptr.c +@@ -0,0 +1,128 @@ ++/* udns_rr_naptr.c ++ parse/query NAPTR IN records ++ ++ Copyright (C) 2005 Michael Tokarev ++ Copyright (C) 2006 Mikael Magnusson ++ This file is part of UDNS library, an async DNS stub resolver. ++ ++ This library is free software; you can redistribute it and/or ++ modify it under the terms of the GNU Lesser General Public ++ License as published by the Free Software Foundation; either ++ version 2.1 of the License, or (at your option) any later version. ++ ++ This library is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ Lesser General Public License for more details. ++ ++ You should have received a copy of the GNU Lesser General Public ++ License along with this library, in file named COPYING.LGPL; if not, ++ write to the Free Software Foundation, Inc., 59 Temple Place, ++ Suite 330, Boston, MA 02111-1307 USA ++ ++ */ ++ ++#include ++#include ++#include ++#include "udns.h" ++ ++/* Get a single string for NAPTR record, pretty much like a DN label. ++ * String length is in first byte in *cur, so it can't be >255. ++ */ ++static int dns_getstr(dnscc_t **cur, dnscc_t *ep, char *buf) ++{ ++ unsigned l; ++ dnscc_t *cp = *cur; ++ ++ l = *cp++; ++ if (cp + l > ep) ++ return DNS_E_PROTOCOL; ++ if (buf) { ++ memcpy(buf, cp, l); ++ buf[l] = '\0'; ++ } ++ cp += l; ++ ++ *cur = cp; ++ return l + 1; ++} ++ ++int ++dns_parse_naptr(dnscc_t *qdn, dnscc_t *pkt, dnscc_t *cur, dnscc_t *end, ++ void **result) { ++ struct dns_rr_naptr *ret; ++ struct dns_parse p; ++ struct dns_rr rr; ++ int r, l; ++ char *sp; ++ dnsc_t dn[DNS_MAXDN]; ++ ++ assert(dns_get16(cur+2) == DNS_C_IN && dns_get16(cur+0) == DNS_T_NAPTR); ++ ++ /* first, validate the answer and count size of the result */ ++ l = 0; ++ dns_initparse(&p, qdn, pkt, cur, end); ++ while((r = dns_nextrr(&p, &rr)) > 0) { ++ int i; ++ dnscc_t *ep = rr.dnsrr_dend; ++ ++ /* first 4 bytes: order & preference */ ++ cur = rr.dnsrr_dptr + 4; ++ ++ /* flags, services and regexp */ ++ for (i = 0; i < 3; i++) { ++ r = dns_getstr(&cur, ep, NULL); ++ if (r < 0) ++ return r; ++ l += r; ++ } ++ /* replacement */ ++ r = dns_getdn(pkt, &cur, end, dn, sizeof(dn)); ++ if (r <= 0 || cur != rr.dnsrr_dend) ++ return DNS_E_PROTOCOL; ++ l += dns_dntop_size(dn); ++ } ++ if (r < 0) ++ return DNS_E_PROTOCOL; ++ if (!p.dnsp_nrr) ++ return DNS_E_NODATA; ++ ++ /* next, allocate and set up result */ ++ l += dns_stdrr_size(&p); ++ ret = malloc(sizeof(*ret) + sizeof(struct dns_naptr) * p.dnsp_nrr + l); ++ if (!ret) ++ return DNS_E_NOMEM; ++ ret->dnsnaptr_nrr = p.dnsp_nrr; ++ ret->dnsnaptr_naptr = (struct dns_naptr *)(ret+1); ++ ++ /* and 3rd, fill in result, finally */ ++ sp = (char*)(&ret->dnsnaptr_naptr[p.dnsp_nrr]); ++ for (dns_rewind(&p, qdn), r = 0; dns_nextrr(&p, &rr); ++r) { ++ cur = rr.dnsrr_dptr; ++ ret->dnsnaptr_naptr[r].order = dns_get16(cur); cur += 2; ++ ret->dnsnaptr_naptr[r].preference = dns_get16(cur); cur += 2; ++ sp += dns_getstr(&cur, end, (ret->dnsnaptr_naptr[r].flags = sp)); ++ sp += dns_getstr(&cur, end, (ret->dnsnaptr_naptr[r].service = sp)); ++ sp += dns_getstr(&cur, end, (ret->dnsnaptr_naptr[r].regexp = sp)); ++ dns_getdn(pkt, &cur, end, dn, sizeof(dn)); ++ sp += dns_dntop(dn, (ret->dnsnaptr_naptr[r].replacement = sp), DNS_MAXNAME); ++ } ++ dns_stdrr_finish((struct dns_rr_null *)ret, sp, &p); ++ *result = ret; ++ return 0; ++} ++ ++struct dns_query * ++dns_submit_naptr(struct dns_ctx *ctx, const char *name, int flags, ++ dns_query_naptr_fn *cbck, void *data) { ++ return ++ dns_submit_p(ctx, name, DNS_C_IN, DNS_T_NAPTR, flags, ++ dns_parse_naptr, (dns_query_fn *)cbck, data); ++} ++ ++struct dns_rr_naptr * ++dns_resolve_naptr(struct dns_ctx *ctx, const char *name, int flags) { ++ return (struct dns_rr_naptr *) ++ dns_resolve_p(ctx, name, DNS_C_IN, DNS_T_NAPTR, flags, dns_parse_naptr); ++} +diff --git a/libudns/udns_rr_ptr.c b/libudns/udns_rr_ptr.c +new file mode 100644 +index 0000000..1f682ae +--- /dev/null ++++ b/libudns/udns_rr_ptr.c +@@ -0,0 +1,109 @@ ++/* udns_rr_ptr.c ++ parse/query PTR records ++ ++ Copyright (C) 2005 Michael Tokarev ++ This file is part of UDNS library, an async DNS stub resolver. ++ ++ This library is free software; you can redistribute it and/or ++ modify it under the terms of the GNU Lesser General Public ++ License as published by the Free Software Foundation; either ++ version 2.1 of the License, or (at your option) any later version. ++ ++ This library is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ Lesser General Public License for more details. ++ ++ You should have received a copy of the GNU Lesser General Public ++ License along with this library, in file named COPYING.LGPL; if not, ++ write to the Free Software Foundation, Inc., 59 Temple Place, ++ Suite 330, Boston, MA 02111-1307 USA ++ ++ */ ++ ++#include ++#include ++#include "udns.h" ++ ++int ++dns_parse_ptr(dnscc_t *qdn, dnscc_t *pkt, dnscc_t *cur, dnscc_t *end, ++ void **result) { ++ struct dns_rr_ptr *ret; ++ struct dns_parse p; ++ struct dns_rr rr; ++ int r, l, c; ++ char *sp; ++ dnsc_t ptr[DNS_MAXDN]; ++ ++ assert(dns_get16(cur+2) == DNS_C_IN && dns_get16(cur+0) == DNS_T_PTR); ++ ++ /* first, validate the answer and count size of the result */ ++ l = c = 0; ++ dns_initparse(&p, qdn, pkt, cur, end); ++ while((r = dns_nextrr(&p, &rr)) > 0) { ++ cur = rr.dnsrr_dptr; ++ r = dns_getdn(pkt, &cur, end, ptr, sizeof(ptr)); ++ if (r <= 0 || cur != rr.dnsrr_dend) ++ return DNS_E_PROTOCOL; ++ l += dns_dntop_size(ptr); ++ ++c; ++ } ++ if (r < 0) ++ return DNS_E_PROTOCOL; ++ if (!c) ++ return DNS_E_NODATA; ++ ++ /* next, allocate and set up result */ ++ ret = malloc(sizeof(*ret) + sizeof(char **) * c + l + dns_stdrr_size(&p)); ++ if (!ret) ++ return DNS_E_NOMEM; ++ ret->dnsptr_nrr = c; ++ ret->dnsptr_ptr = (char **)(ret+1); ++ ++ /* and 3rd, fill in result, finally */ ++ sp = (char*)(ret->dnsptr_ptr + c); ++ c = 0; ++ dns_rewind(&p, qdn); ++ while((r = dns_nextrr(&p, &rr)) > 0) { ++ ret->dnsptr_ptr[c] = sp; ++ cur = rr.dnsrr_dptr; ++ dns_getdn(pkt, &cur, end, ptr, sizeof(ptr)); ++ sp += dns_dntop(ptr, sp, DNS_MAXNAME); ++ ++c; ++ } ++ dns_stdrr_finish((struct dns_rr_null *)ret, sp, &p); ++ *result = ret; ++ return 0; ++} ++ ++struct dns_query * ++dns_submit_a4ptr(struct dns_ctx *ctx, const struct in_addr *addr, ++ dns_query_ptr_fn *cbck, void *data) { ++ dnsc_t dn[DNS_A4RSIZE]; ++ dns_a4todn(addr, 0, dn, sizeof(dn)); ++ return ++ dns_submit_dn(ctx, dn, DNS_C_IN, DNS_T_PTR, DNS_NOSRCH, ++ dns_parse_ptr, (dns_query_fn *)cbck, data); ++} ++ ++struct dns_rr_ptr * ++dns_resolve_a4ptr(struct dns_ctx *ctx, const struct in_addr *addr) { ++ return (struct dns_rr_ptr *) ++ dns_resolve(ctx, dns_submit_a4ptr(ctx, addr, NULL, NULL)); ++} ++ ++struct dns_query * ++dns_submit_a6ptr(struct dns_ctx *ctx, const struct in6_addr *addr, ++ dns_query_ptr_fn *cbck, void *data) { ++ dnsc_t dn[DNS_A6RSIZE]; ++ dns_a6todn(addr, 0, dn, sizeof(dn)); ++ return ++ dns_submit_dn(ctx, dn, DNS_C_IN, DNS_T_PTR, DNS_NOSRCH, ++ dns_parse_ptr, (dns_query_fn *)cbck, data); ++} ++ ++struct dns_rr_ptr * ++dns_resolve_a6ptr(struct dns_ctx *ctx, const struct in6_addr *addr) { ++ return (struct dns_rr_ptr *) ++ dns_resolve(ctx, dns_submit_a6ptr(ctx, addr, NULL, NULL)); ++} +diff --git a/libudns/udns_rr_srv.c b/libudns/udns_rr_srv.c +new file mode 100644 +index 0000000..dfba465 +--- /dev/null ++++ b/libudns/udns_rr_srv.c +@@ -0,0 +1,155 @@ ++/* udns_rr_srv.c ++ parse/query SRV IN (rfc2782) records ++ ++ Copyright (C) 2005 Michael Tokarev ++ This file is part of UDNS library, an async DNS stub resolver. ++ ++ This library is free software; you can redistribute it and/or ++ modify it under the terms of the GNU Lesser General Public ++ License as published by the Free Software Foundation; either ++ version 2.1 of the License, or (at your option) any later version. ++ ++ This library is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ Lesser General Public License for more details. ++ ++ You should have received a copy of the GNU Lesser General Public ++ License along with this library, in file named COPYING.LGPL; if not, ++ write to the Free Software Foundation, Inc., 59 Temple Place, ++ Suite 330, Boston, MA 02111-1307 USA ++ ++ Copyright 2005 Thadeu Lima de Souza Cascardo ++ ++ 2005-09-11: ++ Changed MX parser file into a SRV parser file ++ ++ */ ++ ++#include ++#include ++#include ++#include "udns.h" ++ ++int ++dns_parse_srv(dnscc_t *qdn, dnscc_t *pkt, dnscc_t *cur, dnscc_t *end, ++ void **result) { ++ struct dns_rr_srv *ret; ++ struct dns_parse p; ++ struct dns_rr rr; ++ int r, l; ++ char *sp; ++ dnsc_t srv[DNS_MAXDN]; ++ ++ assert(dns_get16(cur+2) == DNS_C_IN && dns_get16(cur+0) == DNS_T_SRV); ++ ++ /* first, validate the answer and count size of the result */ ++ l = 0; ++ dns_initparse(&p, qdn, pkt, cur, end); ++ while((r = dns_nextrr(&p, &rr)) > 0) { ++ cur = rr.dnsrr_dptr + 6; ++ r = dns_getdn(pkt, &cur, end, srv, sizeof(srv)); ++ if (r <= 0 || cur != rr.dnsrr_dend) ++ return DNS_E_PROTOCOL; ++ l += dns_dntop_size(srv); ++ } ++ if (r < 0) ++ return DNS_E_PROTOCOL; ++ if (!p.dnsp_nrr) ++ return DNS_E_NODATA; ++ ++ /* next, allocate and set up result */ ++ l += dns_stdrr_size(&p); ++ ret = malloc(sizeof(*ret) + sizeof(struct dns_srv) * p.dnsp_nrr + l); ++ if (!ret) ++ return DNS_E_NOMEM; ++ ret->dnssrv_nrr = p.dnsp_nrr; ++ ret->dnssrv_srv = (struct dns_srv *)(ret+1); ++ ++ /* and 3rd, fill in result, finally */ ++ sp = (char*)(ret->dnssrv_srv + p.dnsp_nrr); ++ for (dns_rewind(&p, qdn), r = 0; dns_nextrr(&p, &rr); ++r) { ++ ret->dnssrv_srv[r].name = sp; ++ cur = rr.dnsrr_dptr; ++ ret->dnssrv_srv[r].priority = dns_get16(cur); ++ ret->dnssrv_srv[r].weight = dns_get16(cur+2); ++ ret->dnssrv_srv[r].port = dns_get16(cur+4); ++ cur += 6; ++ dns_getdn(pkt, &cur, end, srv, sizeof(srv)); ++ sp += dns_dntop(srv, sp, DNS_MAXNAME); ++ } ++ dns_stdrr_finish((struct dns_rr_null *)ret, sp, &p); ++ *result = ret; ++ return 0; ++} ++ ++/* Add a single service or proto name prepending an undescore (_), ++ * according to rfc2782 rules. ++ * Return 0 or the label length. ++ * Routing assumes dn holds enouth space for a single DN label. */ ++static int add_sname(dnsc_t *dn, const char *sn) { ++ int l = dns_ptodn(sn, 0, dn + 1, DNS_MAXLABEL-1, NULL); ++ if (l <= 1 || l - 2 != dn[1]) ++ /* Should we really check if sn is exactly one label? Do we care? */ ++ return 0; ++ dn[0] = l - 1; ++ dn[1] = '_'; ++ return l; ++} ++ ++/* Construct a domain name for SRV query from the given name, service and proto. ++ * The code allows any combinations of srv and proto (both are non-NULL, ++ * both NULL, or either one is non-NULL). Whenever it makes any sense or not ++ * is left as an exercise to programmer. ++ * Return negative value on error (malformed query) or addition query flag(s). ++ */ ++static int ++build_srv_dn(dnsc_t *dn, const char *name, const char *srv, const char *proto) ++{ ++ int p = 0, l, isabs; ++ if (srv) { ++ l = add_sname(dn + p, srv); ++ if (!l) ++ return -1; ++ p += l; ++ } ++ if (proto) { ++ l = add_sname(dn + p, proto); ++ if (!l) ++ return -1; ++ p += l; ++ } ++ l = dns_ptodn(name, 0, dn + p, DNS_MAXDN - p, &isabs); ++ if (l < 0) ++ return -1; ++ return isabs ? DNS_NOSRCH : 0; ++} ++ ++struct dns_query * ++dns_submit_srv(struct dns_ctx *ctx, ++ const char *name, const char *srv, const char *proto, ++ int flags, dns_query_srv_fn *cbck, void *data) { ++ dnsc_t dn[DNS_MAXDN]; ++ int r = build_srv_dn(dn, name, srv, proto); ++ if (r < 0) { ++ dns_setstatus (ctx, DNS_E_BADQUERY); ++ return NULL; ++ } ++ return ++ dns_submit_dn(ctx, dn, DNS_C_IN, DNS_T_SRV, flags | r, ++ dns_parse_srv, (dns_query_fn *)cbck, data); ++} ++ ++struct dns_rr_srv * ++dns_resolve_srv(struct dns_ctx *ctx, ++ const char *name, const char *srv, const char *proto, int flags) ++{ ++ dnsc_t dn[DNS_MAXDN]; ++ int r = build_srv_dn(dn, name, srv, proto); ++ if (r < 0) { ++ dns_setstatus(ctx, DNS_E_BADQUERY); ++ return NULL; ++ } ++ return (struct dns_rr_srv *) ++ dns_resolve_dn(ctx, dn, DNS_C_IN, DNS_T_SRV, flags | r, dns_parse_srv); ++} +diff --git a/libudns/udns_rr_txt.c b/libudns/udns_rr_txt.c +new file mode 100644 +index 0000000..97f1dfb +--- /dev/null ++++ b/libudns/udns_rr_txt.c +@@ -0,0 +1,98 @@ ++/* udns_rr_txt.c ++ parse/query TXT records ++ ++ Copyright (C) 2005 Michael Tokarev ++ This file is part of UDNS library, an async DNS stub resolver. ++ ++ This library is free software; you can redistribute it and/or ++ modify it under the terms of the GNU Lesser General Public ++ License as published by the Free Software Foundation; either ++ version 2.1 of the License, or (at your option) any later version. ++ ++ This library is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ Lesser General Public License for more details. ++ ++ You should have received a copy of the GNU Lesser General Public ++ License along with this library, in file named COPYING.LGPL; if not, ++ write to the Free Software Foundation, Inc., 59 Temple Place, ++ Suite 330, Boston, MA 02111-1307 USA ++ ++ */ ++ ++#include ++#include ++#include ++#include "udns.h" ++ ++int ++dns_parse_txt(dnscc_t *qdn, dnscc_t *pkt, dnscc_t *cur, dnscc_t *end, ++ void **result) { ++ struct dns_rr_txt *ret; ++ struct dns_parse p; ++ struct dns_rr rr; ++ int r, l; ++ dnsc_t *sp; ++ dnscc_t *cp, *ep; ++ ++ assert(dns_get16(cur+0) == DNS_T_TXT); ++ ++ /* first, validate the answer and count size of the result */ ++ l = 0; ++ dns_initparse(&p, qdn, pkt, cur, end); ++ while((r = dns_nextrr(&p, &rr)) > 0) { ++ cp = rr.dnsrr_dptr; ep = rr.dnsrr_dend; ++ while(cp < ep) { ++ r = *cp++; ++ if (cp + r > ep) ++ return DNS_E_PROTOCOL; ++ l += r; ++ cp += r; ++ } ++ } ++ if (r < 0) ++ return DNS_E_PROTOCOL; ++ if (!p.dnsp_nrr) ++ return DNS_E_NODATA; ++ ++ /* next, allocate and set up result */ ++ l += (sizeof(struct dns_txt) + 1) * p.dnsp_nrr + dns_stdrr_size(&p); ++ ret = malloc(sizeof(*ret) + l); ++ if (!ret) ++ return DNS_E_NOMEM; ++ ret->dnstxt_nrr = p.dnsp_nrr; ++ ret->dnstxt_txt = (struct dns_txt *)(ret+1); ++ ++ /* and 3rd, fill in result, finally */ ++ sp = (dnsc_t*)(ret->dnstxt_txt + p.dnsp_nrr); ++ for(dns_rewind(&p, qdn), r = 0; dns_nextrr(&p, &rr) > 0; ++r) { ++ ret->dnstxt_txt[r].txt = sp; ++ cp = rr.dnsrr_dptr; ep = rr.dnsrr_dend; ++ while(cp < ep) { ++ l = *cp++; ++ memcpy(sp, cp, l); ++ sp += l; ++ cp += l; ++ } ++ ret->dnstxt_txt[r].len = sp - ret->dnstxt_txt[r].txt; ++ *sp++ = '\0'; ++ } ++ dns_stdrr_finish((struct dns_rr_null *)ret, (char*)sp, &p); ++ *result = ret; ++ return 0; ++} ++ ++struct dns_query * ++dns_submit_txt(struct dns_ctx *ctx, const char *name, int qcls, int flags, ++ dns_query_txt_fn *cbck, void *data) { ++ return ++ dns_submit_p(ctx, name, qcls, DNS_T_TXT, flags, ++ dns_parse_txt, (dns_query_fn *)cbck, data); ++} ++ ++struct dns_rr_txt * ++dns_resolve_txt(struct dns_ctx *ctx, const char *name, int qcls, int flags) { ++ return (struct dns_rr_txt *) ++ dns_resolve_p(ctx, name, qcls, DNS_T_TXT, flags, dns_parse_txt); ++} +diff --git a/src/Makefile.am b/src/Makefile.am +index f890d97..c46c8d5 100644 +--- a/src/Makefile.am ++++ b/src/Makefile.am +@@ -4,6 +4,7 @@ AM_CFLAGS = -g -O2 -Wall -Werror -Wno-deprecated-declarations -fno-strict-aliasi + AM_CFLAGS += $(PTHREAD_CFLAGS) + AM_CFLAGS += -I$(top_srcdir)/libipset/include + AM_CFLAGS += -I$(top_srcdir)/libcork/include ++AM_CFLAGS += -I$(top_srcdir)/libudns + AM_CFLAGS += $(LIBPCRE_CFLAGS) + + SS_COMMON_LIBS = $(top_builddir)/libipset/libipset.la \ +@@ -64,10 +65,10 @@ ss_local_LDADD = $(SS_COMMON_LIBS) + ss_tunnel_LDADD = $(SS_COMMON_LIBS) + ss_server_LDADD = $(SS_COMMON_LIBS) + ss_manager_LDADD = $(SS_COMMON_LIBS) +-ss_local_LDADD += -ludns +-ss_tunnel_LDADD += -ludns +-ss_server_LDADD += -ludns +-ss_manager_LDADD += -ludns ++ss_local_LDADD += $(top_builddir)/libudns/libudns.la ++ss_tunnel_LDADD += $(top_builddir)/libudns/libudns.la ++ss_server_LDADD += $(top_builddir)/libudns/libudns.la ++ss_manager_LDADD += $(top_builddir)/libudns/libudns.la + + ss_local_CFLAGS = $(AM_CFLAGS) -DMODULE_LOCAL + ss_tunnel_CFLAGS = $(AM_CFLAGS) -DMODULE_TUNNEL +@@ -90,7 +91,7 @@ ss_redir_SOURCES = utils.c \ + + ss_redir_CFLAGS = $(AM_CFLAGS) -DMODULE_REDIR + ss_redir_LDADD = $(SS_COMMON_LIBS) +-ss_redir_LDADD += -ludns ++ss_redir_LDADD += $(top_builddir)/libudns/libudns.la + endif + + lib_LTLIBRARIES = libshadowsocks-libev.la +-- +2.10.2.windows.1 +