diff options
author | Steven Young <sdyoung@users.sourceforge.net> | 2000-02-16 17:32:49 +0000 |
---|---|---|
committer | Steven Young <sdyoung@users.sourceforge.net> | 2000-02-16 17:32:49 +0000 |
commit | 37e63909c09359ddd5baf7a237387ee5f7219c2d (patch) | |
tree | 118da48d4b5ed947bc4cb9c004d7e10a976be57a /adns-0.6/src | |
parent | a094587fb0e92c5638808d7bff91ef802e200112 (diff) |
This commit was generated by cvs2svn to compensate for changes in r2,
which included commits to RCS files with non-trunk default branches.
Diffstat (limited to 'adns-0.6/src')
-rw-r--r-- | adns-0.6/src/.cvsignore | 2 | ||||
-rw-r--r-- | adns-0.6/src/Makefile.in | 48 | ||||
-rw-r--r-- | adns-0.6/src/adns.h | 818 | ||||
-rw-r--r-- | adns-0.6/src/adns.make | 25 | ||||
-rw-r--r-- | adns-0.6/src/check.c | 199 | ||||
-rw-r--r-- | adns-0.6/src/config.h.in | 91 | ||||
-rw-r--r-- | adns-0.6/src/dlist.h | 53 | ||||
-rw-r--r-- | adns-0.6/src/event.c | 699 | ||||
-rw-r--r-- | adns-0.6/src/general.c | 360 | ||||
-rw-r--r-- | adns-0.6/src/internal.h | 705 | ||||
-rw-r--r-- | adns-0.6/src/parse.c | 253 | ||||
-rw-r--r-- | adns-0.6/src/poll.c | 131 | ||||
-rw-r--r-- | adns-0.6/src/query.c | 540 | ||||
-rw-r--r-- | adns-0.6/src/reply.c | 357 | ||||
-rw-r--r-- | adns-0.6/src/setup.c | 643 | ||||
-rw-r--r-- | adns-0.6/src/transmit.c | 257 | ||||
-rw-r--r-- | adns-0.6/src/tvarith.h | 41 | ||||
-rw-r--r-- | adns-0.6/src/types.c | 1034 |
18 files changed, 6256 insertions, 0 deletions
diff --git a/adns-0.6/src/.cvsignore b/adns-0.6/src/.cvsignore new file mode 100644 index 0000000..547646a --- /dev/null +++ b/adns-0.6/src/.cvsignore @@ -0,0 +1,2 @@ +Makefile +config.h diff --git a/adns-0.6/src/Makefile.in b/adns-0.6/src/Makefile.in new file mode 100644 index 0000000..7afca95 --- /dev/null +++ b/adns-0.6/src/Makefile.in @@ -0,0 +1,48 @@ +# src/Makefile - library main Makefile +# +# This file is +# Copyright (C) 1997-1999 Ian Jackson <ian@davenant.greenend.org.uk> +# +# It is part of adns, which is +# Copyright (C) 1997-1999 Ian Jackson <ian@davenant.greenend.org.uk> +# Copyright (C) 1999 Tony Finch <dot@dotat.at> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# This program 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +srcdir= @srcdir@ +VPATH= @srcdir@ + +TARGETS= libadns.a +include $(srcdir)/../settings.make +include adns.make + +install: libadns.a + set -xe; for f in $(TARGETS); \ + do $(INSTALL_DATA) $$f $(lib_dir)/$$f; done + $(INSTALL_DATA) $(srcdir)/../src/adns.h $(include_dir)/adns.h + +uninstall: + for f in $(TARGETS); do rm -f $(lib_dir)/$$f; done + rm -f $(include_dir)/adns.h + +ALLOBJS= $(LIBOBJS) + +libadns.a: $(LIBOBJS) + rm -f $@ + $(AR) cqv $@.new $(LIBOBJS) + $(RANLIB) $@.new + mv -f $@.new $@ + +$(LIBOBJS): adns.h internal.h config.h diff --git a/adns-0.6/src/adns.h b/adns-0.6/src/adns.h new file mode 100644 index 0000000..588acba --- /dev/null +++ b/adns-0.6/src/adns.h @@ -0,0 +1,818 @@ +/* + * adns.h + * - adns user-visible API (single-threaded, without any locking) + */ +/* + * + * This file is + * Copyright (C) 1997-1999 Ian Jackson <ian@davenant.greenend.org.uk> + * + * It is part of adns, which is + * Copyright (C) 1997-1999 Ian Jackson <ian@davenant.greenend.org.uk> + * Copyright (C) 1999 Tony Finch <dot@dotat.at> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program 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 General Public License for more details. + * + * + * For the benefit of certain LGPL'd `omnibus' software which provides + * a uniform interface to various things including adns, I make the + * following additional licence. I do this because the GPL would + * otherwise force either the omnibus software to be GPL'd or for the + * adns-using part to be distributed separately. + * + * So, you may also redistribute and/or modify adns.h (but only the + * public header file adns.h and not any other part of adns) under the + * terms of the GNU Library General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * Note that adns itself is GPL'd. Authors of adns-using applications + * with GPL-incompatible licences, and people who distribute adns with + * applications where the whole distribution is not GPL'd, are still + * likely to be in violation of the GPL. Anyone who wants to do this + * should contact Ian Jackson. Please note that to avoid encouraging + * people to infringe the GPL as it applies the body of adns, I think + * that if you take advantage of the special exception to redistribute + * just adns.h under the LGPL, you should retain this paragraph in its + * place in the appropriate copyright statements. + * + * + * You should have received a copy of the GNU General Public License, + * or the GNU Library General Public License, as appropriate, along + * with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * + * $Id: adns.h,v 1.1.1.1 2000-02-16 17:32:46 sdyoung Exp $ + */ + +#ifndef ADNS_H_INCLUDED +#define ADNS_H_INCLUDED +#ifdef __cplusplus +extern "C" { /* I really dislike this - iwj. */ +#endif + +#include <stdio.h> + +#include <sys/socket.h> +#include <netinet/in.h> +#include <sys/types.h> +#include <sys/time.h> +#include <unistd.h> + +/* All struct in_addr anywhere in adns are in NETWORK byte order. */ + +typedef struct adns__state *adns_state; +typedef struct adns__query *adns_query; + +typedef enum { + adns_if_noenv= 0x0001, /* do not look at environment */ + adns_if_noerrprint= 0x0002, /* never print output to stderr (_debug overrides) */ + adns_if_noserverwarn= 0x0004, /* do not warn to stderr about duff nameservers etc */ + adns_if_debug= 0x0008, /* enable all output to stderr plus debug msgs */ + adns_if_logpid= 0x0080, /* include pid in diagnostic output */ + adns_if_noautosys= 0x0010, /* do not make syscalls at every opportunity */ + adns_if_eintr= 0x0020, /* allow _wait and _synchronous to return EINTR */ + adns_if_nosigpipe= 0x0040, /* applic has SIGPIPE set to SIG_IGN, do not protect */ + adns_if_checkc_entex= 0x0100, /* do consistency checks on entry/exit to adns funcs */ + adns_if_checkc_freq= 0x0300 /* do consistency checks very frequently (slow!) */ +} adns_initflags; + +typedef enum { + adns_qf_search= 0x00000001, /* use the searchlist */ + adns_qf_usevc= 0x00000002, /* use a virtual circuit (TCP connection) */ + adns_qf_owner= 0x00000004, /* fill in the owner field in the answer */ + adns_qf_quoteok_query= 0x00000010, /* allow quote-requiring chars in query domain */ + adns_qf_quoteok_cname= 0x00000000, /* allow ... in CNAME we go via - now default */ + adns_qf_quoteok_anshost= 0x00000040, /* allow ... in things supposed to be hostnames */ + adns_qf_quotefail_cname= 0x00000080, /* refuse if quote-req chars in CNAME we go via */ + adns_qf_cname_loose= 0x00000100, /* allow refs to CNAMEs - without, get _s_cname */ + adns_qf_cname_forbid= 0x00000200, /* don't follow CNAMEs, instead give _s_cname */ + adns__qf_internalmask= 0x0ff00000 +} adns_queryflags; + +typedef enum { + adns__rrt_typemask= 0x0ffff, + adns__qtf_deref= 0x10000, /* dereference domains and perhaps produce extra data */ + adns__qtf_mail822= 0x20000, /* make mailboxes be in RFC822 rcpt field format */ + + adns_r_none= 0, + + adns_r_a= 1, + + adns_r_ns_raw= 2, + adns_r_ns= adns_r_ns_raw|adns__qtf_deref, + + adns_r_cname= 5, + + adns_r_soa_raw= 6, + adns_r_soa= adns_r_soa_raw|adns__qtf_mail822, + + adns_r_ptr_raw= 12, + adns_r_ptr= adns_r_ptr_raw|adns__qtf_deref, + + adns_r_hinfo= 13, + + adns_r_mx_raw= 15, + adns_r_mx= adns_r_mx_raw|adns__qtf_deref, + + adns_r_txt= 16, + + adns_r_rp_raw= 17, + adns_r_rp= adns_r_rp_raw|adns__qtf_mail822, + + adns_r_addr= adns_r_a|adns__qtf_deref + +} adns_rrtype; + +/* + * In queries without qf_quoteok_*, all domains must have standard + * legal syntax, or you get adns_s_querydomainvalid (if the query + * domain contains bad characters) or adns_s_answerdomaininvalid (if + * the answer contains bad characters). + * + * In queries _with_ qf_quoteok_*, domains in the query or response + * may contain any characters, quoted according to RFC1035 5.1. On + * input to adns, the char* is a pointer to the interior of a " + * delimited string, except that " may appear in it, and on output, + * the char* is a pointer to a string which would be legal either + * inside or outside " delimiters, and any characters not usually + * legal in domain names will be quoted as \X (if the character is + * 33-126 except \ and ") or \DDD. + * + * If the query goes via a CNAME then the canonical name (ie, the + * thing that the CNAME record refers to) is usually allowed to + * contain any characters, which will be quoted as above. With + * adns_qf_quotefail_cname you get adns_s_answerdomaininvalid when + * this happens. (This is a change from version 0.4 and earlier, in + * which failing the query was the default, and you had to say + * adns_qf_quoteok_cname to avoid this; that flag is now deprecated.) + * + * In version 0.4 and earlier, asking for _raw records containing + * mailboxes without specifying _qf_quoteok_anshost was silly. This + * is no longer the case. In this version only parts of responses + * that are actually supposed to be hostnames will be refused by + * default if quote-requiring characters are found. + */ + +/* + * If you ask for an RR which contains domains which are actually + * encoded mailboxes, and don't ask for the _raw version, then adns + * returns the mailbox formatted suitably for an RFC822 recipient + * header field. The particular format used is that if the mailbox + * requires quoting according to the rules in RFC822 then the + * local-part is quoted in double quotes, which end at the next + * unescaped double quote. (\ is the escape char, and is doubled, and + * is used to escape only \ and ".) Otherwise the local-part is + * presented as-is. In any case this is followed by an @ and the + * domain. The domain will not contain any characters not legal in + * hostnames. adns will protect the application from local parts + * containing control characters - these appear to be legal according + * to RFC822 but are clearly a bad idea. + * + * If you ask for the domain with _raw then _no_ checking is done + * (even on the host part, regardless of adns_qf_quoteok_anshost), and + * you just get the domain name in master file format. + * + * If no mailbox is supplied the returned string will be `.' in either + * caswe. + */ + +typedef enum { + adns_s_ok, + + /* locally induced errors */ + adns_s_nomemory, + adns_s_unknownrrtype, + adns_s_systemfail, + + adns_s_max_localfail= 29, + + /* remotely induced errors, detected locally */ + adns_s_timeout, + adns_s_allservfail, + adns_s_norecurse, + adns_s_invalidresponse, + adns_s_unknownformat, + + adns_s_max_remotefail= 59, + + /* remotely induced errors, reported by remote server to us */ + adns_s_rcodeservfail, + adns_s_rcodeformaterror, + adns_s_rcodenotimplemented, + adns_s_rcoderefused, + adns_s_rcodeunknown, + + adns_s_max_tempfail= 99, + + /* remote configuration errors */ + adns_s_inconsistent, /* PTR gives domain whose A does not exist and match */ + adns_s_prohibitedcname, /* CNAME found where eg A expected (not if _qf_loosecname) */ + adns_s_answerdomaininvalid, + adns_s_answerdomaintoolong, + adns_s_invaliddata, + + adns_s_max_misconfig= 199, + + /* permanent problems with the query */ + adns_s_querydomainwrong, + adns_s_querydomaininvalid, + adns_s_querydomaintoolong, + + adns_s_max_misquery= 299, + + /* permanent errors */ + adns_s_nxdomain, + adns_s_nodata, + + adns_s_max_permfail= 499 + +} adns_status; + +typedef struct { + int len; + union { + struct sockaddr sa; + struct sockaddr_in inet; + } addr; +} adns_rr_addr; + +typedef struct { + char *host; + adns_status astatus; + int naddrs; /* temp fail => -1, perm fail => 0, s_ok => >0 */ + adns_rr_addr *addrs; +} adns_rr_hostaddr; + +typedef struct { + char *(array[2]); +} adns_rr_strpair; + +typedef struct { + int i; + adns_rr_hostaddr ha; +} adns_rr_inthostaddr; + +typedef struct { + /* Used both for mx_raw, in which case i is the preference and str the domain, + * and for txt, in which case each entry has i for the `text' length, + * and str for the data (which will have had an extra nul appended + * so that if it was plain text it is now a null-terminated string). + */ + int i; + char *str; +} adns_rr_intstr; + +typedef struct { + adns_rr_intstr array[2]; +} adns_rr_intstrpair; + +typedef struct { + char *mname, *rname; + unsigned long serial, refresh, retry, expire, minimum; +} adns_rr_soa; + +typedef struct { + adns_status status; + char *cname; /* always NULL if query was for CNAME records */ + char *owner; /* only set if requested in query flags */ + adns_rrtype type; /* guaranteed to be same as in query */ + time_t expires; /* expiry time, defined only if _s_ok, nxdomain or nodata. NOT TTL! */ + int nrrs, rrsz; /* nrrs is 0 if an error occurs */ + union { + void *untyped; + unsigned char *bytes; + char *(*str); /* ns_raw, cname, ptr, ptr_raw */ + adns_rr_intstr *(*manyistr); /* txt (list of strings ends with i=-1, str=0) */ + adns_rr_addr *addr; /* addr */ + struct in_addr *inaddr; /* a */ + adns_rr_hostaddr *hostaddr; /* ns */ + adns_rr_intstrpair *intstrpair; /* hinfo */ + adns_rr_strpair *strpair; /* rp, rp_raw */ + adns_rr_inthostaddr *inthostaddr; /* mx */ + adns_rr_intstr *intstr; /* mx_raw */ + adns_rr_soa *soa; /* soa, soa_raw */ + } rrs; +} adns_answer; + +/* Memory management: + * adns_state and adns_query are actually pointers to malloc'd state; + * On submission questions are copied, including the owner domain; + * Answers are malloc'd as a single piece of memory; pointers in the + * answer struct point into further memory in the answer. + * query_io: + * Must always be non-null pointer; + * If *query_io is 0 to start with then any query may be returned; + * If *query_io is !0 adns_query then only that query may be returned. + * If the call is successful, *query_io, *answer_r, and *context_r + * will all be set. + * Errors: + * Return values are 0 or an errno value. + * + * For _init, _init_strcfg, _submit and _synchronous, system errors + * (eg, failure to create sockets, malloc failure, etc.) return errno + * values. + * + * For _wait and _check failures are reported in the answer + * structure, and only 0, ESRCH or (for _check) EAGAIN is + * returned: if no (appropriate) requests are done adns_check returns + * EAGAIN; if no (appropriate) requests are outstanding both + * adns_query and adns_wait return ESRCH. + * + * Additionally, _wait can return EINTR if you set adns_if_eintr. + * + * All other errors (nameserver failure, timed out connections, &c) + * are returned in the status field of the answer. After a + * successful _wait or _check, if status is nonzero then nrrs will be + * 0, otherwise it will be >0. type will always be the type + * requested. + */ + +int adns_init(adns_state *newstate_r, adns_initflags flags, + FILE *diagfile /*0=>stderr*/); + +int adns_init_strcfg(adns_state *newstate_r, adns_initflags flags, + FILE *diagfile /*0=>discard*/, const char *configtext); + +/* Configuration: + * adns_init reads /etc/resolv.conf, which is expected to be (broadly + * speaking) in the format expected by libresolv, and then + * /etc/resolv-adns.conf if it exists. adns_init_strcfg is instead + * passed a string which is interpreted as if it were the contents of + * resolv.conf or resolv-adns.conf. In general, configuration which + * is set later overrides any that is set earlier. + * + * Standard directives understood in resolv[-adns].conf: + * + * nameserver <address> + * Must be followed by the IP address of a nameserver. Several + * nameservers may be specified, and they will be tried in the order + * found. There is a compiled in limit, currently 5, on the number + * of nameservers. (libresolv supports only 3 nameservers.) + * + * search <domain> ... + * Specifies the search list for queries which specify + * adns_qf_search. This is a list of domains to append to the query + * domain. The query domain will be tried as-is either before all + * of these or after them, depending on the ndots option setting + * (see below). + * + * domain <domain> + * This is present only for backward compatibility with obsolete + * versions of libresolv. It should not be used, and is interpreted + * by adns as if it were `search' - note that this is subtly + * different to libresolv's interpretation of this directive. + * + * sortlist <addr>/<mask> ... + * Should be followed by a sequence of IP-address and netmask pairs, + * separated by spaces. They may be specified as + * eg. 172.30.206.0/24 or 172.30.206.0/255.255.255.0. Currently up + * to 15 pairs may be specified (but note that libresolv only + * supports up to 10). + * + * options + * Should followed by one or more options, separated by spaces. + * Each option consists of an option name, followed by optionally + * a colon and a value. Options are listed below. + * + * Non-standard directives understood in resolv[-adns].conf: + * + * clearnameservers + * Clears the list of nameservers, so that further nameserver lines + * start again from the beginning. + * + * include <filename> + * The specified file will be read. + * + * Additionally, adns will ignore lines in resolv[-adns].conf which + * start with a #. + * + * Standard options understood: + * + * debug + * Enables debugging output from the resolver, which will be written + * to stderr. + * + * ndots:<count> + * Affects whether queries with adns_qf_search will be tried first + * without adding domains from the searchlist, or whether the bare + * query domain will be tried last. Queries which contain at least + * <count> dots will be tried bare first. The default is 1. + * + * Non-standard options understood: + * + * adns_checkc:none + * adns_checkc:entex + * adns_checkc:freq + * Changes the consistency checking frequency; this overrides the + * setting of adns_if_check_entex, adns_if_check_freq, or neither, + * in the flags passed to adns_init. + * + * There are a number of environment variables which can modify the + * behaviour of adns. They take effect only if adns_init is used, and + * the caller of adns_init can disable them using adns_if_noenv. In + * each case there is both a FOO and an ADNS_FOO; the latter is + * interpreted later so that it can override the former. Unless + * otherwise stated, environment variables are interpreted after + * resolv[-adns].conf are read, in the order they are listed here. + * + * RES_CONF, ADNS_RES_CONF + * A filename, whose contets are in the format of resolv.conf. + * + * RES_CONF_TEXT, ADNS_RES_CONF_TEXT + * A string in the format of resolv.conf. + * + * RES_OPTIONS, ADNS_RES_OPTIONS + * These are parsed as if they appeared in the `options' line of a + * resolv.conf. In addition to being parsed at this point in the + * sequence, they are also parsed at the very beginning before + * resolv.conf or any other environment variables are read, so that + * any debug option can affect the processing of the configuration. + * + * LOCALDOMAIN, ADNS_LOCALDOMAIN + * These are interpreted as if their contents appeared in a `search' + * line in resolv.conf. + */ + +int adns_synchronous(adns_state ads, + const char *owner, + adns_rrtype type, + adns_queryflags flags, + adns_answer **answer_r); + +/* NB: if you set adns_if_noautosys then _submit and _check do not + * make any system calls; you must use some of the asynch-io event + * processing functions to actually get things to happen. + */ + +int adns_submit(adns_state ads, + const char *owner, + adns_rrtype type, + adns_queryflags flags, + void *context, + adns_query *query_r); + +/* The owner should be quoted in master file format. */ + +int adns_check(adns_state ads, + adns_query *query_io, + adns_answer **answer_r, + void **context_r); + +int adns_wait(adns_state ads, + adns_query *query_io, + adns_answer **answer_r, + void **context_r); + +/* same as adns_wait but uses poll(2) internally */ +int adns_wait_poll(adns_state ads, + adns_query *query_io, + adns_answer **answer_r, + void **context_r); + +void adns_cancel(adns_query query); + +/* The adns_query you get back from _submit is valid (ie, can be + * legitimately passed into adns functions) until it is returned by + * adns_check or adns_wait, or passed to adns_cancel. After that it + * must not be used. You can rely on it not being reused until the + * first adns_submit or _transact call using the same adns_state after + * it became invalid, so you may compare it for equality with other + * query handles until you next call _query or _transact. + * + * _submit and _synchronous return ENOSYS if they don't understand the + * query type. + */ + +int adns_submit_reverse(adns_state ads, + const struct sockaddr *addr, + adns_rrtype type, + adns_queryflags flags, + void *context, + adns_query *query_r); +/* type must be _r_ptr or _r_ptr_raw. _qf_search is ignored. + * addr->sa_family must be AF_INET or you get ENOSYS. + */ + +void adns_finish(adns_state ads); +/* You may call this even if you have queries outstanding; + * they will be cancelled. + */ + + +void adns_forallqueries_begin(adns_state ads); +adns_query adns_forallqueries_next(adns_state ads, void **context_r); +/* Iterator functions, which you can use to loop over the outstanding + * (submitted but not yet successfuly checked/waited) queries. + * + * You can only have one iteration going at once. You may call _begin + * at any time; after that, an iteration will be in progress. You may + * only call _next when an iteration is in progress - anything else + * may coredump. The iteration remains in progress until _next + * returns 0, indicating that all the queries have been walked over, + * or ANY other adns function is called with the same adns_state (or a + * query in the same adns_state). There is no need to explicitly + * finish an iteration. + * + * context_r may be 0. *context_r may not be set when _next returns 0. + */ + +void adns_checkconsistency(adns_state ads, adns_query qu); +/* Checks the consistency of adns's internal data structures. + * If any error is found, the program will abort(). + * You may pass 0 for qu; if you pass non-null then additional checks + * are done to make sure that qu is a valid query. + */ + +/* + * Example expected/legal calling sequence for submit/check/wait: + * adns_init + * adns_submit 1 + * adns_submit 2 + * adns_submit 3 + * adns_wait 1 + * adns_check 3 -> EAGAIN + * adns_wait 2 + * adns_wait 3 + * .... + * adns_finish + */ + +/* + * Entrypoints for generic asynch io: + * (these entrypoints are not very useful except in combination with * + * some of the other I/O model calls which can tell you which fds to + * be interested in): + * + * Note that any adns call may cause adns to open and close fds, so + * you must call beforeselect or beforepoll again just before + * blocking, or you may not have an up-to-date list of it's fds. + */ + +int adns_processany(adns_state ads); +/* Gives adns flow-of-control for a bit. This will never block, and + * can be used with any threading/asynch-io model. If some error + * occurred which might cause an event loop to spin then the errno + * value is returned. + */ + +int adns_processreadable(adns_state ads, int fd, const struct timeval *now); +int adns_processwriteable(adns_state ads, int fd, const struct timeval *now); +int adns_processexceptional(adns_state ads, int fd, const struct timeval *now); +/* Gives adns flow-of-control so that it can process incoming data + * from, or send outgoing data via, fd. Very like _processany. If it + * returns zero then fd will no longer be readable or writeable + * (unless of course more data has arrived since). adns will _only_ + * use that fd and only in the manner specified, regardless of whether + * adns_if_noautosys was specified. + * + * adns_processexceptional should be called when select(2) reports an + * exceptional condition, or poll(2) reports POLLPRI. + * + * It is fine to call _processreabable or _processwriteable when the + * fd is not ready, or with an fd that doesn't belong to adns; it will + * then just return 0. + * + * If some error occurred which might prevent an event loop to spin + * then the errno value is returned. + */ + +void adns_processtimeouts(adns_state ads, const struct timeval *now); +/* Gives adns flow-of-control so that it can process any timeouts + * which might have happened. Very like _processreadable/writeable. + * + * now may be 0; if it isn't, *now must be the current time, recently + * obtained from gettimeofday. + */ + +void adns_firsttimeout(adns_state ads, + struct timeval **tv_mod, struct timeval *tv_buf, + struct timeval now); +/* Asks adns when it would first like the opportunity to time + * something out. now must be the current time, from gettimeofday. + * + * If tv_mod points to 0 then tv_buf must be non-null, and + * _firsttimeout will fill in *tv_buf with the time until the first + * timeout, and make *tv_mod point to tv_buf. If adns doesn't have + * anything that might need timing out it will leave *tv_mod as 0. + * + * If *tv_mod is not 0 then tv_buf is not used. adns will update + * *tv_mod if it has any earlier timeout, and leave it alone if it + * doesn't. + * + * This call will not actually do any I/O, or change the fds that adns + * is using. It always succeeds and never blocks. + */ + +void adns_globalsystemfailure(adns_state ads); +/* If serious problem(s) happen which globally affect your ability to + * interact properly with adns, or adns's ability to function + * properly, you or adns can call this function. + * + * All currently outstanding queries will be made to fail with + * adns_s_systemfail, and adns will close any stream sockets it has + * open. + * + * This is used by adns, for example, if gettimeofday() fails. + * Without this the program's event loop might start to spin ! + * + * This call will never block. + */ + +/* + * Entrypoints for select-loop based asynch io: + */ + +void adns_beforeselect(adns_state ads, int *maxfd, fd_set *readfds, + fd_set *writefds, fd_set *exceptfds, + struct timeval **tv_mod, struct timeval *tv_buf, + const struct timeval *now); +/* Find out file descriptors adns is interested in, and when it would + * like the opportunity to time something out. If you do not plan to + * block then tv_mod may be 0. Otherwise, tv_mod and tv_buf are as + * for adns_firsttimeout. readfds, writefds, exceptfds and maxfd_io may + * not be 0. + * + * If *now is not 0 then this will never actually do any I/O, or + * change the fds that adns is using or the timeouts it wants. In any + * case it won't block. + */ + +void adns_afterselect(adns_state ads, int maxfd, const fd_set *readfds, + const fd_set *writefds, const fd_set *exceptfds, + const struct timeval *now); +/* Gives adns flow-of-control for a bit; intended for use after + * select. This is just a fancy way of calling adns_processreadable/ + * writeable/timeouts as appropriate, as if select had returned the + * data being passed. Always succeeds. + */ + +/* + * Example calling sequence: + * + * adns_init _noautosys + * loop { + * adns_beforeselect + * select + * adns_afterselect + * ... + * adns_submit / adns_check + * ... + * } + */ + +/* + * Entrypoints for poll-loop based asynch io: + */ + +struct pollfd; +/* In case your system doesn't have it or you forgot to include + * <sys/poll.h>, to stop the following declarations from causing + * problems. If your system doesn't have poll then the following + * entrypoints will not be defined in libadns. Sorry ! + */ + +int adns_beforepoll(adns_state ads, struct pollfd *fds, int *nfds_io, int *timeout_io, + const struct timeval *now); +/* Finds out which fd's adns is interested in, and when it would like + * to be able to time things out. This is in a form suitable for use + * with poll(2). + * + * On entry, usually fds should point to at least *nfds_io structs. + * adns will fill up to that many structs will information for poll, + * and record in *nfds_io how many structs it filled. If it wants to + * listen for more structs then *nfds_io will be set to the number + * required and _beforepoll will return ERANGE. + * + * You may call _beforepoll with fds==0 and *nfds_io 0, in which case + * adns will fill in the number of fds that it might be interested in + * in *nfds_io, and always return either 0 (if it is not interested in + * any fds) or ERANGE (if it is). + * + * NOTE that (unless now is 0) adns may acquire additional fds + * from one call to the next, so you must put adns_beforepoll in a + * loop, rather than assuming that the second call (with the buffer + * size requested by the first) will not return ERANGE. + * + * adns only ever sets POLLIN, POLLOUT and POLLPRI in its pollfd + * structs, and only ever looks at those bits. POLLPRI is required to + * detect TCP Urgent Data (which should not be used by a DNS server) + * so that adns can know that the TCP stream is now useless. + * + * In any case, *timeout_io should be a timeout value as for poll(2), + * which adns will modify downwards as required. If the caller does + * not plan to block then *timeout_io should be 0 on entry, or + * alternatively, timeout_io may be 0. (Alternatively, the caller may + * use _beforeselect with timeout_io==0 to find out about file + * descriptors, and use _firsttimeout is used to find out when adns + * might want to time something out.) + * + * adns_beforepoll will return 0 on success, and will not fail for any + * reason other than the fds buffer being too small (ERANGE). + * + * This call will never actually do any I/O. If you supply the + * current time it will not change the fds that adns is using or the + * timeouts it wants. + * + * In any case this call won't block. + */ + +#define ADNS_POLLFDS_RECOMMENDED 2 +/* If you allocate an fds buf with at least RECOMMENDED entries then + * you are unlikely to need to enlarge it. You are recommended to do + * so if it's convenient. However, you must be prepared for adns to + * require more space than this. + */ + +void adns_afterpoll(adns_state ads, const struct pollfd *fds, int nfds, + const struct timeval *now); +/* Gives adns flow-of-control for a bit; intended for use after + * poll(2). fds and nfds should be the results from poll(). pollfd + * structs mentioning fds not belonging to adns will be ignored. + */ + + +adns_status adns_rr_info(adns_rrtype type, + const char **rrtname_r, const char **fmtname_r, + int *len_r, + const void *datap, char **data_r); +/* + * Get information about a query type, or convert reply data to a + * textual form. type must be specified, and the official name of the + * corresponding RR type will be returned in *rrtname_r, and + * information about the processing style in *fmtname_r. The length + * of the table entry in an answer for that type will be returned in + * in *len_r. Any or all of rrtname_r, fmtname_r and len_r may be 0. + * If fmtname_r is non-null then *fmtname_r may be null on return, + * indicating that no special processing is involved. + * + * data_r be must be non-null iff datap is. In this case *data_r will + * be set to point to a string pointing to a representation of the RR + * data in master file format. (The owner name, timeout, class and + * type will not be present - only the data part of the RR.) The + * memory will have been obtained from malloc() and must be freed by + * the caller. + * + * Usually this routine will succeed. Possible errors include: + * adns_s_nomemory + * adns_s_rrtypeunknown + * adns_s_invaliddata (*datap contained garbage) + * If an error occurs then no memory has been allocated, + * and *rrtname_r, *fmtname_r, *len_r and *data_r are undefined. + * + * There are some adns-invented data formats which are not official + * master file formats. These include: + * + * Mailboxes if __qtf_mail822: these are just included as-is. + * + * Addresses (adns_rr_addr): these may be of pretty much any type. + * The representation is in two parts: first, a word for the address + * family (ie, in AF_XXX, the XXX), and then one or more items for the + * address itself, depending on the format. For an IPv4 address the + * syntax is INET followed by the dotted quad (from inet_ntoa). + * Currently only IPv4 is supported. + * + * Text strings (as in adns_rr_txt) appear inside double quotes, and + * use \" and \\ to represent " and \, and \xHH to represent + * characters not in the range 32-126. + * + * Hostname with addresses (adns_rr_hostaddr): this consists of the + * hostname, as usual, followed by the adns_status value, as an + * abbreviation, and then a descriptive string (encoded as if it were + * a piece of text), for the address lookup, followed by zero or more + * addresses enclosed in ( and ). If the result was a permanent + * failure, then a single ? appears instead of the ( ). If the + * result was a temporary failure then an empty pair of parentheses + * appears (which a space in between). For example, one of the NS + * records for greenend.org.uk comes out like + * ns.chiark.greenend.org.uk ok "OK" ( INET 195.224.76.132 ) + * an MX referring to a nonexistent host might come out like: + * 50 sun2.nsfnet-relay.ac.uk nxdomain "No such domain" ( ) + * and if nameserver information is not available you might get: + * dns2.spong.dyn.ml.org timeout "DNS query timed out" ? + */ + +const char *adns_strerror(adns_status st); +const char *adns_errabbrev(adns_status st); +const char *adns_errtypeabbrev(adns_status st); +/* Like strerror but for adns_status values. adns_errabbrev returns + * the abbreviation of the error - eg, for adns_s_timeout it returns + * "timeout". adns_errtypeabbrev returns the abbreviation of the + * error class: ie, for values up to adns_s_max_XXX it will return the + * string XXX. You MUST NOT call these functions with status values + * not returned by the same adns library. + */ + +#ifdef __cplusplus +} /* end of extern "C" */ +#endif +#endif diff --git a/adns-0.6/src/adns.make b/adns-0.6/src/adns.make new file mode 100644 index 0000000..d468075 --- /dev/null +++ b/adns-0.6/src/adns.make @@ -0,0 +1,25 @@ +# src/adns.make - library definitions, including list of object files +# +# This file is +# Copyright (C) 1997-1999 Ian Jackson <ian@davenant.greenend.org.uk> +# +# It is part of adns, which is +# Copyright (C) 1997-1999 Ian Jackson <ian@davenant.greenend.org.uk> +# Copyright (C) 1999 Tony Finch <dot@dotat.at> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# This program 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +LIBOBJS= types.o event.o query.o reply.o general.o setup.o transmit.o \ + parse.o poll.o check.o diff --git a/adns-0.6/src/check.c b/adns-0.6/src/check.c new file mode 100644 index 0000000..f0f1eec --- /dev/null +++ b/adns-0.6/src/check.c @@ -0,0 +1,199 @@ +/* + * check.c + * - consistency checks + */ +/* + * This file is + * Copyright (C) 1997-1999 Ian Jackson <ian@davenant.greenend.org.uk> + * + * It is part of adns, which is + * Copyright (C) 1997-1999 Ian Jackson <ian@davenant.greenend.org.uk> + * Copyright (C) 1999 Tony Finch <dot@dotat.at> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "internal.h" + +void adns_checkconsistency(adns_state ads, adns_query qu) { + adns__consistency(ads,qu,cc_user); +} + +#define DLIST_CHECK(list, nodevar, part, body) \ + if ((list).head) { \ + assert(! (list).head->part back); \ + for ((nodevar)= (list).head; (nodevar); (nodevar)= (nodevar)->part next) { \ + assert((nodevar)->part next \ + ? (nodevar) == (nodevar)->part next->part back \ + : (nodevar) == (list).tail); \ + body \ + } \ + } + +#define DLIST_ASSERTON(node, nodevar, list, part) \ + do { \ + for ((nodevar)= (list).head; \ + (nodevar) != (node); \ + (nodevar)= (nodevar)->part next) { \ + assert((nodevar)); \ + } \ + } while(0) + +static void checkc_query_alloc(adns_state ads, adns_query qu) { + allocnode *an; + + DLIST_CHECK(qu->allocations, an, , { + }); +} + +static void checkc_query(adns_state ads, adns_query qu) { + adns_query child; + + assert(qu->udpnextserver < ads->nservers); + assert(!(qu->udpsent & (~0UL << ads->nservers))); + assert(qu->search_pos <= ads->nsearchlist); + if (qu->parent) DLIST_ASSERTON(qu, child, qu->parent->children, siblings.); +} + +static void checkc_notcpbuf(adns_state ads) { + assert(!ads->tcpsend.used); + assert(!ads->tcprecv.used); + assert(!ads->tcprecv_skip); +} + +static void checkc_global(adns_state ads) { + int i; + + assert(ads->udpsocket >= 0); + + for (i=0; i<ads->nsortlist; i++) + assert(!(ads->sortlist[i].base.s_addr & ~ads->sortlist[i].mask.s_addr)); + + assert(ads->tcpserver >= 0 && ads->tcpserver < ads->nservers); + + switch (ads->tcpstate) { + case server_connecting: + assert(ads->tcpsocket >= 0); + checkc_notcpbuf(ads); + break; + case server_disconnected: + case server_broken: + assert(ads->tcpsocket == -1); + checkc_notcpbuf(ads); + break; + case server_ok: + assert(ads->tcpsocket >= 0); + assert(ads->tcprecv_skip <= ads->tcprecv.used); + break; + default: + assert(!"ads->tcpstate value"); + } + + assert(ads->searchlist || !ads->nsearchlist); +} + +static void checkc_queue_udpw(adns_state ads) { + adns_query qu; + + DLIST_CHECK(ads->udpw, qu, , { + assert(qu->state==query_tosend); + assert(qu->retries <= UDPMAXRETRIES); + assert(qu->udpsent); + assert(!qu->children.head && !qu->children.tail); + checkc_query(ads,qu); + checkc_query_alloc(ads,qu); + }); +} + +static void checkc_queue_tcpw(adns_state ads) { + adns_query qu; + + DLIST_CHECK(ads->tcpw, qu, , { + assert(qu->state==query_tcpw); + assert(!qu->children.head && !qu->children.tail); + assert(qu->retries <= ads->nservers+1); + checkc_query(ads,qu); + checkc_query_alloc(ads,qu); + }); +} + +static void checkc_queue_childw(adns_state ads) { + adns_query parent, child; + + DLIST_CHECK(ads->childw, parent, , { + assert(parent->state == query_childw); + assert(parent->children.head); + DLIST_CHECK(parent->children, child, siblings., { + assert(child->parent == parent); + assert(child->state != query_done); + }); + checkc_query(ads,parent); + checkc_query_alloc(ads,parent); + }); +} + +static void checkc_queue_output(adns_state ads) { + adns_query qu; + + DLIST_CHECK(ads->output, qu, , { + assert(qu->state == query_done); + assert(!qu->children.head && !qu->children.tail); + assert(!qu->parent); + assert(!qu->allocations.head && !qu->allocations.tail); + checkc_query(ads,qu); + }); +} + +void adns__consistency(adns_state ads, adns_query qu, consistency_checks cc) { + adns_query search; + + switch (cc) { + case cc_user: + break; + case cc_entex: + if (!(ads->iflags & adns_if_checkc_entex)) return; + break; + case cc_freq: + if ((ads->iflags & adns_if_checkc_freq) != adns_if_checkc_freq) return; + break; + default: + abort(); + } + + checkc_global(ads); + checkc_queue_udpw(ads); + checkc_queue_tcpw(ads); + checkc_queue_childw(ads); + checkc_queue_output(ads); + + if (qu) { + switch (qu->state) { + case query_tosend: + DLIST_ASSERTON(qu, search, ads->udpw, ); + break; + case query_tcpw: + DLIST_ASSERTON(qu, search, ads->tcpw, ); + break; + case query_childw: + DLIST_ASSERTON(qu, search, ads->childw, ); + break; + case query_done: + DLIST_ASSERTON(qu, search, ads->output, ); + break; + default: + assert(!"specific query state"); + } + } +} diff --git a/adns-0.6/src/config.h.in b/adns-0.6/src/config.h.in new file mode 100644 index 0000000..93a4ad8 --- /dev/null +++ b/adns-0.6/src/config.h.in @@ -0,0 +1,91 @@ +/* src/config.h.in. Generated automatically from configure.in by autoheader. */ + +/* Define if function attributes a la GCC 2.5 and higher are available. */ +#undef HAVE_GNUC25_ATTRIB + +/* Define if constant functions a la GCC 2.5 and higher are available. */ +#undef HAVE_GNUC25_CONST + +/* Define if nonreturning functions a la GCC 2.5 and higher are available. */ +#undef HAVE_GNUC25_NORETURN + +/* Define if printf-format argument lists a la GCC are available. */ +#undef HAVE_GNUC25_PRINTFFORMAT + +/* Define if we want to include rpc/types.h. Crap BSDs put INADDR_LOOPBACK there. */ +#undef HAVEUSE_RPCTYPES_H + +/* Define if you have the poll function. */ +#undef HAVE_POLL + +/* Define if you have the nsl library (-lnsl). */ +#undef HAVE_LIBNSL + +/* Define if you have the socket library (-lsocket). */ +#undef HAVE_LIBSOCKET + +/* Use the definitions: */ + +#ifdef HAVE_POLL +#include <sys/poll.h> +#else +/* kludge it up */ +struct pollfd { int fd; short events; short revents; }; +#define POLLIN 1 +#define POLLPRI 2 +#define POLLOUT 4 +#endif + +/* GNU C attributes. */ +#ifndef FUNCATTR +#ifdef HAVE_GNUC25_ATTRIB +#define FUNCATTR(x) __attribute__(x) +#else +#define FUNCATTR(x) +#endif +#endif + +/* GNU C printf formats, or null. */ +#ifndef ATTRPRINTF +#ifdef HAVE_GNUC25_PRINTFFORMAT +#define ATTRPRINTF(si,tc) format(printf,si,tc) +#else +#define ATTRPRINTF(si,tc) +#endif +#endif +#ifndef PRINTFFORMAT +#define PRINTFFORMAT(si,tc) FUNCATTR((ATTRPRINTF(si,tc))) +#endif + +/* GNU C nonreturning functions, or null. */ +#ifndef ATTRNORETURN +#ifdef HAVE_GNUC25_NORETURN +#define ATTRNORETURN noreturn +#else +#define ATTRNORETURN +#endif +#endif +#ifndef NONRETURNING +#define NONRETURNING FUNCATTR((ATTRNORETURN)) +#endif + +/* Combination of both the above. */ +#ifndef NONRETURNPRINTFFORMAT +#define NONRETURNPRINTFFORMAT(si,tc) FUNCATTR((ATTRPRINTF(si,tc),ATTRNORETURN)) +#endif + +/* GNU C constant functions, or null. */ +#ifndef ATTRCONST +#ifdef HAVE_GNUC25_CONST +#define ATTRCONST const +#else +#define ATTRCONST +#endif +#endif +#ifndef CONSTANT +#define CONSTANT FUNCATTR((ATTRCONST)) +#endif + +#ifdef HAVEUSE_RPCTYPES_H +#include <rpc/types.h> +#endif diff --git a/adns-0.6/src/dlist.h b/adns-0.6/src/dlist.h new file mode 100644 index 0000000..de0268f --- /dev/null +++ b/adns-0.6/src/dlist.h @@ -0,0 +1,53 @@ +/* + * dlist.h + * - macros for handling doubly linked lists + */ +/* + * This file is + * Copyright (C) 1997-1999 Ian Jackson <ian@davenant.greenend.org.uk> + * + * It is part of adns, which is + * Copyright (C) 1997-1999 Ian Jackson <ian@davenant.greenend.org.uk> + * Copyright (C) 1999 Tony Finch <dot@dotat.at> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef ADNS_DLIST_H_INCLUDED +#define ADNS_DLIST_H_INCLUDED + +#define LIST_INIT(list) ((list).head= (list).tail= 0) +#define LINK_INIT(link) ((link).next= (link).back= 0) + +#define LIST_UNLINK_PART(list,node,part) \ + do { \ + if ((node)->part back) (node)->part back->part next= (node)->part next; \ + else (list).head= (node)->part next; \ + if ((node)->part next) (node)->part next->part back= (node)->part back; \ + else (list).tail= (node)->part back; \ + } while(0) + +#define LIST_LINK_TAIL_PART(list,node,part) \ + do { \ + (node)->part next= 0; \ + (node)->part back= (list).tail; \ + if ((list).tail) (list).tail->part next= (node); else (list).head= (node); \ + (list).tail= (node); \ + } while(0) + +#define LIST_UNLINK(list,node) LIST_UNLINK_PART(list,node,) +#define LIST_LINK_TAIL(list,node) LIST_LINK_TAIL_PART(list,node,) + +#endif diff --git a/adns-0.6/src/event.c b/adns-0.6/src/event.c new file mode 100644 index 0000000..a1641c6 --- /dev/null +++ b/adns-0.6/src/event.c @@ -0,0 +1,699 @@ +/* + * event.c + * - event loop core + * - TCP connection management + * - user-visible check/wait and event-loop-related functions + */ +/* + * This file is + * Copyright (C) 1997-1999 Ian Jackson <ian@davenant.greenend.org.uk> + * + * It is part of adns, which is + * Copyright (C) 1997-1999 Ian Jackson <ian@davenant.greenend.org.uk> + * Copyright (C) 1999 Tony Finch <dot@dotat.at> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include <errno.h> +#include <stdlib.h> +#include <unistd.h> + +#include <sys/types.h> +#include <sys/time.h> +#include <netdb.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> + +#include "internal.h" +#include "tvarith.h" + +/* TCP connection management. */ + +static void tcp_close(adns_state ads) { + int serv; + + serv= ads->tcpserver; + close(ads->tcpsocket); + ads->tcpsocket= -1; + ads->tcprecv.used= ads->tcprecv_skip= ads->tcpsend.used= 0; +} + +void adns__tcp_broken(adns_state ads, const char *what, const char *why) { + int serv; + + assert(ads->tcpstate == server_connecting || ads->tcpstate == server_ok); + serv= ads->tcpserver; + if (what) adns__warn(ads,serv,0,"TCP connection failed: %s: %s",what,why); + + tcp_close(ads); + ads->tcpstate= server_broken; + ads->tcpserver= (serv+1)%ads->nservers; +} + +static void tcp_connected(adns_state ads, struct timeval now) { + adns_query qu, nqu; + + adns__debug(ads,ads->tcpserver,0,"TCP connected"); + ads->tcpstate= server_ok; + for (qu= ads->tcpw.head; qu && ads->tcpstate == server_ok; qu= nqu) { + nqu= qu->next; + assert(qu->state == query_tcpw); + adns__querysend_tcp(qu,now); + } +} + +void adns__tcp_tryconnect(adns_state ads, struct timeval now) { + int r, fd, tries; + struct sockaddr_in addr; + struct protoent *proto; + + for (tries=0; tries<ads->nservers; tries++) { + switch (ads->tcpstate) { + case server_connecting: + case server_ok: + case server_broken: + return; + case server_disconnected: + break; + default: + abort(); + } + + assert(!ads->tcpsend.used); + assert(!ads->tcprecv.used); + assert(!ads->tcprecv_skip); + + proto= getprotobyname("tcp"); + if (!proto) { adns__diag(ads,-1,0,"unable to find protocol no. for TCP !"); return; } + fd= socket(AF_INET,SOCK_STREAM,proto->p_proto); + if (fd<0) { + adns__diag(ads,-1,0,"cannot create TCP socket: %s",strerror(errno)); + return; + } + r= adns__setnonblock(ads,fd); + if (r) { + adns__diag(ads,-1,0,"cannot make TCP socket nonblocking: %s",strerror(r)); + close(fd); + return; + } + memset(&addr,0,sizeof(addr)); + addr.sin_family= AF_INET; + addr.sin_port= htons(DNS_PORT); + addr.sin_addr= ads->servers[ads->tcpserver].addr; + r= connect(fd,(const struct sockaddr*)&addr,sizeof(addr)); + ads->tcpsocket= fd; + ads->tcpstate= server_connecting; + if (r==0) { tcp_connected(ads,now); return; } + if (errno == EWOULDBLOCK || errno == EINPROGRESS) { + ads->tcptimeout= now; + timevaladd(&ads->tcptimeout,TCPCONNMS); + return; + } + adns__tcp_broken(ads,"connect",strerror(errno)); + ads->tcpstate= server_disconnected; + } +} + +/* Timeout handling functions. */ + +void adns__must_gettimeofday(adns_state ads, const struct timeval **now_io, + struct timeval *tv_buf) { + const struct timeval *now; + int r; + + now= *now_io; + if (now) return; + r= gettimeofday(tv_buf,0); if (!r) { *now_io= tv_buf; return; } + adns__diag(ads,-1,0,"gettimeofday failed: %s",strerror(errno)); + adns_globalsystemfailure(ads); + return; +} + +static void inter_maxto(struct timeval **tv_io, struct timeval *tvbuf, + struct timeval maxto) { + struct timeval *rbuf; + + if (!tv_io) return; + rbuf= *tv_io; + if (!rbuf) { + *tvbuf= maxto; *tv_io= tvbuf; + } else { + if (timercmp(rbuf,&maxto,>)) *rbuf= maxto; + } +/*fprintf(stderr,"inter_maxto maxto=%ld.%06ld result=%ld.%06ld\n", + maxto.tv_sec,maxto.tv_usec,(**tv_io).tv_sec,(**tv_io).tv_usec);*/ +} + +static void inter_maxtoabs(struct timeval **tv_io, struct timeval *tvbuf, + struct timeval now, struct timeval maxtime) { + /* tv_io may be 0 */ + ldiv_t dr; + +/*fprintf(stderr,"inter_maxtoabs now=%ld.%06ld maxtime=%ld.%06ld\n", + now.tv_sec,now.tv_usec,maxtime.tv_sec,maxtime.tv_usec);*/ + if (!tv_io) return; + maxtime.tv_sec -= (now.tv_sec+2); + maxtime.tv_usec -= (now.tv_usec-2000000); + dr= ldiv(maxtime.tv_usec,1000000); + maxtime.tv_sec += dr.quot; + maxtime.tv_usec -= dr.quot*1000000; + if (maxtime.tv_sec<0) timerclear(&maxtime); + inter_maxto(tv_io,tvbuf,maxtime); +} + +static void timeouts_queue(adns_state ads, int act, + struct timeval **tv_io, struct timeval *tvbuf, + struct timeval now, struct query_queue *queue) { + adns_query qu, nqu; + + for (qu= queue->head; qu; qu= nqu) { + nqu= qu->next; + if (!timercmp(&now,&qu->timeout,>)) { + inter_maxtoabs(tv_io,tvbuf,now,qu->timeout); + } else { + if (!act) { + tvbuf->tv_sec= 0; + tvbuf->tv_usec= 0; + *tv_io= tvbuf; + return; + } + LIST_UNLINK(*queue,qu); + if (qu->state != query_tosend) { + adns__query_fail(qu,adns_s_timeout); + } else { + adns__query_send(qu,now); + } + nqu= queue->head; + } + } +} + +static void tcp_events(adns_state ads, int act, + struct timeval **tv_io, struct timeval *tvbuf, + struct timeval now) { + adns_query qu, nqu; + + for (;;) { + switch (ads->tcpstate) { + case server_broken: + for (qu= ads->tcpw.head; qu; qu= nqu) { + nqu= qu->next; + assert(qu->state == query_tcpw); + if (qu->retries > ads->nservers) { + LIST_UNLINK(ads->tcpw,qu); + adns__query_fail(qu,adns_s_allservfail); + } + } + ads->tcpstate= server_disconnected; + case server_disconnected: /* fall through */ + if (!ads->tcpw.head) return; + adns__tcp_tryconnect(ads,now); + break; + case server_ok: + if (ads->tcpw.head) return; + if (!ads->tcptimeout.tv_sec) { + assert(!ads->tcptimeout.tv_usec); + ads->tcptimeout= now; + timevaladd(&ads->tcptimeout,TCPIDLEMS); + } + case server_connecting: /* fall through */ + if (!timercmp(&now,&ads->tcptimeout,>)) { + inter_maxtoabs(tv_io,tvbuf,now,ads->tcptimeout); + return; + } { + /* TCP timeout has happened */ + switch (ads->tcpstate) { + case server_connecting: /* failed to connect */ + adns__tcp_broken(ads,"unable to make connection","timed out"); + break; + case server_ok: /* idle timeout */ + tcp_close(ads); + ads->tcpstate= server_disconnected; + return; + default: + abort(); + } + } + break; + default: + abort(); + } + } +} + +void adns__timeouts(adns_state ads, int act, + struct timeval **tv_io, struct timeval *tvbuf, + struct timeval now) { + timeouts_queue(ads,act,tv_io,tvbuf,now, &ads->udpw); + timeouts_queue(ads,act,tv_io,tvbuf,now, &ads->tcpw); + tcp_events(ads,act,tv_io,tvbuf,now); +} + +void adns_firsttimeout(adns_state ads, + struct timeval **tv_io, struct timeval *tvbuf, + struct timeval now) { + adns__consistency(ads,0,cc_entex); + adns__timeouts(ads, 0, tv_io,tvbuf, now); + adns__consistency(ads,0,cc_entex); +} + +void adns_processtimeouts(adns_state ads, const struct timeval *now) { + struct timeval tv_buf; + + adns__consistency(ads,0,cc_entex); + adns__must_gettimeofday(ads,&now,&tv_buf); + if (now) adns__timeouts(ads, 1, 0,0, *now); + adns__consistency(ads,0,cc_entex); +} + +/* fd handling functions. These are the top-level of the real work of + * reception and often transmission. + */ + +int adns__pollfds(adns_state ads, struct pollfd pollfds_buf[MAX_POLLFDS]) { + /* Returns the number of entries filled in. Always zeroes revents. */ + + assert(MAX_POLLFDS==2); + + pollfds_buf[0].fd= ads->udpsocket; + pollfds_buf[0].events= POLLIN; + pollfds_buf[0].revents= 0; + + switch (ads->tcpstate) { + case server_disconnected: + return 1; + case server_connecting: + pollfds_buf[1].events= POLLOUT; + break; + case server_ok: + pollfds_buf[1].events= ads->tcpsend.used ? POLLIN|POLLOUT|POLLPRI : POLLIN|POLLPRI; + break; + default: + abort(); + } + pollfds_buf[1].fd= ads->tcpsocket; + return 2; +} + +int adns_processreadable(adns_state ads, int fd, const struct timeval *now) { + int want, dgramlen, r, udpaddrlen, serv, old_skip; + byte udpbuf[DNS_MAXUDP]; + struct sockaddr_in udpaddr; + + adns__consistency(ads,0,cc_entex); + + switch (ads->tcpstate) { + case server_disconnected: + case server_connecting: + break; + case server_ok: + if (fd != ads->tcpsocket) break; + assert(!ads->tcprecv_skip); + for (;;) { + if (ads->tcprecv.used >= ads->tcprecv_skip+2) { + dgramlen= ((ads->tcprecv.buf[ads->tcprecv_skip]<<8) | + ads->tcprecv.buf[ads->tcprecv_skip+1]); + if (ads->tcprecv.used >= ads->tcprecv_skip+2+dgramlen) { + old_skip= ads->tcprecv_skip; + ads->tcprecv_skip += 2+dgramlen; + adns__procdgram(ads, ads->tcprecv.buf+old_skip+2, + dgramlen, ads->tcpserver, 1,*now); + continue; + } else { + want= 2+dgramlen; + } + } else { + want= 2; + } + ads->tcprecv.used -= ads->tcprecv_skip; + memmove(ads->tcprecv.buf,ads->tcprecv.buf+ads->tcprecv_skip,ads->tcprecv.used); + ads->tcprecv_skip= 0; + if (!adns__vbuf_ensure(&ads->tcprecv,want)) { r= ENOMEM; goto xit; } + assert(ads->tcprecv.used <= ads->tcprecv.avail); + if (ads->tcprecv.used == ads->tcprecv.avail) continue; + r= read(ads->tcpsocket, + ads->tcprecv.buf+ads->tcprecv.used, + ads->tcprecv.avail-ads->tcprecv.used); + if (r>0) { + ads->tcprecv.used+= r; + } else { + if (r) { + if (errno==EAGAIN || errno==EWOULDBLOCK) { r= 0; goto xit; } + if (errno==EINTR) continue; + if (errno_resources(errno)) { r= errno; goto xit; } + } + adns__tcp_broken(ads,"read",r?strerror(errno):"closed"); + r= 0; goto xit; + } + } /* never reached */ + default: + abort(); + } + if (fd == ads->udpsocket) { + for (;;) { + udpaddrlen= sizeof(udpaddr); + r= recvfrom(ads->udpsocket,udpbuf,sizeof(udpbuf),0, + (struct sockaddr*)&udpaddr,&udpaddrlen); + if (r<0) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { r= 0; goto xit; } + if (errno == EINTR) continue; + if (errno_resources(errno)) { r= errno; goto xit; } + adns__warn(ads,-1,0,"datagram receive error: %s",strerror(errno)); + r= 0; goto xit; + } + if (udpaddrlen != sizeof(udpaddr)) { + adns__diag(ads,-1,0,"datagram received with wrong address length %d" + " (expected %d)", udpaddrlen,sizeof(udpaddr)); + continue; + } + if (udpaddr.sin_family != AF_INET) { + adns__diag(ads,-1,0,"datagram received with wrong protocol family" + " %u (expected %u)",udpaddr.sin_family,AF_INET); + continue; + } + if (ntohs(udpaddr.sin_port) != DNS_PORT) { + adns__diag(ads,-1,0,"datagram received from wrong port %u (expected %u)", + ntohs(udpaddr.sin_port),DNS_PORT); + continue; + } + for (serv= 0; + serv < ads->nservers && + ads->servers[serv].addr.s_addr != udpaddr.sin_addr.s_addr; + serv++); + if (serv >= ads->nservers) { + adns__warn(ads,-1,0,"datagram received from unknown nameserver %s", + inet_ntoa(udpaddr.sin_addr)); + continue; + } + adns__procdgram(ads,udpbuf,r,serv,0,*now); + } + } + r= 0; +xit: + adns__consistency(ads,0,cc_entex); + return r; +} + +int adns_processwriteable(adns_state ads, int fd, const struct timeval *now) { + int r; + + adns__consistency(ads,0,cc_entex); + + switch (ads->tcpstate) { + case server_disconnected: + break; + case server_connecting: + if (fd != ads->tcpsocket) break; + assert(ads->tcprecv.used==0); + assert(ads->tcprecv_skip==0); + for (;;) { + if (!adns__vbuf_ensure(&ads->tcprecv,1)) { r= ENOMEM; goto xit; } + r= read(ads->tcpsocket,&ads->tcprecv.buf,1); + if (r==0 || (r<0 && (errno==EAGAIN || errno==EWOULDBLOCK))) { + tcp_connected(ads,*now); + r= 0; goto xit; + } + if (r>0) { + adns__tcp_broken(ads,"connect/read","sent data before first request"); + r= 0; goto xit; + } + if (errno==EINTR) continue; + if (errno_resources(errno)) { r= errno; goto xit; } + adns__tcp_broken(ads,"connect/read",strerror(errno)); + r= 0; goto xit; + } /* not reached */ + case server_ok: + if (!(ads->tcpsend.used && fd == ads->tcpsocket)) break; + for (;;) { + adns__sigpipe_protect(ads); + r= write(ads->tcpsocket,ads->tcpsend.buf,ads->tcpsend.used); + adns__sigpipe_unprotect(ads); + if (r<0) { + if (errno==EINTR) continue; + if (errno==EAGAIN || errno==EWOULDBLOCK) { r= 0; goto xit; } + if (errno_resources(errno)) { r= errno; goto xit; } + adns__tcp_broken(ads,"write",strerror(errno)); + r= 0; goto xit; + } else if (r>0) { + ads->tcpsend.used -= r; + memmove(ads->tcpsend.buf,ads->tcpsend.buf+r,ads->tcpsend.used); + } + } /* not reached */ + default: + abort(); + } + r= 0; +xit: + adns__consistency(ads,0,cc_entex); + return r; +} + +int adns_processexceptional(adns_state ads, int fd, const struct timeval *now) { + adns__consistency(ads,0,cc_entex); + switch (ads->tcpstate) { + case server_disconnected: + break; + case server_connecting: + case server_ok: + if (fd != ads->tcpsocket) break; + adns__tcp_broken(ads,"poll/select","exceptional condition detected"); + break; + default: + abort(); + } + adns__consistency(ads,0,cc_entex); + return 0; +} + +static void fd_event(adns_state ads, int fd, + int revent, int pollflag, + int maxfd, const fd_set *fds, + int (*func)(adns_state, int fd, const struct timeval *now), + struct timeval now, int *r_r) { + int r; + + if (!(revent & pollflag)) return; + if (fds && !(fd<maxfd && FD_ISSET(fd,fds))) return; + r= func(ads,fd,&now); + if (r) { + if (r_r) { + *r_r= r; + } else { + adns__diag(ads,-1,0,"process fd failed after select: %s",strerror(errno)); + adns_globalsystemfailure(ads); + } + } +} + +void adns__fdevents(adns_state ads, + const struct pollfd *pollfds, int npollfds, + int maxfd, const fd_set *readfds, + const fd_set *writefds, const fd_set *exceptfds, + struct timeval now, int *r_r) { + int i, fd, revents; + + for (i=0; i<npollfds; i++) { + fd= pollfds[i].fd; + if (fd >= maxfd) maxfd= fd+1; + revents= pollfds[i].revents; + fd_event(ads,fd, revents,POLLIN, maxfd,readfds, adns_processreadable,now,r_r); + fd_event(ads,fd, revents,POLLOUT, maxfd,writefds, adns_processwriteable,now,r_r); + fd_event(ads,fd, revents,POLLPRI, maxfd,exceptfds, adns_processexceptional,now,r_r); + } +} + +/* Wrappers for select(2). */ + +void adns_beforeselect(adns_state ads, int *maxfd_io, fd_set *readfds_io, + fd_set *writefds_io, fd_set *exceptfds_io, + struct timeval **tv_mod, struct timeval *tv_tobuf, + const struct timeval *now) { + struct timeval tv_nowbuf; + struct pollfd pollfds[MAX_POLLFDS]; + int i, fd, maxfd, npollfds; + + adns__consistency(ads,0,cc_entex); + + if (tv_mod && (!*tv_mod || (*tv_mod)->tv_sec || (*tv_mod)->tv_usec)) { + /* The caller is planning to sleep. */ + adns__must_gettimeofday(ads,&now,&tv_nowbuf); + if (!now) goto xit; + adns__timeouts(ads, 1, tv_mod,tv_tobuf, *now); + } + + npollfds= adns__pollfds(ads,pollfds); + maxfd= *maxfd_io; + for (i=0; i<npollfds; i++) { + fd= pollfds[i].fd; + if (fd >= maxfd) maxfd= fd+1; + if (pollfds[i].events & POLLIN) FD_SET(fd,readfds_io); + if (pollfds[i].events & POLLOUT) FD_SET(fd,writefds_io); + if (pollfds[i].events & POLLPRI) FD_SET(fd,exceptfds_io); + } + *maxfd_io= maxfd; + +xit: + adns__consistency(ads,0,cc_entex); +} + +void adns_afterselect(adns_state ads, int maxfd, const fd_set *readfds, + const fd_set *writefds, const fd_set *exceptfds, + const struct timeval *now) { + struct timeval tv_buf; + struct pollfd pollfds[MAX_POLLFDS]; + int npollfds, i; + + adns__consistency(ads,0,cc_entex); + adns__must_gettimeofday(ads,&now,&tv_buf); + if (!now) goto xit; + adns_processtimeouts(ads,now); + + npollfds= adns__pollfds(ads,pollfds); + for (i=0; i<npollfds; i++) pollfds[i].revents= POLLIN|POLLOUT|POLLPRI; + adns__fdevents(ads, + pollfds,npollfds, + maxfd,readfds,writefds,exceptfds, + *now, 0); +xit: + adns__consistency(ads,0,cc_entex); +} + +/* General helpful functions. */ + +void adns_globalsystemfailure(adns_state ads) { + adns__consistency(ads,0,cc_entex); + + while (ads->udpw.head) adns__query_fail(ads->udpw.head, adns_s_systemfail); + while (ads->tcpw.head) adns__query_fail(ads->tcpw.head, adns_s_systemfail); + + switch (ads->tcpstate) { + case server_connecting: + case server_ok: + adns__tcp_broken(ads,0,0); + break; + case server_disconnected: + break; + default: + abort(); + } + adns__consistency(ads,0,cc_entex); +} + +int adns_processany(adns_state ads) { + int r, i; + struct timeval now; + struct pollfd pollfds[MAX_POLLFDS]; + int npollfds; + + adns__consistency(ads,0,cc_entex); + + r= gettimeofday(&now,0); + if (!r) adns_processtimeouts(ads,&now); + + /* We just use adns__fdevents to loop over the fd's trying them. + * This seems more sensible than calling select, since we're most + * likely just to want to do a read on one or two fds anyway. + */ + npollfds= adns__pollfds(ads,pollfds); + for (i=0; i<npollfds; i++) pollfds[i].revents= pollfds[i].events; + adns__fdevents(ads, + pollfds,npollfds, + 0,0,0,0, + now,&r); + + adns__consistency(ads,0,cc_entex); + return 0; +} + +void adns__autosys(adns_state ads, struct timeval now) { + if (ads->iflags & adns_if_noautosys) return; + adns_processany(ads); +} + +int adns__internal_check(adns_state ads, + adns_query *query_io, + adns_answer **answer, + void **context_r) { + adns_query qu; + + qu= *query_io; + if (!qu) { + if (ads->output.head) { + qu= ads->output.head; + } else if (ads->udpw.head || ads->tcpw.head) { + return EAGAIN; + } else { + return ESRCH; + } + } else { + if (qu->id>=0) return EAGAIN; + } + LIST_UNLINK(ads->output,qu); + *answer= qu->answer; + if (context_r) *context_r= qu->ctx.ext; + *query_io= qu; + free(qu); + return 0; +} + +int adns_wait(adns_state ads, + adns_query *query_io, + adns_answer **answer_r, + void **context_r) { + int r, maxfd, rsel; + fd_set readfds, writefds, exceptfds; + struct timeval tvbuf, *tvp; + + adns__consistency(ads,*query_io,cc_entex); + for (;;) { + r= adns__internal_check(ads,query_io,answer_r,context_r); + if (r != EAGAIN) break; + maxfd= 0; tvp= 0; + FD_ZERO(&readfds); FD_ZERO(&writefds); FD_ZERO(&exceptfds); + adns_beforeselect(ads,&maxfd,&readfds,&writefds,&exceptfds,&tvp,&tvbuf,0); + rsel= select(maxfd,&readfds,&writefds,&exceptfds,tvp); + if (rsel==-1) { + if (errno == EINTR) { + if (ads->iflags & adns_if_eintr) { r= EINTR; break; } + } else { + adns__diag(ads,-1,0,"select failed in wait: %s",strerror(errno)); + adns_globalsystemfailure(ads); + } + } else { + assert(rsel >= 0); + adns_afterselect(ads,maxfd,&readfds,&writefds,&exceptfds,0); + } + } + adns__consistency(ads,0,cc_entex); + return r; +} + +int adns_check(adns_state ads, + adns_query *query_io, + adns_answer **answer_r, + void **context_r) { + struct timeval now; + int r; + + adns__consistency(ads,*query_io,cc_entex); + r= gettimeofday(&now,0); + if (!r) adns__autosys(ads,now); + + r= adns__internal_check(ads,query_io,answer_r,context_r); + adns__consistency(ads,0,cc_entex); + return r; +} diff --git a/adns-0.6/src/general.c b/adns-0.6/src/general.c new file mode 100644 index 0000000..8793762 --- /dev/null +++ b/adns-0.6/src/general.c @@ -0,0 +1,360 @@ +/* + * general.c + * - diagnostic functions + * - vbuf handling + */ +/* + * This file is + * Copyright (C) 1997-1999 Ian Jackson <ian@davenant.greenend.org.uk> + * + * It is part of adns, which is + * Copyright (C) 1997-1999 Ian Jackson <ian@davenant.greenend.org.uk> + * Copyright (C) 1999 Tony Finch <dot@dotat.at> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include <stdlib.h> +#include <unistd.h> + +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> + +#include "internal.h" + +/* Core diagnostic functions */ + +void adns__vdiag(adns_state ads, const char *pfx, adns_initflags prevent, + int serv, adns_query qu, const char *fmt, va_list al) { + const char *bef, *aft; + vbuf vb; + + if (!ads->diagfile || + (!(ads->iflags & adns_if_debug) && (!prevent || (ads->iflags & prevent)))) + return; + + if (ads->iflags & adns_if_logpid) { + fprintf(ads->diagfile,"adns%s [%ld]: ",pfx,(long)getpid()); + } else { + fprintf(ads->diagfile,"adns%s: ",pfx); + } + + vfprintf(ads->diagfile,fmt,al); + + bef= " ("; + aft= "\n"; + + if (qu && qu->query_dgram) { + adns__vbuf_init(&vb); + fprintf(ads->diagfile,"%sQNAME=%s, QTYPE=%s", + bef, + adns__diag_domain(qu->ads,-1,0, &vb, + qu->query_dgram,qu->query_dglen,DNS_HDRSIZE), + qu->typei ? qu->typei->rrtname : "<unknown>"); + if (qu->typei && qu->typei->fmtname) + fprintf(ads->diagfile,"(%s)",qu->typei->fmtname); + bef=", "; aft=")\n"; + adns__vbuf_free(&vb); + } + + if (serv>=0) { + fprintf(ads->diagfile,"%sNS=%s",bef,inet_ntoa(ads->servers[serv].addr)); + bef=", "; aft=")\n"; + } + + fputs(aft,ads->diagfile); +} + +void adns__debug(adns_state ads, int serv, adns_query qu, const char *fmt, ...) { + va_list al; + + va_start(al,fmt); + adns__vdiag(ads," debug",0,serv,qu,fmt,al); + va_end(al); +} + +void adns__warn(adns_state ads, int serv, adns_query qu, const char *fmt, ...) { + va_list al; + + va_start(al,fmt); + adns__vdiag(ads," warning",adns_if_noerrprint|adns_if_noserverwarn,serv,qu,fmt,al); + va_end(al); +} + +void adns__diag(adns_state ads, int serv, adns_query qu, const char *fmt, ...) { + va_list al; + + va_start(al,fmt); + adns__vdiag(ads,"",adns_if_noerrprint,serv,qu,fmt,al); + va_end(al); +} + +/* vbuf functions */ + +void adns__vbuf_init(vbuf *vb) { + vb->used= vb->avail= 0; vb->buf= 0; +} + +int adns__vbuf_ensure(vbuf *vb, int want) { + void *nb; + + if (vb->avail >= want) return 1; + nb= realloc(vb->buf,want); if (!nb) return 0; + vb->buf= nb; + vb->avail= want; + return 1; +} + +void adns__vbuf_appendq(vbuf *vb, const byte *data, int len) { + memcpy(vb->buf+vb->used,data,len); + vb->used+= len; +} + +int adns__vbuf_append(vbuf *vb, const byte *data, int len) { + int newlen; + void *nb; + + newlen= vb->used+len; + if (vb->avail < newlen) { + if (newlen<20) newlen= 20; + newlen <<= 1; + nb= realloc(vb->buf,newlen); + if (!nb) { newlen= vb->used+len; nb= realloc(vb->buf,newlen); } + if (!nb) return 0; + vb->buf= nb; + vb->avail= newlen; + } + adns__vbuf_appendq(vb,data,len); + return 1; +} + +int adns__vbuf_appendstr(vbuf *vb, const char *data) { + int l; + l= strlen(data); + return adns__vbuf_append(vb,data,l); +} + +void adns__vbuf_free(vbuf *vb) { + free(vb->buf); + adns__vbuf_init(vb); +} + +/* Additional diagnostic functions */ + +const char *adns__diag_domain(adns_state ads, int serv, adns_query qu, + vbuf *vb, const byte *dgram, int dglen, int cbyte) { + adns_status st; + + st= adns__parse_domain(ads,serv,qu,vb, pdf_quoteok, dgram,dglen,&cbyte,dglen); + if (st == adns_s_nomemory) { + return "<cannot report domain... out of memory>"; + } + if (st) { + vb->used= 0; + if (!(adns__vbuf_appendstr(vb,"<bad format... ") && + adns__vbuf_appendstr(vb,adns_strerror(st)) && + adns__vbuf_appendstr(vb,">") && + adns__vbuf_append(vb,"",1))) { + return "<cannot report bad format... out of memory>"; + } + } + if (!vb->used) { + adns__vbuf_appendstr(vb,"<truncated ...>"); + adns__vbuf_append(vb,"",1); + } + return vb->buf; +} + +adns_status adns_rr_info(adns_rrtype type, + const char **rrtname_r, const char **fmtname_r, + int *len_r, + const void *datap, char **data_r) { + const typeinfo *typei; + vbuf vb; + adns_status st; + + typei= adns__findtype(type); + if (!typei) return adns_s_unknownrrtype; + + if (rrtname_r) *rrtname_r= typei->rrtname; + if (fmtname_r) *fmtname_r= typei->fmtname; + if (len_r) *len_r= typei->rrsz; + + if (!datap) return adns_s_ok; + + adns__vbuf_init(&vb); + st= typei->convstring(&vb,datap); + if (st) goto x_freevb; + if (!adns__vbuf_append(&vb,"",1)) { st= adns_s_nomemory; goto x_freevb; } + assert(strlen(vb.buf) == vb.used-1); + *data_r= realloc(vb.buf,vb.used); + if (!*data_r) *data_r= vb.buf; + return adns_s_ok; + + x_freevb: + adns__vbuf_free(&vb); + return st; +} + + +#define SINFO(n,s) { adns_s_##n, #n, s } + +static const struct sinfo { + adns_status st; + const char *abbrev; + const char *string; +} sinfos[]= { + SINFO( ok, "OK" ), + + SINFO( nomemory, "Out of memory" ), + SINFO( unknownrrtype, "Query not implemented in DNS library" ), + SINFO( systemfail, "General resolver or system failure" ), + + SINFO( timeout, "DNS query timed out" ), + SINFO( allservfail, "All nameservers failed" ), + SINFO( norecurse, "Recursion denied by nameserver" ), + SINFO( invalidresponse, "Nameserver sent bad response" ), + SINFO( unknownformat, "Nameserver used unknown format" ), + + SINFO( rcodeservfail, "Nameserver reports failure" ), + SINFO( rcodeformaterror, "Query not understood by nameserver" ), + SINFO( rcodenotimplemented, "Query not implemented by nameserver" ), + SINFO( rcoderefused, "Query refused by nameserver" ), + SINFO( rcodeunknown, "Nameserver sent unknown response code" ), + + SINFO( inconsistent, "Inconsistent resource records in DNS" ), + SINFO( prohibitedcname, "DNS alias found where canonical name wanted" ), + SINFO( answerdomaininvalid, "Found syntactically invalid domain name" ), + SINFO( answerdomaintoolong, "Found overly-long domain name" ), + SINFO( invaliddata, "Found invalid DNS data" ), + + SINFO( querydomainwrong, "Domain invalid for particular DNS query type" ), + SINFO( querydomaininvalid, "Domain name is syntactically invalid" ), + SINFO( querydomaintoolong, "Domain name is too long" ), + + SINFO( nxdomain, "No such domain" ), + SINFO( nodata, "No such data" ) +}; + +static int si_compar(const void *key, const void *elem) { + const adns_status *st= key; + const struct sinfo *si= elem; + + return *st < si->st ? -1 : *st > si->st ? 1 : 0; +} + +static const struct sinfo *findsinfo(adns_status st) { + return bsearch(&st,sinfos,sizeof(sinfos)/sizeof(*sinfos),sizeof(*sinfos),si_compar); +} + +const char *adns_strerror(adns_status st) { + const struct sinfo *si; + + si= findsinfo(st); + return si->string; +} + +const char *adns_errabbrev(adns_status st) { + const struct sinfo *si; + + si= findsinfo(st); + return si->abbrev; +} + + +#define STINFO(max) { adns_s_max_##max, #max } + +static const struct stinfo { + adns_status stmax; + const char *abbrev; +} stinfos[]= { + { adns_s_ok, "ok" }, + STINFO( localfail ), + STINFO( remotefail ), + STINFO( tempfail ), + STINFO( misconfig ), + STINFO( misquery ), + STINFO( permfail ) +}; + +static int sti_compar(const void *key, const void *elem) { + const adns_status *st= key; + const struct stinfo *sti= elem; + + adns_status here, min, max; + + here= *st; + min= (sti==stinfos) ? 0 : sti[-1].stmax+1; + max= sti->stmax; + + return here < min ? -1 : here > max ? 1 : 0; +} + +const char *adns_errtypeabbrev(adns_status st) { + const struct stinfo *sti; + + sti= bsearch(&st,stinfos,sizeof(stinfos)/sizeof(*stinfos),sizeof(*stinfos),sti_compar); + return sti->abbrev; +} + + +void adns__isort(void *array, int nobjs, int sz, void *tempbuf, + int (*needswap)(void *context, const void *a, const void *b), + void *context) { + byte *data= array; + int i, place; + + for (i=0; i<nobjs; i++) { + for (place= i; + place>0 && needswap(context, data + (place-1)*sz, data + i*sz); + place--); + if (place != i) { + memcpy(tempbuf, data + i*sz, sz); + memmove(data + (place+1)*sz, data + place*sz, (i-place)*sz); + memcpy(data + place*sz, tempbuf, sz); + } + } +} + +/* SIGPIPE protection. */ + +void adns__sigpipe_protect(adns_state ads) { + sigset_t toblock; + struct sigaction sa; + int r; + + if (ads->iflags & adns_if_nosigpipe) return; + + sigfillset(&toblock); + sigdelset(&toblock,SIGPIPE); + + sa.sa_handler= SIG_IGN; + sigfillset(&sa.sa_mask); + sa.sa_flags= 0; + + r= sigprocmask(SIG_SETMASK,&toblock,&ads->stdsigmask); assert(!r); + r= sigaction(SIGPIPE,&sa,&ads->stdsigpipe); assert(!r); +} + +void adns__sigpipe_unprotect(adns_state ads) { + int r; + + if (ads->iflags & adns_if_nosigpipe) return; + + r= sigaction(SIGPIPE,&ads->stdsigpipe,0); assert(!r); + r= sigprocmask(SIG_SETMASK,&ads->stdsigmask,0); assert(!r); +} diff --git a/adns-0.6/src/internal.h b/adns-0.6/src/internal.h new file mode 100644 index 0000000..9e442fe --- /dev/null +++ b/adns-0.6/src/internal.h @@ -0,0 +1,705 @@ +/* + * internal.h + * - declarations of private objects with external linkage (adns__*) + * - definitons of internal macros + * - comments regarding library data structures + */ +/* + * This file is + * Copyright (C) 1997-1999 Ian Jackson <ian@davenant.greenend.org.uk> + * + * It is part of adns, which is + * Copyright (C) 1997-1999 Ian Jackson <ian@davenant.greenend.org.uk> + * Copyright (C) 1999 Tony Finch <dot@dotat.at> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef ADNS_INTERNAL_H_INCLUDED +#define ADNS_INTERNAL_H_INCLUDED + +#include "config.h" +typedef unsigned char byte; + +#include <stdarg.h> +#include <assert.h> +#include <unistd.h> +#include <signal.h> +#include <errno.h> +#include <string.h> + +#include <sys/time.h> + +#include "adns.h" +#include "dlist.h" + +/* Configuration and constants */ + +#define MAXSERVERS 5 +#define MAXSORTLIST 15 +#define UDPMAXRETRIES 15 +#define UDPRETRYMS 2000 +#define TCPWAITMS 30000 +#define TCPCONNMS 14000 +#define TCPIDLEMS 30000 +#define MAXTTLBELIEVE (7*86400) /* any TTL > 7 days is capped */ + +#define DNS_PORT 53 +#define DNS_MAXUDP 512 +#define DNS_MAXDOMAIN 255 +#define DNS_HDRSIZE 12 +#define DNS_IDOFFSET 0 +#define DNS_CLASS_IN 1 + +#define DNS_INADDR_ARPA "in-addr", "arpa" + +#define MAX_POLLFDS ADNS_POLLFDS_RECOMMENDED + +typedef enum { + cc_user, + cc_entex, + cc_freq +} consistency_checks; + +typedef enum { + rcode_noerror, + rcode_formaterror, + rcode_servfail, + rcode_nxdomain, + rcode_notimp, + rcode_refused +} dns_rcode; + +/* Shared data structures */ + +typedef union { + adns_status status; + char *cp; + adns_rrtype type; + int i; + struct in_addr ia; + unsigned long ul; +} rr_align; + +typedef struct { + int used, avail; + byte *buf; +} vbuf; + +typedef struct { + adns_state ads; + adns_query qu; + int serv; + const byte *dgram; + int dglen, nsstart, nscount, arcount; + struct timeval now; +} parseinfo; + +typedef struct { + adns_rrtype type; + const char *rrtname; + const char *fmtname; + int rrsz; + + void (*makefinal)(adns_query qu, void *data); + /* Change memory management of *data. + * Previously, used alloc_interim, now use alloc_final. + */ + + adns_status (*convstring)(vbuf *vb, const void *data); + /* Converts the RR data to a string representation in vbuf. + * vbuf will be appended to (it must have been initialised), + * and will not be null-terminated by convstring. + */ + + adns_status (*parse)(const parseinfo *pai, int cbyte, int max, void *store_r); + /* Parse one RR, in dgram of length dglen, starting at cbyte and + * extending until at most max. + * + * The RR should be stored at *store_r, of length qu->typei->rrsz. + * + * If there is an overrun which might indicate truncation, it should set + * *rdstart to -1; otherwise it may set it to anything else positive. + * + * nsstart is the offset of the authority section. + */ + + int (*diff_needswap)(adns_state ads, const void *datap_a, const void *datap_b); + /* Returns !0 if RR a should be strictly after RR b in the sort order, + * 0 otherwise. Must not fail. + */ +} typeinfo; + +typedef struct allocnode { + struct allocnode *next, *back; +} allocnode; + +union maxalign { + byte d[1]; + struct in_addr ia; + long l; + void *p; + void (*fp)(void); + union maxalign *up; +} data; + +typedef struct { + void *ext; + void (*callback)(adns_query parent, adns_query child); + union { + adns_rr_addr ptr_parent_addr; + adns_rr_hostaddr *hostaddr; + } info; +} qcontext; + +struct adns__query { + adns_state ads; + enum { query_tosend, query_tcpw, query_childw, query_done } state; + adns_query back, next, parent; + struct { adns_query head, tail; } children; + struct { adns_query back, next; } siblings; + struct { allocnode *head, *tail; } allocations; + int interim_allocd, preserved_allocd; + void *final_allocspace; + + const typeinfo *typei; + byte *query_dgram; + int query_dglen; + + vbuf vb; + /* General-purpose messing-about buffer. + * Wherever a `big' interface is crossed, this may be corrupted/changed + * unless otherwise specified. + */ + + adns_answer *answer; + /* This is allocated when a query is submitted, to avoid being unable + * to relate errors to queries if we run out of memory. During + * query processing status, rrs is 0. cname is set if + * we found a cname (this corresponds to cname_dgram in the query + * structure). type is set from the word go. nrrs and rrs + * are set together, when we find how many rrs there are. + * owner is set during querying unless we're doing searchlist, + * in which case it is set only when we find an answer. + */ + + byte *cname_dgram; + int cname_dglen, cname_begin; + /* If non-0, has been allocated using . */ + + vbuf search_vb; + int search_origlen, search_pos, search_doneabs; + /* Used by the searching algorithm. The query domain in textual form + * is copied into the vbuf, and _origlen set to its length. Then + * we walk the searchlist, if we want to. _pos says where we are + * (next entry to try), and _doneabs says whether we've done the + * absolute query yet (0=not yet, 1=done, -1=must do straight away, + * but not done yet). If flags doesn't have adns_qf_search then + * the vbuf is initialised but empty and everything else is zero. + */ + + int id, flags, retries; + int udpnextserver; + unsigned long udpsent; /* bitmap indexed by server */ + struct timeval timeout; + time_t expires; /* Earliest expiry time of any record we used. */ + + qcontext ctx; + + /* Possible states: + * + * state Queue child id nextudpserver udpsent tcpfailed + * + * tosend NONE null >=0 0 zero zero + * tosend udpw null >=0 any nonzero zero + * tosend NONE null >=0 any nonzero zero + * + * tcpw tcpw null >=0 irrelevant any any + * + * child childw set >=0 irrelevant irrelevant irrelevant + * child NONE null >=0 irrelevant irrelevant irrelevant + * done output null -1 irrelevant irrelevant irrelevant + * + * Queries are only not on a queue when they are actually being processed. + * Queries in state tcpw/tcpw have been sent (or are in the to-send buffer) + * iff the tcp connection is in state server_ok. + * + * +------------------------+ + * START -----> | tosend/NONE | + * +------------------------+ + * / |\ \ + * too big for UDP / UDP timeout \ \ send via UDP + * send via TCP / more retries \ \ + * when conn'd / desired \ \ + * | | | + * v | v + * +-----------+ +-------------+ + * | tcpw/tcpw | ________ | tosend/udpw | + * +-----------+ \ +-------------+ + * | | | UDP timeout | | + * | | | no more | | + * | | | retries | | + * \ | TCP died | desired | | + * \ \ no more | | | + * \ \ servers | TCP / | + * \ \ to try | timeout / | + * got \ \ v |_ | got + * reply \ _| +------------------+ / reply + * \ | done/output FAIL | / + * \ +------------------+ / + * \ / + * _| |_ + * (..... got reply ....) + * / \ + * need child query/ies / \ no child query + * / \ + * |_ _| + * +---------------+ +----------------+ + * | childw/childw | ----------------> | done/output OK | + * +---------------+ children done +----------------+ + */ +}; + +struct query_queue { adns_query head, tail; }; + +struct adns__state { + adns_initflags iflags; + FILE *diagfile; + int configerrno; + struct query_queue udpw, tcpw, childw, output; + adns_query forallnext; + int nextid, udpsocket, tcpsocket; + vbuf tcpsend, tcprecv; + int nservers, nsortlist, nsearchlist, searchndots, tcpserver, tcprecv_skip; + enum adns__tcpstate { + server_disconnected, server_connecting, + server_ok, server_broken + } tcpstate; + struct timeval tcptimeout; + /* This will have tv_sec==0 if it is not valid. + * It will always be valid if tcpstate _connecting. + * When _ok, it will be nonzero if we are idle + * (ie, tcpw queue is empty) and counting down. + */ + struct sigaction stdsigpipe; + sigset_t stdsigmask; + struct pollfd pollfds_buf[MAX_POLLFDS]; + struct server { + struct in_addr addr; + } servers[MAXSERVERS]; + struct sortlist { + struct in_addr base, mask; + } sortlist[MAXSORTLIST]; + char **searchlist; +}; + +/* From setup.c: */ + +int adns__setnonblock(adns_state ads, int fd); /* => errno value */ + +/* From general.c: */ + +void adns__vdiag(adns_state ads, const char *pfx, adns_initflags prevent, + int serv, adns_query qu, const char *fmt, va_list al); + +void adns__debug(adns_state ads, int serv, adns_query qu, + const char *fmt, ...) PRINTFFORMAT(4,5); +void adns__warn(adns_state ads, int serv, adns_query qu, + const char *fmt, ...) PRINTFFORMAT(4,5); +void adns__diag(adns_state ads, int serv, adns_query qu, + const char *fmt, ...) PRINTFFORMAT(4,5); + +int adns__vbuf_ensure(vbuf *vb, int want); +int adns__vbuf_appendstr(vbuf *vb, const char *data); /* does not include nul */ +int adns__vbuf_append(vbuf *vb, const byte *data, int len); +/* 1=>success, 0=>realloc failed */ +void adns__vbuf_appendq(vbuf *vb, const byte *data, int len); +void adns__vbuf_init(vbuf *vb); +void adns__vbuf_free(vbuf *vb); + +const char *adns__diag_domain(adns_state ads, int serv, adns_query qu, + vbuf *vb, const byte *dgram, int dglen, int cbyte); +/* Unpicks a domain in a datagram and returns a string suitable for + * printing it as. Never fails - if an error occurs, it will + * return some kind of string describing the error. + * + * serv may be -1 and qu may be 0. vb must have been initialised, + * and will be left in an arbitrary consistent state. + * + * Returns either vb->buf, or a pointer to a string literal. Do not modify + * vb before using the return value. + */ + +void adns__isort(void *array, int nobjs, int sz, void *tempbuf, + int (*needswap)(void *context, const void *a, const void *b), + void *context); +/* Does an insertion sort of array which must contain nobjs objects + * each sz bytes long. tempbuf must point to a buffer at least + * sz bytes long. needswap should return !0 if a>b (strictly, ie + * wrong order) 0 if a<=b (ie, order is fine). + */ + +void adns__sigpipe_protect(adns_state); +void adns__sigpipe_unprotect(adns_state); +/* If SIGPIPE protection is not disabled, will block all signals except + * SIGPIPE, and set SIGPIPE's disposition to SIG_IGN. (And then restore.) + * Each call to _protect must be followed by a call to _unprotect before + * any significant amount of code gets to run, since the old signal mask + * is stored in the adns structure. + */ + +/* From transmit.c: */ + +adns_status adns__mkquery(adns_state ads, vbuf *vb, int *id_r, + const char *owner, int ol, + const typeinfo *typei, adns_queryflags flags); +/* Assembles a query packet in vb. A new id is allocated and returned. + */ + +adns_status adns__mkquery_frdgram(adns_state ads, vbuf *vb, int *id_r, + const byte *qd_dgram, int qd_dglen, int qd_begin, + adns_rrtype type, adns_queryflags flags); +/* Same as adns__mkquery, but takes the owner domain from an existing datagram. + * That domain must be correct and untruncated. + */ + +void adns__querysend_tcp(adns_query qu, struct timeval now); +/* Query must be in state tcpw/tcpw; it will be sent if possible and + * no further processing can be done on it for now. The connection + * might be broken, but no reconnect will be attempted. + */ + +void adns__query_send(adns_query qu, struct timeval now); +/* Query must be in state tosend/NONE; it will be moved to a new state, + * and no further processing can be done on it for now. + * (Resulting state is one of udp/timew, tcpwait/timew (if server not connected), + * tcpsent/timew, child/childw or done/output.) + * __query_send may decide to use either UDP or TCP depending whether + * _qf_usevc is set (or has become set) and whether the query is too + * large. + */ + +/* From query.c: */ + +adns_status adns__internal_submit(adns_state ads, adns_query *query_r, + const typeinfo *typei, vbuf *qumsg_vb, int id, + adns_queryflags flags, struct timeval now, + const qcontext *ctx); +/* Submits a query (for internal use, called during external submits). + * + * The new query is returned in *query_r, or we return adns_s_nomemory. + * + * The query datagram should already have been assembled in qumsg_vb; + * the memory for it is _taken over_ by this routine whether it + * succeeds or fails (if it succeeds, the vbuf is reused for qu->vb). + * + * *ctx is copied byte-for-byte into the query. + * + * When the child query is done, ctx->callback will be called. The + * child will already have been taken off both the global list of + * queries in ads and the list of children in the parent. The child + * will be freed when the callback returns. The parent will have been + * taken off the global childw queue. + * + * The callback should either call adns__query_done, if it is + * complete, or adns__query_fail, if an error has occurred, in which + * case the other children (if any) will be cancelled. If the parent + * has more unfinished children (or has just submitted more) then the + * callback may choose to wait for them - it must then put the parent + * back on the childw queue. + */ + +void adns__search_next(adns_state ads, adns_query qu, struct timeval now); +/* Walks down the searchlist for a query with adns_qf_search. + * The query should have just had a negative response, or not had + * any queries sent yet, and should not be on any queue. + * The query_dgram if any will be freed and forgotten and a new + * one constructed from the search_* members of the query. + * + * Cannot fail (in case of error, calls adns__query_fail). + */ + +void *adns__alloc_interim(adns_query qu, size_t sz); +void *adns__alloc_preserved(adns_query qu, size_t sz); +/* Allocates some memory, and records which query it came from + * and how much there was. + * + * If an error occurs in the query, all the memory from _interim is + * simply freed. If the query succeeds, one large buffer will be made + * which is big enough for all these allocations, and then + * adns__alloc_final will get memory from this buffer. + * + * _alloc_interim can fail (and return 0). + * The caller must ensure that the query is failed. + * + * The memory from _preserved is is kept and transferred into the + * larger buffer - unless we run out of memory, in which case it too + * is freed. When you use _preserved you have to add code to the + * x_nomem error exit case in adns__makefinal_query to clear out the + * pointers you made to those allocations, because that's when they're + * thrown away; you should also make a note in the declaration of + * those pointer variables, to note that they are _preserved rather + * than _interim. If they're in the answer, note it here: + * answer->cname and answer->owner are _preserved. + */ + +void adns__transfer_interim(adns_query from, adns_query to, void *block, size_t sz); +/* Transfers an interim allocation from one query to another, so that + * the `to' query will have room for the data when we get to makefinal + * and so that the free will happen when the `to' query is freed + * rather than the `from' query. + * + * It is legal to call adns__transfer_interim with a null pointer; this + * has no effect. + * + * _transfer_interim also ensures that the expiry time of the `to' query + * is no later than that of the `from' query, so that child queries' + * TTLs get inherited by their parents. + */ + +void *adns__alloc_mine(adns_query qu, size_t sz); +/* Like _interim, but does not record the length for later + * copying into the answer. This just ensures that the memory + * will be freed when we're done with the query. + */ + +void *adns__alloc_final(adns_query qu, size_t sz); +/* Cannot fail, and cannot return 0. + */ + +void adns__makefinal_block(adns_query qu, void **blpp, size_t sz); +void adns__makefinal_str(adns_query qu, char **strp); + +void adns__reset_preserved(adns_query qu); +/* Resets all of the memory management stuff etc. to take account of + * only the _preserved stuff from _alloc_preserved. Used when we find + * an error somewhere and want to just report the error (with perhaps + * CNAME, owner, etc. info), and also when we're halfway through RRs + * in a datagram and discover that we need to retry the query. + */ + +void adns__query_done(adns_query qu); +void adns__query_fail(adns_query qu, adns_status stat); + +/* From reply.c: */ + +void adns__procdgram(adns_state ads, const byte *dgram, int len, + int serv, int viatcp, struct timeval now); +/* This function is allowed to cause new datagrams to be constructed + * and sent, or even new queries to be started. However, + * query-sending functions are not allowed to call any general event + * loop functions in case they accidentally call this. + * + * Ie, receiving functions may call sending functions. + * Sending functions may NOT call receiving functions. + */ + +/* From types.c: */ + +const typeinfo *adns__findtype(adns_rrtype type); + +/* From parse.c: */ + +typedef struct { + adns_state ads; + adns_query qu; + int serv; + const byte *dgram; + int dglen, max, cbyte, namelen; + int *dmend_r; +} findlabel_state; + +void adns__findlabel_start(findlabel_state *fls, adns_state ads, + int serv, adns_query qu, + const byte *dgram, int dglen, int max, + int dmbegin, int *dmend_rlater); +/* Finds labels in a domain in a datagram. + * + * Call this routine first. + * dmend_rlater may be null. ads (and of course fls) may not be. + * serv may be -1, qu may be null - they are for error reporting. + */ + +adns_status adns__findlabel_next(findlabel_state *fls, int *lablen_r, int *labstart_r); +/* Then, call this one repeatedly. + * + * It will return adns_s_ok if all is well, and tell you the length + * and start of successive labels. labstart_r may be null, but + * lablen_r must not be. + * + * After the last label, it will return with *lablen_r zero. + * Do not then call it again; instead, just throw away the findlabel_state. + * + * *dmend_rlater will have been set to point to the next part of + * the datagram after the label (or after the uncompressed part, + * if compression was used). *namelen_rlater will have been set + * to the length of the domain name (total length of labels plus + * 1 for each intervening dot). + * + * If the datagram appears to be truncated, *lablen_r will be -1. + * *dmend_rlater, *labstart_r and *namelen_r may contain garbage. + * Do not call _next again. + * + * There may also be errors, in which case *dmend_rlater, + * *namelen_rlater, *lablen_r and *labstart_r may contain garbage. + * Do not then call findlabel_next again. + */ + +typedef enum { + pdf_quoteok= 0x001 +} parsedomain_flags; + +adns_status adns__parse_domain(adns_state ads, int serv, adns_query qu, + vbuf *vb, parsedomain_flags flags, + const byte *dgram, int dglen, int *cbyte_io, int max); +/* vb must already have been initialised; it will be reset if necessary. + * If there is truncation, vb->used will be set to 0; otherwise + * (if there is no error) vb will be null-terminated. + * If there is an error vb and *cbyte_io may be left indeterminate. + * + * serv may be -1 and qu may be 0 - they are used for error reporting only. + */ + +adns_status adns__parse_domain_more(findlabel_state *fls, adns_state ads, + adns_query qu, vbuf *vb, parsedomain_flags flags, + const byte *dgram); +/* Like adns__parse_domain, but you pass it a pre-initialised findlabel_state, + * for continuing an existing domain or some such of some kind. Also, unlike + * _parse_domain, the domain data will be appended to vb, rather than replacing + * the existing contents. + */ + +adns_status adns__findrr(adns_query qu, int serv, + const byte *dgram, int dglen, int *cbyte_io, + int *type_r, int *class_r, unsigned long *ttl_r, + int *rdlen_r, int *rdstart_r, + int *ownermatchedquery_r); +/* Finds the extent and some of the contents of an RR in a datagram + * and does some checks. The datagram is *dgram, length dglen, and + * the RR starts at *cbyte_io (which is updated afterwards to point + * to the end of the RR). + * + * The type, class, TTL and RRdata length and start are returned iff + * the corresponding pointer variables are not null. type_r, class_r + * and ttl_r may not be null. The TTL will be capped. + * + * If ownermatchedquery_r != 0 then the owner domain of this + * RR will be compared with that in the query (or, if the query + * has gone to a CNAME lookup, with the canonical name). + * In this case, *ownermatchedquery_r will be set to 0 or 1. + * The query datagram (or CNAME datagram) MUST be valid and not truncated. + * + * If there is truncation then *type_r will be set to -1 and + * *cbyte_io, *class_r, *rdlen_r, *rdstart_r and *eo_matched_r will be + * undefined. + * + * qu must obviously be non-null. + * + * If an error is returned then *type_r will be undefined too. + */ + +adns_status adns__findrr_anychk(adns_query qu, int serv, + const byte *dgram, int dglen, int *cbyte_io, + int *type_r, int *class_r, unsigned long *ttl_r, + int *rdlen_r, int *rdstart_r, + const byte *eo_dgram, int eo_dglen, int eo_cbyte, + int *eo_matched_r); +/* Like adns__findrr_checked, except that the datagram and + * owner to compare with can be specified explicitly. + * + * If the caller thinks they know what the owner of the RR ought to + * be they can pass in details in eo_*: this is another (or perhaps + * the same datagram), and a pointer to where the putative owner + * starts in that datagram. In this case *eo_matched_r will be set + * to 1 if the datagram matched or 0 if it did not. Either + * both eo_dgram and eo_matched_r must both be non-null, or they + * must both be null (in which case eo_dglen and eo_cbyte will be ignored). + * The eo datagram and contained owner domain MUST be valid and + * untruncated. + */ + +void adns__update_expires(adns_query qu, unsigned long ttl, struct timeval now); +/* Updates the `expires' field in the query, so that it doesn't exceed + * now + ttl. + */ + +int vbuf__append_quoted1035(vbuf *vb, const byte *buf, int len); + +/* From event.c: */ + +void adns__tcp_broken(adns_state ads, const char *what, const char *why); +/* what and why may be both 0, or both non-0. */ + +void adns__tcp_tryconnect(adns_state ads, struct timeval now); + +void adns__autosys(adns_state ads, struct timeval now); +/* Make all the system calls we want to if the application wants us to. + * Must not be called from within adns internal processing functions, + * lest we end up in recursive descent ! + */ + +void adns__must_gettimeofday(adns_state ads, const struct timeval **now_io, + struct timeval *tv_buf); + +int adns__pollfds(adns_state ads, struct pollfd pollfds_buf[MAX_POLLFDS]); +void adns__fdevents(adns_state ads, + const struct pollfd *pollfds, int npollfds, + int maxfd, const fd_set *readfds, + const fd_set *writefds, const fd_set *exceptfds, + struct timeval now, int *r_r); +int adns__internal_check(adns_state ads, + adns_query *query_io, + adns_answer **answer, + void **context_r); + +void adns__timeouts(adns_state ads, int act, + struct timeval **tv_io, struct timeval *tvbuf, + struct timeval now); +/* If act is !0, then this will also deal with the TCP connection + * if previous events broke it or require it to be connected. + */ + +/* From check.c: */ + +void adns__consistency(adns_state ads, adns_query qu, consistency_checks cc); + +/* Useful static inline functions: */ + +static inline int ctype_whitespace(int c) { return c==' ' || c=='\n' || c=='\t'; } +static inline int ctype_digit(int c) { return c>='0' && c<='9'; } +static inline int ctype_alpha(int c) { + return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'); +} +static inline int ctype_822special(int c) { return strchr("()<>@,;:\\\".[]",c) != 0; } + +static inline int errno_resources(int e) { return e==ENOMEM || e==ENOBUFS; } + +/* Useful macros */ + +#define MEM_ROUND(sz) \ + (( ((sz)+sizeof(union maxalign)-1) / sizeof(union maxalign) ) \ + * sizeof(union maxalign) ) + +#define GETIL_B(cb) (((dgram)[(cb)++]) & 0x0ff) +#define GET_B(cb,tv) ((tv)= GETIL_B((cb))) +#define GET_W(cb,tv) ((tv)=0, (tv)|=(GETIL_B((cb))<<8), (tv)|=GETIL_B(cb), (tv)) +#define GET_L(cb,tv) ( (tv)=0, \ + (tv)|=(GETIL_B((cb))<<24), \ + (tv)|=(GETIL_B((cb))<<16), \ + (tv)|=(GETIL_B((cb))<<8), \ + (tv)|=GETIL_B(cb), \ + (tv) ) + +#endif diff --git a/adns-0.6/src/parse.c b/adns-0.6/src/parse.c new file mode 100644 index 0000000..9a4e0c7 --- /dev/null +++ b/adns-0.6/src/parse.c @@ -0,0 +1,253 @@ +/* + * parse.c + * - parsing assistance functions (mainly for domains inside datagrams) + */ +/* + * This file is + * Copyright (C) 1997-1999 Ian Jackson <ian@davenant.greenend.org.uk> + * + * It is part of adns, which is + * Copyright (C) 1997-1999 Ian Jackson <ian@davenant.greenend.org.uk> + * Copyright (C) 1999 Tony Finch <dot@dotat.at> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "internal.h" + +int vbuf__append_quoted1035(vbuf *vb, const byte *buf, int len) { + char qbuf[10]; + int i, ch; + + while (len) { + qbuf[0]= 0; + for (i=0; i<len; i++) { + ch= buf[i]; + if (ch == '.' || ch == '"' || ch == '(' || ch == ')' || + ch == '@' || ch == ';' || ch == '$' || ch == '\\') { + sprintf(qbuf,"\\%c",ch); + break; + } else if (ch <= ' ' || ch >= 127) { + sprintf(qbuf,"\\%03o",ch); + break; + } + } + if (!adns__vbuf_append(vb,buf,i) || !adns__vbuf_append(vb,qbuf,strlen(qbuf))) + return 0; + if (i<len) i++; + buf+= i; + len-= i; + } + return 1; +} + +void adns__findlabel_start(findlabel_state *fls, adns_state ads, + int serv, adns_query qu, + const byte *dgram, int dglen, int max, + int dmbegin, int *dmend_rlater) { + fls->ads= ads; + fls->qu= qu; + fls->serv= serv; + fls->dgram= dgram; + fls->dglen= dglen; + fls->max= max; + fls->cbyte= dmbegin; + fls->namelen= 0; + fls->dmend_r= dmend_rlater; +} + +adns_status adns__findlabel_next(findlabel_state *fls, + int *lablen_r, int *labstart_r) { + int lablen, jumped, jumpto; + const char *dgram; + + jumped= 0; + dgram= fls->dgram; + for (;;) { + if (fls->cbyte >= fls->dglen) goto x_truncated; + if (fls->cbyte >= fls->max) goto x_badresponse; + GET_B(fls->cbyte,lablen); + if (!(lablen & 0x0c0)) break; + if ((lablen & 0x0c0) != 0x0c0) return adns_s_unknownformat; + if (jumped++) { + adns__diag(fls->ads,fls->serv,fls->qu,"compressed datagram contains loop"); + return adns_s_invalidresponse; + } + if (fls->cbyte >= fls->dglen) goto x_truncated; + if (fls->cbyte >= fls->max) goto x_badresponse; + GET_B(fls->cbyte,jumpto); + jumpto |= (lablen&0x3f)<<8; + if (fls->dmend_r) *(fls->dmend_r)= fls->cbyte; + fls->cbyte= jumpto; + fls->dmend_r= 0; fls->max= fls->dglen+1; + } + if (labstart_r) *labstart_r= fls->cbyte; + if (lablen) { + if (fls->namelen) fls->namelen++; + fls->namelen+= lablen; + if (fls->namelen > DNS_MAXDOMAIN) return adns_s_answerdomaintoolong; + fls->cbyte+= lablen; + if (fls->cbyte > fls->dglen) goto x_truncated; + if (fls->cbyte > fls->max) goto x_badresponse; + } else { + if (fls->dmend_r) *(fls->dmend_r)= fls->cbyte; + } + *lablen_r= lablen; +/*if (labstart_r) fprintf(stderr,"label %d >%.*s<\n",lablen,lablen,fls->dgram+*labstart_r);*/ + return adns_s_ok; + + x_truncated: + *lablen_r= -1; + return adns_s_ok; + + x_badresponse: + adns__diag(fls->ads,fls->serv,fls->qu,"label in domain runs beyond end of domain"); + return adns_s_invalidresponse; +} + +adns_status adns__parse_domain(adns_state ads, int serv, adns_query qu, + vbuf *vb, adns_queryflags flags, + const byte *dgram, int dglen, int *cbyte_io, int max) { + findlabel_state fls; + + adns__findlabel_start(&fls,ads, serv,qu, dgram,dglen,max, *cbyte_io,cbyte_io); + vb->used= 0; + return adns__parse_domain_more(&fls,ads,qu, vb,flags,dgram); +} + +adns_status adns__parse_domain_more(findlabel_state *fls, adns_state ads, + adns_query qu, vbuf *vb, parsedomain_flags flags, + const byte *dgram) { + int lablen, labstart, i, ch, first; + adns_status st; + + first= 1; + for (;;) { + st= adns__findlabel_next(fls,&lablen,&labstart); + if (st) return st; + if (lablen<0) { vb->used=0; return adns_s_ok; } + if (!lablen) break; + if (first) { + first= 0; + } else { + if (!adns__vbuf_append(vb,".",1)) return adns_s_nomemory; + } + if (flags & pdf_quoteok) { + if (!vbuf__append_quoted1035(vb,dgram+labstart,lablen)) + return adns_s_nomemory; + } else { + ch= dgram[labstart]; + if (!ctype_alpha(ch) && !ctype_digit(ch)) return adns_s_answerdomaininvalid; + for (i= labstart+1; i<labstart+lablen; i++) { + ch= dgram[i]; + if (ch != '-' && !ctype_alpha(ch) && !ctype_digit(ch)) + return adns_s_answerdomaininvalid; + } + if (!adns__vbuf_append(vb,dgram+labstart,lablen)) + return adns_s_nomemory; + } + } + if (!adns__vbuf_append(vb,"",1)) return adns_s_nomemory; + return adns_s_ok; +} + +adns_status adns__findrr_anychk(adns_query qu, int serv, + const byte *dgram, int dglen, int *cbyte_io, + int *type_r, int *class_r, unsigned long *ttl_r, + int *rdlen_r, int *rdstart_r, + const byte *eo_dgram, int eo_dglen, int eo_cbyte, + int *eo_matched_r) { + findlabel_state fls, eo_fls; + int cbyte; + + int tmp, rdlen, mismatch; + unsigned long ttl; + int lablen, labstart, ch; + int eo_lablen, eo_labstart, eo_ch; + adns_status st; + + cbyte= *cbyte_io; + + adns__findlabel_start(&fls,qu->ads, serv,qu, dgram,dglen,dglen,cbyte,&cbyte); + if (eo_dgram) { + adns__findlabel_start(&eo_fls,qu->ads, -1,0, eo_dgram,eo_dglen,eo_dglen,eo_cbyte,0); + mismatch= 0; + } else { + mismatch= 1; + } + + for (;;) { + st= adns__findlabel_next(&fls,&lablen,&labstart); + if (st) return st; + if (lablen<0) goto x_truncated; + + if (!mismatch) { + st= adns__findlabel_next(&eo_fls,&eo_lablen,&eo_labstart); + assert(!st); assert(eo_lablen>=0); + if (lablen != eo_lablen) mismatch= 1; + while (!mismatch && eo_lablen-- > 0) { + ch= dgram[labstart++]; if (ctype_alpha(ch)) ch &= ~32; + eo_ch= eo_dgram[eo_labstart++]; if (ctype_alpha(eo_ch)) eo_ch &= ~32; + if (ch != eo_ch) mismatch= 1; + } + } + if (!lablen) break; + } + if (eo_matched_r) *eo_matched_r= !mismatch; + + if (cbyte+10>dglen) goto x_truncated; + GET_W(cbyte,tmp); *type_r= tmp; + GET_W(cbyte,tmp); *class_r= tmp; + + GET_L(cbyte,ttl); + if (ttl > MAXTTLBELIEVE) ttl= MAXTTLBELIEVE; + *ttl_r= ttl; + + GET_W(cbyte,rdlen); if (rdlen_r) *rdlen_r= rdlen; + if (rdstart_r) *rdstart_r= cbyte; + cbyte+= rdlen; + if (cbyte>dglen) goto x_truncated; + *cbyte_io= cbyte; + return adns_s_ok; + + x_truncated: + *type_r= -1; + return 0; +} + +adns_status adns__findrr(adns_query qu, int serv, + const byte *dgram, int dglen, int *cbyte_io, + int *type_r, int *class_r, unsigned long *ttl_r, + int *rdlen_r, int *rdstart_r, + int *ownermatchedquery_r) { + if (!ownermatchedquery_r) { + return adns__findrr_anychk(qu,serv, + dgram,dglen,cbyte_io, + type_r,class_r,ttl_r,rdlen_r,rdstart_r, + 0,0,0, 0); + } else if (!qu->cname_dgram) { + return adns__findrr_anychk(qu,serv, + dgram,dglen,cbyte_io, + type_r,class_r,ttl_r,rdlen_r,rdstart_r, + qu->query_dgram,qu->query_dglen,DNS_HDRSIZE, + ownermatchedquery_r); + } else { + return adns__findrr_anychk(qu,serv, + dgram,dglen,cbyte_io, + type_r,class_r,ttl_r,rdlen_r,rdstart_r, + qu->cname_dgram,qu->cname_dglen,qu->cname_begin, + ownermatchedquery_r); + } +} diff --git a/adns-0.6/src/poll.c b/adns-0.6/src/poll.c new file mode 100644 index 0000000..45dc437 --- /dev/null +++ b/adns-0.6/src/poll.c @@ -0,0 +1,131 @@ +/* + * poll.c + * - wrappers for poll(2) + */ +/* + * This file is + * Copyright (C) 1997-1999 Ian Jackson <ian@davenant.greenend.org.uk> + * + * It is part of adns, which is + * Copyright (C) 1997-1999 Ian Jackson <ian@davenant.greenend.org.uk> + * Copyright (C) 1999 Tony Finch <dot@dotat.at> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include <limits.h> +#include <string.h> + +#include "internal.h" + +#ifdef HAVE_POLL + +int adns_beforepoll(adns_state ads, struct pollfd *fds, int *nfds_io, int *timeout_io, + const struct timeval *now) { + struct timeval tv_nowbuf, tv_tobuf, *tv_to; + int space, found, timeout_ms, r; + struct pollfd fds_tmp[MAX_POLLFDS]; + + adns__consistency(ads,0,cc_entex); + + if (timeout_io) { + adns__must_gettimeofday(ads,&now,&tv_nowbuf); + if (!now) { *nfds_io= 0; r= 0; goto xit; } + + timeout_ms= *timeout_io; + if (timeout_ms == -1) { + tv_to= 0; + } else { + tv_tobuf.tv_sec= timeout_ms / 1000; + tv_tobuf.tv_usec= (timeout_ms % 1000)*1000; + tv_to= &tv_tobuf; + } + + adns__timeouts(ads, 0, &tv_to,&tv_tobuf, *now); + + if (tv_to) { + assert(tv_to == &tv_tobuf); + timeout_ms= (tv_tobuf.tv_usec+999)/1000; + assert(tv_tobuf.tv_sec < (INT_MAX-timeout_ms)/1000); + timeout_ms += tv_tobuf.tv_sec*1000; + } else { + timeout_ms= -1; + } + *timeout_io= timeout_ms; + } + + space= *nfds_io; + if (space >= MAX_POLLFDS) { + found= adns__pollfds(ads,fds); + *nfds_io= found; + } else { + found= adns__pollfds(ads,fds_tmp); + *nfds_io= found; + if (space < found) { r= ERANGE; goto xit; } + memcpy(fds,fds_tmp,sizeof(struct pollfd)*found); + } + r= 0; +xit: + adns__consistency(ads,0,cc_entex); + return r; +} + +void adns_afterpoll(adns_state ads, const struct pollfd *fds, int nfds, + const struct timeval *now) { + struct timeval tv_buf; + + adns__consistency(ads,0,cc_entex); + adns__must_gettimeofday(ads,&now,&tv_buf); + if (now) { + adns__timeouts(ads, 1, 0,0, *now); + adns__fdevents(ads, fds,nfds, 0,0,0,0, *now,0); + } + adns__consistency(ads,0,cc_entex); +} + +int adns_wait_poll(adns_state ads, + adns_query *query_io, + adns_answer **answer_r, + void **context_r) { + int r, nfds, to; + struct pollfd fds[MAX_POLLFDS]; + + adns__consistency(ads,0,cc_entex); + + for (;;) { + r= adns__internal_check(ads,query_io,answer_r,context_r); + if (r != EAGAIN) goto xit; + nfds= MAX_POLLFDS; to= -1; + adns_beforepoll(ads,fds,&nfds,&to,0); + r= poll(fds,nfds,to); + if (r == -1) { + if (errno == EINTR) { + if (ads->iflags & adns_if_eintr) { r= EINTR; goto xit; } + } else { + adns__diag(ads,-1,0,"poll failed in wait: %s",strerror(errno)); + adns_globalsystemfailure(ads); + } + } else { + assert(r >= 0); + adns_afterpoll(ads,fds,nfds,0); + } + } + + xit: + adns__consistency(ads,0,cc_entex); + return r; +} + +#endif diff --git a/adns-0.6/src/query.c b/adns-0.6/src/query.c new file mode 100644 index 0000000..37306d1 --- /dev/null +++ b/adns-0.6/src/query.c @@ -0,0 +1,540 @@ +/* + * query.c + * - overall query management (allocation, completion) + * - per-query memory management + * - query submission and cancellation (user-visible and internal) + */ +/* + * This file is + * Copyright (C) 1997-1999 Ian Jackson <ian@davenant.greenend.org.uk> + * + * It is part of adns, which is + * Copyright (C) 1997-1999 Ian Jackson <ian@davenant.greenend.org.uk> + * Copyright (C) 1999 Tony Finch <dot@dotat.at> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "internal.h" + +#include <stdlib.h> +#include <unistd.h> +#include <errno.h> + +#include <sys/time.h> + +#include "internal.h" + +static adns_query query_alloc(adns_state ads, const typeinfo *typei, + adns_queryflags flags, struct timeval now) { + /* Allocate a virgin query and return it. */ + adns_query qu; + + qu= malloc(sizeof(*qu)); if (!qu) return 0; + qu->answer= malloc(sizeof(*qu->answer)); if (!qu->answer) { free(qu); return 0; } + + qu->ads= ads; + qu->state= query_tosend; + qu->back= qu->next= qu->parent= 0; + LIST_INIT(qu->children); + LINK_INIT(qu->siblings); + LIST_INIT(qu->allocations); + qu->interim_allocd= 0; + qu->preserved_allocd= 0; + qu->final_allocspace= 0; + + qu->typei= typei; + qu->query_dgram= 0; + qu->query_dglen= 0; + adns__vbuf_init(&qu->vb); + + qu->cname_dgram= 0; + qu->cname_dglen= qu->cname_begin= 0; + + adns__vbuf_init(&qu->search_vb); + qu->search_origlen= qu->search_pos= qu->search_doneabs= 0; + + qu->id= 0; + qu->flags= flags; + qu->retries= 0; + qu->udpnextserver= 0; + qu->udpsent= 0; + timerclear(&qu->timeout); + qu->expires= now.tv_sec + MAXTTLBELIEVE; + + memset(&qu->ctx,0,sizeof(qu->ctx)); + + qu->answer->status= adns_s_ok; + qu->answer->cname= qu->answer->owner= 0; + qu->answer->type= typei->type; + qu->answer->expires= -1; + qu->answer->nrrs= 0; + qu->answer->rrs.untyped= 0; + qu->answer->rrsz= typei->rrsz; + + return qu; +} + +static void query_submit(adns_state ads, adns_query qu, + const typeinfo *typei, vbuf *qumsg_vb, int id, + adns_queryflags flags, struct timeval now) { + /* Fills in the query message in for a previously-allocated query, + * and submits it. Cannot fail. Takes over the memory for qumsg_vb. + */ + + qu->vb= *qumsg_vb; + adns__vbuf_init(qumsg_vb); + + qu->query_dgram= malloc(qu->vb.used); + if (!qu->query_dgram) { adns__query_fail(qu,adns_s_nomemory); return; } + + qu->id= id; + qu->query_dglen= qu->vb.used; + memcpy(qu->query_dgram,qu->vb.buf,qu->vb.used); + + adns__query_send(qu,now); +} + +adns_status adns__internal_submit(adns_state ads, adns_query *query_r, + const typeinfo *typei, vbuf *qumsg_vb, int id, + adns_queryflags flags, struct timeval now, + const qcontext *ctx) { + adns_query qu; + + qu= query_alloc(ads,typei,flags,now); + if (!qu) { adns__vbuf_free(qumsg_vb); return adns_s_nomemory; } + *query_r= qu; + + memcpy(&qu->ctx,ctx,sizeof(qu->ctx)); + query_submit(ads,qu, typei,qumsg_vb,id,flags,now); + + return adns_s_ok; +} + +static void query_simple(adns_state ads, adns_query qu, + const char *owner, int ol, + const typeinfo *typei, adns_queryflags flags, + struct timeval now) { + vbuf vb; + int id; + adns_status stat; + + vb= qu->vb; + + stat= adns__mkquery(ads,&vb,&id, owner,ol, typei,flags); + if (stat) { adns__query_fail(qu,stat); return; } + + query_submit(ads,qu, typei,&vb,id, flags,now); +} + +void adns__search_next(adns_state ads, adns_query qu, struct timeval now) { + const char *nextentry; + adns_status stat; + + if (qu->search_doneabs<0) { + nextentry= 0; + qu->search_doneabs= 1; + } else { + if (qu->search_pos >= ads->nsearchlist) { + if (qu->search_doneabs) { + stat= adns_s_nxdomain; goto x_fail; + return; + } else { + nextentry= 0; + qu->search_doneabs= 1; + } + } else { + nextentry= ads->searchlist[qu->search_pos++]; + } + } + + qu->search_vb.used= qu->search_origlen; + if (nextentry) { + if (!adns__vbuf_append(&qu->search_vb,".",1) || + !adns__vbuf_appendstr(&qu->search_vb,nextentry)) { + stat= adns_s_nomemory; goto x_fail; + } + } + + free(qu->query_dgram); + qu->query_dgram= 0; qu->query_dglen= 0; + + query_simple(ads,qu, qu->search_vb.buf, qu->search_vb.used, qu->typei, qu->flags, now); + return; + +x_fail: + adns__query_fail(qu,stat); +} + +static int save_owner(adns_query qu, const char *owner, int ol) { + /* Returns 1 if OK, otherwise there was no memory. */ + adns_answer *ans; + + ans= qu->answer; + assert(!ans->owner); + + ans->owner= adns__alloc_preserved(qu,ol+1); if (!ans->owner) return 0; + + memcpy(ans->owner,owner,ol); + ans->owner[ol]= 0; + return 1; +} + +int adns_submit(adns_state ads, + const char *owner, + adns_rrtype type, + adns_queryflags flags, + void *context, + adns_query *query_r) { + int r, ol, ndots; + adns_status stat; + const typeinfo *typei; + struct timeval now; + adns_query qu; + const char *p; + + adns__consistency(ads,0,cc_entex); + + typei= adns__findtype(type); + if (!typei) return ENOSYS; + + r= gettimeofday(&now,0); if (r) goto x_errno; + qu= query_alloc(ads,typei,flags,now); if (!qu) goto x_errno; + + qu->ctx.ext= context; + qu->ctx.callback= 0; + memset(&qu->ctx.info,0,sizeof(qu->ctx.info)); + + *query_r= qu; + + ol= strlen(owner); + if (!ol) { stat= adns_s_querydomaininvalid; goto x_adnsfail; } + if (ol>DNS_MAXDOMAIN+1) { stat= adns_s_querydomaintoolong; goto x_adnsfail; } + + if (ol>=1 && owner[ol-1]=='.' && (ol<2 || owner[ol-2]!='\\')) { + flags &= ~adns_qf_search; + qu->flags= flags; + ol--; + } + + if (flags & adns_qf_search) { + r= adns__vbuf_append(&qu->search_vb,owner,ol); + if (!r) { stat= adns_s_nomemory; goto x_adnsfail; } + + for (ndots=0, p=owner; (p= strchr(p,'.')); p++, ndots++); + qu->search_doneabs= (ndots >= ads->searchndots) ? -1 : 0; + qu->search_origlen= ol; + adns__search_next(ads,qu,now); + } else { + if (flags & adns_qf_owner) { + if (!save_owner(qu,owner,ol)) { stat= adns_s_nomemory; goto x_adnsfail; } + } + query_simple(ads,qu, owner,ol, typei,flags, now); + } + adns__autosys(ads,now); + adns__consistency(ads,qu,cc_entex); + return 0; + + x_adnsfail: + adns__query_fail(qu,stat); + adns__consistency(ads,qu,cc_entex); + return 0; + + x_errno: + r= errno; + assert(r); + adns__consistency(ads,0,cc_entex); + return r; +} + +int adns_submit_reverse(adns_state ads, + const struct sockaddr *addr, + adns_rrtype type, + adns_queryflags flags, + void *context, + adns_query *query_r) { + const unsigned char *iaddr; + char buf[30]; + + if (type != adns_r_ptr && type != adns_r_ptr_raw) return EINVAL; + flags &= ~adns_qf_search; + + if (addr->sa_family != AF_INET) return ENOSYS; + iaddr= (const unsigned char*) &(((const struct sockaddr_in*)addr) -> sin_addr); + + sprintf(buf, "%d.%d.%d.%d.in-addr.arpa", + iaddr[3], iaddr[2], iaddr[1], iaddr[0]); + + return adns_submit(ads,buf,type,flags,context,query_r); +} + +int adns_synchronous(adns_state ads, + const char *owner, + adns_rrtype type, + adns_queryflags flags, + adns_answer **answer_r) { + adns_query qu; + int r; + + r= adns_submit(ads,owner,type,flags,0,&qu); + if (r) return r; + + r= adns_wait(ads,&qu,answer_r,0); + if (r) adns_cancel(qu); + + return r; +} + +static void *alloc_common(adns_query qu, size_t sz) { + allocnode *an; + + if (!sz) return qu; /* Any old pointer will do */ + assert(!qu->final_allocspace); + an= malloc(MEM_ROUND(MEM_ROUND(sizeof(*an)) + sz)); + if (!an) return 0; + LIST_LINK_TAIL(qu->allocations,an); + return (byte*)an + MEM_ROUND(sizeof(*an)); +} + +void *adns__alloc_interim(adns_query qu, size_t sz) { + void *rv; + + sz= MEM_ROUND(sz); + rv= alloc_common(qu,sz); + if (!rv) return 0; + qu->interim_allocd += sz; + return rv; +} + +void *adns__alloc_preserved(adns_query qu, size_t sz) { + void *rv; + + sz= MEM_ROUND(sz); + rv= adns__alloc_interim(qu,sz); + if (!rv) return 0; + qu->preserved_allocd += sz; + return rv; +} + +void *adns__alloc_mine(adns_query qu, size_t sz) { + return alloc_common(qu,MEM_ROUND(sz)); +} + +void adns__transfer_interim(adns_query from, adns_query to, void *block, size_t sz) { + allocnode *an; + + if (!block) return; + an= (void*)((byte*)block - MEM_ROUND(sizeof(*an))); + + assert(!to->final_allocspace); + assert(!from->final_allocspace); + + LIST_UNLINK(from->allocations,an); + LIST_LINK_TAIL(to->allocations,an); + + from->interim_allocd -= sz; + to->interim_allocd += sz; + + if (to->expires > from->expires) to->expires= from->expires; +} + +void *adns__alloc_final(adns_query qu, size_t sz) { + /* When we're in the _final stage, we _subtract_ from interim_alloc'd + * each allocation, and use final_allocspace to point to the next free + * bit. + */ + void *rp; + + sz= MEM_ROUND(sz); + rp= qu->final_allocspace; + assert(rp); + qu->interim_allocd -= sz; + assert(qu->interim_allocd>=0); + qu->final_allocspace= (byte*)rp + sz; + return rp; +} + +static void cancel_children(adns_query qu) { + adns_query cqu, ncqu; + + for (cqu= qu->children.head; cqu; cqu= ncqu) { + ncqu= cqu->siblings.next; + adns_cancel(cqu); + } +} + +void adns__reset_preserved(adns_query qu) { + assert(!qu->final_allocspace); + cancel_children(qu); + qu->answer->nrrs= 0; + qu->answer->rrs.untyped= 0; + qu->interim_allocd= qu->preserved_allocd; +} + +static void free_query_allocs(adns_query qu) { + allocnode *an, *ann; + + cancel_children(qu); + for (an= qu->allocations.head; an; an= ann) { ann= an->next; free(an); } + LIST_INIT(qu->allocations); + adns__vbuf_free(&qu->vb); + adns__vbuf_free(&qu->search_vb); + free(qu->query_dgram); +} + +void adns_cancel(adns_query qu) { + adns_state ads; + + ads= qu->ads; + adns__consistency(ads,qu,cc_entex); + if (qu->parent) LIST_UNLINK_PART(qu->parent->children,qu,siblings.); + switch (qu->state) { + case query_tosend: + LIST_UNLINK(ads->udpw,qu); + break; + case query_tcpw: + LIST_UNLINK(ads->tcpw,qu); + break; + case query_childw: + LIST_UNLINK(ads->childw,qu); + break; + case query_done: + LIST_UNLINK(ads->output,qu); + break; + default: + abort(); + } + free_query_allocs(qu); + free(qu->answer); + free(qu); + adns__consistency(ads,0,cc_entex); +} + +void adns__update_expires(adns_query qu, unsigned long ttl, struct timeval now) { + time_t max; + + assert(ttl <= MAXTTLBELIEVE); + max= now.tv_sec + ttl; + if (qu->expires < max) return; + qu->expires= max; +} + +static void makefinal_query(adns_query qu) { + adns_answer *ans; + int rrn; + + ans= qu->answer; + + if (qu->interim_allocd) { + ans= realloc(qu->answer, MEM_ROUND(MEM_ROUND(sizeof(*ans)) + qu->interim_allocd)); + if (!ans) goto x_nomem; + qu->answer= ans; + } + + qu->final_allocspace= (byte*)ans + MEM_ROUND(sizeof(*ans)); + adns__makefinal_str(qu,&ans->cname); + adns__makefinal_str(qu,&ans->owner); + + if (ans->nrrs) { + adns__makefinal_block(qu, &ans->rrs.untyped, ans->nrrs*ans->rrsz); + + for (rrn=0; rrn<ans->nrrs; rrn++) + qu->typei->makefinal(qu, ans->rrs.bytes + rrn*ans->rrsz); + } + + free_query_allocs(qu); + return; + + x_nomem: + qu->preserved_allocd= 0; + qu->answer->cname= 0; + qu->answer->owner= 0; + adns__reset_preserved(qu); /* (but we just threw away the preserved stuff) */ + + qu->answer->status= adns_s_nomemory; + free_query_allocs(qu); +} + +void adns__query_done(adns_query qu) { + adns_answer *ans; + adns_query parent; + + cancel_children(qu); + + qu->id= -1; + ans= qu->answer; + + if (qu->flags & adns_qf_owner && qu->flags & adns_qf_search && + ans->status != adns_s_nomemory) { + if (!save_owner(qu, qu->search_vb.buf, qu->search_vb.used)) { + adns__query_fail(qu,adns_s_nomemory); + return; + } + } + + if (ans->nrrs && qu->typei->diff_needswap) { + if (!adns__vbuf_ensure(&qu->vb,qu->typei->rrsz)) { + adns__query_fail(qu,adns_s_nomemory); + return; + } + adns__isort(ans->rrs.bytes, ans->nrrs, ans->rrsz, + qu->vb.buf, + (int(*)(void*, const void*, const void*))qu->typei->diff_needswap, + qu->ads); + } + + ans->expires= qu->expires; + parent= qu->parent; + if (parent) { + LIST_UNLINK_PART(parent->children,qu,siblings.); + LIST_UNLINK(qu->ads->childw,parent); + qu->ctx.callback(parent,qu); + free_query_allocs(qu); + free(qu->answer); + free(qu); + } else { + makefinal_query(qu); + LIST_LINK_TAIL(qu->ads->output,qu); + qu->state= query_done; + } +} + +void adns__query_fail(adns_query qu, adns_status stat) { + adns__reset_preserved(qu); + qu->answer->status= stat; + adns__query_done(qu); +} + +void adns__makefinal_str(adns_query qu, char **strp) { + int l; + char *before, *after; + + before= *strp; + if (!before) return; + l= strlen(before)+1; + after= adns__alloc_final(qu,l); + memcpy(after,before,l); + *strp= after; +} + +void adns__makefinal_block(adns_query qu, void **blpp, size_t sz) { + void *before, *after; + + before= *blpp; + if (!before) return; + after= adns__alloc_final(qu,sz); + memcpy(after,before,sz); + *blpp= after; +} diff --git a/adns-0.6/src/reply.c b/adns-0.6/src/reply.c new file mode 100644 index 0000000..246d52d --- /dev/null +++ b/adns-0.6/src/reply.c @@ -0,0 +1,357 @@ +/* + * reply.c + * - main handling and parsing routine for received datagrams + */ +/* + * This file is + * Copyright (C) 1997-1999 Ian Jackson <ian@davenant.greenend.org.uk> + * + * It is part of adns, which is + * Copyright (C) 1997-1999 Ian Jackson <ian@davenant.greenend.org.uk> + * Copyright (C) 1999 Tony Finch <dot@dotat.at> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include <stdlib.h> + +#include "internal.h" + +void adns__procdgram(adns_state ads, const byte *dgram, int dglen, + int serv, int viatcp, struct timeval now) { + int cbyte, rrstart, wantedrrs, rri, foundsoa, foundns, cname_here; + int id, f1, f2, qdcount, ancount, nscount, arcount; + int flg_ra, flg_rd, flg_tc, flg_qr, opcode; + int rrtype, rrclass, rdlength, rdstart; + int anstart, nsstart, arstart; + int ownermatched, l, nrrs; + unsigned long ttl, soattl; + const typeinfo *typei; + adns_query qu, nqu; + dns_rcode rcode; + adns_status st; + vbuf tempvb; + byte *newquery, *rrsdata; + parseinfo pai; + + if (dglen<DNS_HDRSIZE) { + adns__diag(ads,serv,0,"received datagram too short for message header (%d)",dglen); + return; + } + cbyte= 0; + GET_W(cbyte,id); + GET_B(cbyte,f1); + GET_B(cbyte,f2); + GET_W(cbyte,qdcount); + GET_W(cbyte,ancount); + GET_W(cbyte,nscount); + GET_W(cbyte,arcount); + assert(cbyte == DNS_HDRSIZE); + + flg_qr= f1&0x80; + opcode= (f1&0x78)>>3; + flg_tc= f1&0x02; + flg_rd= f1&0x01; + flg_ra= f2&0x80; + rcode= (f2&0x0f); + + cname_here= 0; + + if (!flg_qr) { + adns__diag(ads,serv,0,"server sent us a query, not a response"); + return; + } + if (opcode) { + adns__diag(ads,serv,0,"server sent us unknown opcode %d (wanted 0=QUERY)",opcode); + return; + } + if (!qdcount) { + adns__diag(ads,serv,0,"server sent reply without quoting our question"); + return; + } else if (qdcount>1) { + adns__diag(ads,serv,0,"server claimed to answer %d questions with one message", + qdcount); + return; + } + for (qu= viatcp ? ads->tcpw.head : ads->udpw.head; qu; qu= nqu) { + nqu= qu->next; + if (qu->id != id) continue; + if (dglen < qu->query_dglen) continue; + if (memcmp(qu->query_dgram+DNS_HDRSIZE, + dgram+DNS_HDRSIZE, + qu->query_dglen-DNS_HDRSIZE)) + continue; + if (viatcp) { + assert(qu->state == query_tcpw); + } else { + assert(qu->state == query_tosend); + if (!(qu->udpsent & (1<<serv))) continue; + } + break; + } + if (!qu) { + if (ads->iflags & adns_if_debug) { + adns__vbuf_init(&tempvb); + adns__debug(ads,serv,0,"reply not found, id %02x, query owner %s", + id, adns__diag_domain(ads,serv,0,&tempvb,dgram,dglen,DNS_HDRSIZE)); + adns__vbuf_free(&tempvb); + } + return; + } + anstart= qu->query_dglen; + arstart= -1; + + if (viatcp) LIST_UNLINK(ads->tcpw,qu); + else LIST_UNLINK(ads->udpw,qu); + /* We're definitely going to do something with this query now */ + + switch (rcode) { + case rcode_noerror: + case rcode_nxdomain: + break; + case rcode_formaterror: + adns__warn(ads,serv,qu,"server cannot understand our query (Format Error)"); + adns__query_fail(qu,adns_s_rcodeformaterror); + return; + case rcode_servfail: + adns__query_fail(qu,adns_s_rcodeservfail); + return; + case rcode_notimp: + adns__warn(ads,serv,qu,"server claims not to implement our query"); + adns__query_fail(qu,adns_s_rcodenotimplemented); + return; + case rcode_refused: + adns__warn(ads,serv,qu,"server refused our query"); + adns__query_fail(qu,adns_s_rcoderefused); + return; + default: + adns__warn(ads,serv,qu,"server gave unknown response code %d",rcode); + adns__query_fail(qu,adns_s_rcodeunknown); + return; + } + + /* Now, take a look at the answer section, and see if it is complete. + * If it has any CNAMEs we stuff them in the answer. + */ + wantedrrs= 0; + cbyte= anstart; + for (rri= 0; rri<ancount; rri++) { + rrstart= cbyte; + st= adns__findrr(qu,serv, dgram,dglen,&cbyte, + &rrtype,&rrclass,&ttl, &rdlength,&rdstart, + &ownermatched); + if (st) { adns__query_fail(qu,st); return; } + if (rrtype == -1) goto x_truncated; + + if (rrclass != DNS_CLASS_IN) { + adns__diag(ads,serv,qu,"ignoring answer RR with wrong class %d (expected IN=%d)", + rrclass,DNS_CLASS_IN); + continue; + } + if (!ownermatched) { + if (ads->iflags & adns_if_debug) { + adns__debug(ads,serv,qu,"ignoring RR with an unexpected owner %s", + adns__diag_domain(ads,serv,qu, &qu->vb, dgram,dglen,rrstart)); + } + continue; + } + if (rrtype == adns_r_cname && + (qu->typei->type & adns__rrt_typemask) != adns_r_cname) { + if (qu->flags & adns_qf_cname_forbid) { + adns__query_fail(qu,adns_s_prohibitedcname); + return; + } else if (qu->cname_dgram) { /* Ignore second and subsequent CNAME(s) */ + adns__debug(ads,serv,qu,"allegedly canonical name %s is actually alias for %s", + qu->answer->cname, + adns__diag_domain(ads,serv,qu, &qu->vb, dgram,dglen,rdstart)); + adns__query_fail(qu,adns_s_prohibitedcname); + return; + } else if (wantedrrs) { /* Ignore CNAME(s) after RR(s). */ + adns__debug(ads,serv,qu,"ignoring CNAME (to %s) coexisting with RR", + adns__diag_domain(ads,serv,qu, &qu->vb, dgram,dglen,rdstart)); + } else { + qu->cname_begin= rdstart; + qu->cname_dglen= dglen; + st= adns__parse_domain(ads,serv,qu, &qu->vb, + qu->flags & adns_qf_quoteok_cname ? pdf_quoteok : 0, + dgram,dglen, &rdstart,rdstart+rdlength); + if (!qu->vb.used) goto x_truncated; + if (st) { adns__query_fail(qu,st); return; } + l= strlen(qu->vb.buf)+1; + qu->answer->cname= adns__alloc_preserved(qu,l); + if (!qu->answer->cname) { adns__query_fail(qu,adns_s_nomemory); return; } + + qu->cname_dgram= adns__alloc_mine(qu,dglen); + memcpy(qu->cname_dgram,dgram,dglen); + + memcpy(qu->answer->cname,qu->vb.buf,l); + cname_here= 1; + adns__update_expires(qu,ttl,now); + /* If we find the answer section truncated after this point we restart + * the query at the CNAME; if beforehand then we obviously have to use + * TCP. If there is no truncation we can use the whole answer if + * it contains the relevant info. + */ + } + } else if (rrtype == (qu->typei->type & adns__rrt_typemask)) { + wantedrrs++; + } else { + adns__debug(ads,serv,qu,"ignoring answer RR with irrelevant type %d",rrtype); + } + } + + /* We defer handling truncated responses here, in case there was a CNAME + * which we could use. + */ + if (flg_tc) goto x_truncated; + + nsstart= cbyte; + + if (!wantedrrs) { + /* Oops, NODATA or NXDOMAIN or perhaps a referral (which would be a problem) */ + + /* RFC2308: NODATA has _either_ a SOA _or_ _no_ NS records in authority section */ + foundsoa= 0; soattl= 0; foundns= 0; + for (rri= 0; rri<nscount; rri++) { + rrstart= cbyte; + st= adns__findrr(qu,serv, dgram,dglen,&cbyte, + &rrtype,&rrclass,&ttl, &rdlength,&rdstart, 0); + if (st) { adns__query_fail(qu,st); return; } + if (rrtype==-1) goto x_truncated; + if (rrclass != DNS_CLASS_IN) { + adns__diag(ads,serv,qu, + "ignoring authority RR with wrong class %d (expected IN=%d)", + rrclass,DNS_CLASS_IN); + continue; + } + if (rrtype == adns_r_soa_raw) { foundsoa= 1; soattl= ttl; break; } + else if (rrtype == adns_r_ns_raw) { foundns= 1; } + } + + if (rcode == rcode_nxdomain) { + /* We still wanted to look for the SOA so we could find the TTL. */ + adns__update_expires(qu,soattl,now); + + if (qu->flags & adns_qf_search) { + adns__search_next(ads,qu,now); + } else { + adns__query_fail(qu,adns_s_nxdomain); + } + return; + } + + if (foundsoa || !foundns) { + /* Aha ! A NODATA response, good. */ + adns__update_expires(qu,soattl,now); + adns__query_fail(qu,adns_s_nodata); + return; + } + + /* Now what ? No relevant answers, no SOA, and at least some NS's. + * Looks like a referral. Just one last chance ... if we came across + * a CNAME in this datagram then we should probably do our own CNAME + * lookup now in the hope that we won't get a referral again. + */ + if (cname_here) goto x_restartquery; + + /* Bloody hell, I thought we asked for recursion ? */ + if (flg_rd) { + adns__diag(ads,serv,qu,"server thinks we didn't ask for recursive lookup"); + } + if (!flg_ra) { + adns__diag(ads,serv,qu,"server is not willing to do recursive lookups for us"); + adns__query_fail(qu,adns_s_norecurse); + } else { + adns__diag(ads,serv,qu,"server claims to do recursion, but gave us a referral"); + adns__query_fail(qu,adns_s_invalidresponse); + } + return; + } + + /* Now, we have some RRs which we wanted. */ + + qu->answer->rrs.untyped= adns__alloc_interim(qu,qu->typei->rrsz*wantedrrs); + if (!qu->answer->rrs.untyped) { adns__query_fail(qu,adns_s_nomemory); return; } + + typei= qu->typei; + cbyte= anstart; + rrsdata= qu->answer->rrs.bytes; + + pai.ads= qu->ads; + pai.qu= qu; + pai.serv= serv; + pai.dgram= dgram; + pai.dglen= dglen; + pai.nsstart= nsstart; + pai.nscount= nscount; + pai.arcount= arcount; + pai.now= now; + + for (rri=0, nrrs=0; rri<ancount; rri++) { + st= adns__findrr(qu,serv, dgram,dglen,&cbyte, + &rrtype,&rrclass,&ttl, &rdlength,&rdstart, + &ownermatched); + assert(!st); assert(rrtype != -1); + if (rrclass != DNS_CLASS_IN || + rrtype != (qu->typei->type & adns__rrt_typemask) || + !ownermatched) + continue; + adns__update_expires(qu,ttl,now); + st= typei->parse(&pai, rdstart,rdstart+rdlength, rrsdata+nrrs*typei->rrsz); + if (st) { adns__query_fail(qu,st); return; } + if (rdstart==-1) goto x_truncated; + nrrs++; + } + assert(nrrs==wantedrrs); + qu->answer->nrrs= nrrs; + + /* This may have generated some child queries ... */ + if (qu->children.head) { + qu->state= query_childw; + LIST_LINK_TAIL(ads->childw,qu); + return; + } + adns__query_done(qu); + return; + + x_truncated: + + if (!flg_tc) { + adns__diag(ads,serv,qu,"server sent datagram which points outside itself"); + adns__query_fail(qu,adns_s_invalidresponse); + return; + } + qu->flags |= adns_qf_usevc; + + x_restartquery: + if (qu->cname_dgram) { + st= adns__mkquery_frdgram(qu->ads,&qu->vb,&qu->id, + qu->cname_dgram, qu->cname_dglen, qu->cname_begin, + qu->typei->type, qu->flags); + if (st) { adns__query_fail(qu,st); return; } + + newquery= realloc(qu->query_dgram,qu->vb.used); + if (!newquery) { adns__query_fail(qu,adns_s_nomemory); return; } + + qu->query_dgram= newquery; + qu->query_dglen= qu->vb.used; + memcpy(newquery,qu->vb.buf,qu->vb.used); + } + + if (qu->state == query_tcpw) qu->state= query_tosend; + qu->retries= 0; + adns__reset_preserved(qu); + adns__query_send(qu,now); +} diff --git a/adns-0.6/src/setup.c b/adns-0.6/src/setup.c new file mode 100644 index 0000000..ac88c33 --- /dev/null +++ b/adns-0.6/src/setup.c @@ -0,0 +1,643 @@ +/* + * setup.c + * - configuration file parsing + * - management of global state + */ +/* + * This file is + * Copyright (C) 1997-1999 Ian Jackson <ian@davenant.greenend.org.uk> + * + * It is part of adns, which is + * Copyright (C) 1997-1999 Ian Jackson <ian@davenant.greenend.org.uk> + * Copyright (C) 1999 Tony Finch <dot@dotat.at> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include <stdlib.h> +#include <errno.h> +#include <limits.h> +#include <unistd.h> +#include <fcntl.h> + +#include <netdb.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> + +#include "internal.h" + +static void readconfig(adns_state ads, const char *filename, int warnmissing); + +static void addserver(adns_state ads, struct in_addr addr) { + int i; + struct server *ss; + + for (i=0; i<ads->nservers; i++) { + if (ads->servers[i].addr.s_addr == addr.s_addr) { + adns__debug(ads,-1,0,"duplicate nameserver %s ignored",inet_ntoa(addr)); + return; + } + } + + if (ads->nservers>=MAXSERVERS) { + adns__diag(ads,-1,0,"too many nameservers, ignoring %s",inet_ntoa(addr)); + return; + } + + ss= ads->servers+ads->nservers; + ss->addr= addr; + ads->nservers++; +} + +static void freesearchlist(adns_state ads) { + if (ads->nsearchlist) free(*ads->searchlist); + free(ads->searchlist); +} + +static void saveerr(adns_state ads, int en) { + if (!ads->configerrno) ads->configerrno= en; +} + +static void configparseerr(adns_state ads, const char *fn, int lno, + const char *fmt, ...) { + va_list al; + + saveerr(ads,EINVAL); + if (!ads->diagfile || (ads->iflags & adns_if_noerrprint)) return; + + if (lno==-1) fprintf(ads->diagfile,"adns: %s: ",fn); + else fprintf(ads->diagfile,"adns: %s:%d: ",fn,lno); + va_start(al,fmt); + vfprintf(ads->diagfile,fmt,al); + va_end(al); + fputc('\n',ads->diagfile); +} + +static int nextword(const char **bufp_io, const char **word_r, int *l_r) { + const char *p, *q; + + p= *bufp_io; + while (ctype_whitespace(*p)) p++; + if (!*p) return 0; + + q= p; + while (*q && !ctype_whitespace(*q)) q++; + + *l_r= q-p; + *word_r= p; + *bufp_io= q; + + return 1; +} + +static void ccf_nameserver(adns_state ads, const char *fn, int lno, const char *buf) { + struct in_addr ia; + + if (!inet_aton(buf,&ia)) { + configparseerr(ads,fn,lno,"invalid nameserver address `%s'",buf); + return; + } + adns__debug(ads,-1,0,"using nameserver %s",inet_ntoa(ia)); + addserver(ads,ia); +} + +static void ccf_search(adns_state ads, const char *fn, int lno, const char *buf) { + const char *bufp, *word; + char *newchars, **newptrs, **pp; + int count, tl, l; + + if (!buf) return; + + bufp= buf; + count= 0; + tl= 0; + while (nextword(&bufp,&word,&l)) { count++; tl += l+1; } + + newptrs= malloc(sizeof(char*)*count); if (!newptrs) { saveerr(ads,errno); return; } + newchars= malloc(tl); if (!newchars) { saveerr(ads,errno); free(newptrs); return; } + + bufp= buf; + pp= newptrs; + while (nextword(&bufp,&word,&l)) { + *pp++= newchars; + memcpy(newchars,word,l); + newchars += l; + *newchars++ = 0; + } + + freesearchlist(ads); + ads->nsearchlist= count; + ads->searchlist= newptrs; +} + +static void ccf_sortlist(adns_state ads, const char *fn, int lno, const char *buf) { + const char *word; + char tbuf[200], *slash, *ep; + struct in_addr base, mask; + int l; + unsigned long initial, baselocal; + + if (!buf) return; + + ads->nsortlist= 0; + while (nextword(&buf,&word,&l)) { + if (ads->nsortlist >= MAXSORTLIST) { + adns__diag(ads,-1,0,"too many sortlist entries, ignoring %.*s onwards",l,word); + return; + } + + if (l >= sizeof(tbuf)) { + configparseerr(ads,fn,lno,"sortlist entry `%.*s' too long",l,word); + continue; + } + + memcpy(tbuf,word,l); tbuf[l]= 0; + slash= strchr(tbuf,'/'); + if (slash) *slash++= 0; + + if (!inet_aton(tbuf,&base)) { + configparseerr(ads,fn,lno,"invalid address `%s' in sortlist",tbuf); + continue; + } + + if (slash) { + if (strchr(slash,'.')) { + if (!inet_aton(slash,&mask)) { + configparseerr(ads,fn,lno,"invalid mask `%s' in sortlist",slash); + continue; + } + if (base.s_addr & ~mask.s_addr) { + configparseerr(ads,fn,lno, + "mask `%s' in sortlist overlaps address `%s'",slash,tbuf); + continue; + } + } else { + initial= strtoul(slash,&ep,10); + if (*ep || initial>32) { + configparseerr(ads,fn,lno,"mask length `%s' invalid",slash); + continue; + } + mask.s_addr= htonl((0x0ffffffffUL) << (32-initial)); + } + } else { + baselocal= ntohl(base.s_addr); + if (!baselocal & 0x080000000UL) /* class A */ + mask.s_addr= htonl(0x0ff000000UL); + else if ((baselocal & 0x0c0000000UL) == 0x080000000UL) + mask.s_addr= htonl(0x0ffff0000UL); /* class B */ + else if ((baselocal & 0x0f0000000UL) == 0x0e0000000UL) + mask.s_addr= htonl(0x0ff000000UL); /* class C */ + else { + configparseerr(ads,fn,lno, + "network address `%s' in sortlist is not in classed ranges," + " must specify mask explicitly", tbuf); + continue; + } + } + + ads->sortlist[ads->nsortlist].base= base; + ads->sortlist[ads->nsortlist].mask= mask; + ads->nsortlist++; + } +} + +static void ccf_options(adns_state ads, const char *fn, int lno, const char *buf) { + const char *word; + char *ep; + unsigned long v; + int l; + + if (!buf) return; + + while (nextword(&buf,&word,&l)) { + if (l==5 && !memcmp(word,"debug",5)) { + ads->iflags |= adns_if_debug; + continue; + } + if (l>=6 && !memcmp(word,"ndots:",6)) { + v= strtoul(word+6,&ep,10); + if (l==6 || ep != word+l || v > INT_MAX) { + configparseerr(ads,fn,lno,"option `%.*s' malformed or has bad value",l,word); + continue; + } + ads->searchndots= v; + continue; + } + if (l>=12 && !memcmp(word,"adns_checkc:",12)) { + if (!strcmp(word+12,"none")) { + ads->iflags &= ~adns_if_checkc_freq; + ads->iflags |= adns_if_checkc_entex; + } else if (!strcmp(word+12,"entex")) { + ads->iflags &= ~adns_if_checkc_freq; + ads->iflags |= adns_if_checkc_entex; + } else if (!strcmp(word+12,"freq")) { + ads->iflags |= adns_if_checkc_freq; + } else { + configparseerr(ads,fn,lno, "option adns_checkc has bad value `%s' " + "(must be none, entex or freq", word+12); + } + continue; + } + adns__diag(ads,-1,0,"%s:%d: unknown option `%.*s'", fn,lno, l,word); + } +} + +static void ccf_clearnss(adns_state ads, const char *fn, int lno, const char *buf) { + ads->nservers= 0; +} + +static void ccf_include(adns_state ads, const char *fn, int lno, const char *buf) { + if (!*buf) { + configparseerr(ads,fn,lno,"`include' directive with no filename"); + return; + } + readconfig(ads,buf,1); +} + +static const struct configcommandinfo { + const char *name; + void (*fn)(adns_state ads, const char *fn, int lno, const char *buf); +} configcommandinfos[]= { + { "nameserver", ccf_nameserver }, + { "domain", ccf_search }, + { "search", ccf_search }, + { "sortlist", ccf_sortlist }, + { "options", ccf_options }, + { "clearnameservers", ccf_clearnss }, + { "include", ccf_include }, + { 0 } +}; + +typedef union { + FILE *file; + const char *text; +} getline_ctx; + +static int gl_file(adns_state ads, getline_ctx *src_io, const char *filename, + int lno, char *buf, int buflen) { + FILE *file= src_io->file; + int c, i; + char *p; + + p= buf; + buflen--; + i= 0; + + for (;;) { /* loop over chars */ + if (i == buflen) { + adns__diag(ads,-1,0,"%s:%d: line too long, ignored",filename,lno); + goto x_badline; + } + c= getc(file); + if (!c) { + adns__diag(ads,-1,0,"%s:%d: line contains nul, ignored",filename,lno); + goto x_badline; + } else if (c == '\n') { + break; + } else if (c == EOF) { + if (ferror(file)) { + saveerr(ads,errno); + adns__diag(ads,-1,0,"%s:%d: read error: %s",filename,lno,strerror(errno)); + return -1; + } + if (!i) return -1; + break; + } else { + *p++= c; + i++; + } + } + + *p++= 0; + return i; + + x_badline: + saveerr(ads,EINVAL); + while ((c= getc(file)) != EOF && c != '\n'); + return -2; +} + +static int gl_text(adns_state ads, getline_ctx *src_io, const char *filename, + int lno, char *buf, int buflen) { + const char *cp= src_io->text; + int l; + + if (!cp || !*cp) return -1; + + if (*cp == ';' || *cp == '\n') cp++; + l= strcspn(cp,";\n"); + src_io->text = cp+l; + + if (l >= buflen) { + adns__diag(ads,-1,0,"%s:%d: line too long, ignored",filename,lno); + saveerr(ads,EINVAL); + return -2; + } + + memcpy(buf,cp,l); + buf[l]= 0; + return l; +} + +static void readconfiggeneric(adns_state ads, const char *filename, + int (*getline)(adns_state ads, getline_ctx*, + const char *filename, int lno, + char *buf, int buflen), + /* Returns >=0 for success, -1 for EOF or error + * (error will have been reported), or -2 for + * bad line was encountered, try again. + */ + getline_ctx gl_ctx) { + char linebuf[2000], *p, *q; + int lno, l, dirl; + const struct configcommandinfo *ccip; + + for (lno=1; + (l= getline(ads,&gl_ctx, filename,lno, linebuf,sizeof(linebuf))) != -1; + lno++) { + if (l == -2) continue; + while (l>0 && ctype_whitespace(linebuf[l-1])) l--; + linebuf[l]= 0; + p= linebuf; + while (ctype_whitespace(*p)) p++; + if (*p == '#' || !*p) continue; + q= p; + while (*q && !ctype_whitespace(*q)) q++; + dirl= q-p; + for (ccip=configcommandinfos; + ccip->name && !(strlen(ccip->name)==dirl && !memcmp(ccip->name,p,q-p)); + ccip++); + if (!ccip->name) { + adns__diag(ads,-1,0,"%s:%d: unknown configuration directive `%.*s'", + filename,lno,q-p,p); + continue; + } + while (ctype_whitespace(*q)) q++; + ccip->fn(ads,filename,lno,q); + } +} + +static const char *instrum_getenv(adns_state ads, const char *envvar) { + const char *value; + + value= getenv(envvar); + if (!value) adns__debug(ads,-1,0,"environment variable %s not set",envvar); + else adns__debug(ads,-1,0,"environment variable %s set to `%s'",envvar,value); + return value; +} + +static void readconfig(adns_state ads, const char *filename, int warnmissing) { + getline_ctx gl_ctx; + + gl_ctx.file= fopen(filename,"r"); + if (!gl_ctx.file) { + if (errno == ENOENT) { + if (warnmissing) + adns__debug(ads,-1,0,"configuration file `%s' does not exist",filename); + return; + } + saveerr(ads,errno); + adns__diag(ads,-1,0,"cannot open configuration file `%s': %s", + filename,strerror(errno)); + return; + } + + readconfiggeneric(ads,filename,gl_file,gl_ctx); + + fclose(gl_ctx.file); +} + +static void readconfigtext(adns_state ads, const char *text, const char *showname) { + getline_ctx gl_ctx; + + gl_ctx.text= text; + readconfiggeneric(ads,showname,gl_text,gl_ctx); +} + +static void readconfigenv(adns_state ads, const char *envvar) { + const char *filename; + + if (ads->iflags & adns_if_noenv) { + adns__debug(ads,-1,0,"not checking environment variable `%s'",envvar); + return; + } + filename= instrum_getenv(ads,envvar); + if (filename) readconfig(ads,filename,1); +} + +static void readconfigenvtext(adns_state ads, const char *envvar) { + const char *textdata; + + if (ads->iflags & adns_if_noenv) { + adns__debug(ads,-1,0,"not checking environment variable `%s'",envvar); + return; + } + textdata= instrum_getenv(ads,envvar); + if (textdata) readconfigtext(ads,textdata,envvar); +} + + +int adns__setnonblock(adns_state ads, int fd) { + int r; + + r= fcntl(fd,F_GETFL,0); if (r<0) return errno; + r |= O_NONBLOCK; + r= fcntl(fd,F_SETFL,r); if (r<0) return errno; + return 0; +} + +static int init_begin(adns_state *ads_r, adns_initflags flags, FILE *diagfile) { + adns_state ads; + + ads= malloc(sizeof(*ads)); if (!ads) return errno; + + ads->iflags= flags; + ads->diagfile= diagfile; + ads->configerrno= 0; + LIST_INIT(ads->udpw); + LIST_INIT(ads->tcpw); + LIST_INIT(ads->childw); + LIST_INIT(ads->output); + ads->forallnext= 0; + ads->nextid= 0x311f; + ads->udpsocket= ads->tcpsocket= -1; + adns__vbuf_init(&ads->tcpsend); + adns__vbuf_init(&ads->tcprecv); + ads->tcprecv_skip= 0; + ads->nservers= ads->nsortlist= ads->nsearchlist= ads->tcpserver= 0; + ads->searchndots= 1; + ads->tcpstate= server_disconnected; + timerclear(&ads->tcptimeout); + ads->searchlist= 0; + + *ads_r= ads; + return 0; +} + +static int init_finish(adns_state ads) { + struct in_addr ia; + struct protoent *proto; + int r; + + if (!ads->nservers) { + if (ads->diagfile && ads->iflags & adns_if_debug) + fprintf(ads->diagfile,"adns: no nameservers, using localhost\n"); + ia.s_addr= htonl(INADDR_LOOPBACK); + addserver(ads,ia); + } + + proto= getprotobyname("udp"); if (!proto) { r= ENOPROTOOPT; goto x_free; } + ads->udpsocket= socket(AF_INET,SOCK_DGRAM,proto->p_proto); + if (ads->udpsocket<0) { r= errno; goto x_free; } + + r= adns__setnonblock(ads,ads->udpsocket); + if (r) { r= errno; goto x_closeudp; } + + return 0; + + x_closeudp: + close(ads->udpsocket); + x_free: + free(ads); + return r; +} + +static void init_abort(adns_state ads) { + if (ads->nsearchlist) { + free(ads->searchlist[0]); + free(ads->searchlist); + } + free(ads); +} + +int adns_init(adns_state *ads_r, adns_initflags flags, FILE *diagfile) { + adns_state ads; + const char *res_options, *adns_res_options; + int r; + + r= init_begin(&ads, flags, diagfile ? diagfile : stderr); + if (r) return r; + + res_options= instrum_getenv(ads,"RES_OPTIONS"); + adns_res_options= instrum_getenv(ads,"ADNS_RES_OPTIONS"); + ccf_options(ads,"RES_OPTIONS",-1,res_options); + ccf_options(ads,"ADNS_RES_OPTIONS",-1,adns_res_options); + + readconfig(ads,"/etc/resolv.conf",1); + readconfig(ads,"/etc/resolv-adns.conf",0); + readconfigenv(ads,"RES_CONF"); + readconfigenv(ads,"ADNS_RES_CONF"); + + readconfigenvtext(ads,"RES_CONF_TEXT"); + readconfigenvtext(ads,"ADNS_RES_CONF_TEXT"); + + ccf_options(ads,"RES_OPTIONS",-1,res_options); + ccf_options(ads,"ADNS_RES_OPTIONS",-1,adns_res_options); + + ccf_search(ads,"LOCALDOMAIN",-1,instrum_getenv(ads,"LOCALDOMAIN")); + ccf_search(ads,"ADNS_LOCALDOMAIN",-1,instrum_getenv(ads,"ADNS_LOCALDOMAIN")); + + if (ads->configerrno && ads->configerrno != EINVAL) { + r= ads->configerrno; + init_abort(ads); + return r; + } + + r= init_finish(ads); + if (r) return r; + + adns__consistency(ads,0,cc_entex); + *ads_r= ads; + return 0; +} + +int adns_init_strcfg(adns_state *ads_r, adns_initflags flags, + FILE *diagfile, const char *configtext) { + adns_state ads; + int r; + + r= init_begin(&ads, flags, diagfile); if (r) return r; + + readconfigtext(ads,configtext,"<supplied configuration text>"); + if (ads->configerrno) { + r= ads->configerrno; + init_abort(ads); + return r; + } + + r= init_finish(ads); if (r) return r; + adns__consistency(ads,0,cc_entex); + *ads_r= ads; + return 0; +} + + +void adns_finish(adns_state ads) { + adns__consistency(ads,0,cc_entex); + for (;;) { + if (ads->udpw.head) adns_cancel(ads->udpw.head); + else if (ads->tcpw.head) adns_cancel(ads->tcpw.head); + else if (ads->childw.head) adns_cancel(ads->childw.head); + else if (ads->output.head) adns_cancel(ads->output.head); + else break; + } + close(ads->udpsocket); + if (ads->tcpsocket >= 0) close(ads->tcpsocket); + adns__vbuf_free(&ads->tcpsend); + adns__vbuf_free(&ads->tcprecv); + freesearchlist(ads); + free(ads); +} + +void adns_forallqueries_begin(adns_state ads) { + adns__consistency(ads,0,cc_entex); + ads->forallnext= + ads->udpw.head ? ads->udpw.head : + ads->tcpw.head ? ads->tcpw.head : + ads->childw.head ? ads->childw.head : + ads->output.head; +} + +adns_query adns_forallqueries_next(adns_state ads, void **context_r) { + adns_query qu, nqu; + + adns__consistency(ads,0,cc_entex); + nqu= ads->forallnext; + for (;;) { + qu= nqu; + if (!qu) return 0; + if (qu->next) { + nqu= qu->next; + } else if (qu == ads->udpw.tail) { + nqu= + ads->tcpw.head ? ads->tcpw.head : + ads->childw.head ? ads->childw.head : + ads->output.head; + } else if (qu == ads->tcpw.tail) { + nqu= + ads->childw.head ? ads->childw.head : + ads->output.head; + } else if (qu == ads->childw.tail) { + nqu= ads->output.head; + } else { + nqu= 0; + } + if (!qu->parent) break; + } + ads->forallnext= nqu; + if (context_r) *context_r= qu->ctx.ext; + return qu; +} diff --git a/adns-0.6/src/transmit.c b/adns-0.6/src/transmit.c new file mode 100644 index 0000000..c669af6 --- /dev/null +++ b/adns-0.6/src/transmit.c @@ -0,0 +1,257 @@ +/* + * transmit.c + * - construct queries + * - send queries + */ +/* + * This file is + * Copyright (C) 1997-1999 Ian Jackson <ian@davenant.greenend.org.uk> + * + * It is part of adns, which is + * Copyright (C) 1997-1999 Ian Jackson <ian@davenant.greenend.org.uk> + * Copyright (C) 1999 Tony Finch <dot@dotat.at> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include <errno.h> + +#include <sys/types.h> +#include <sys/uio.h> + +#include "internal.h" +#include "tvarith.h" + +#define MKQUERY_START(vb) (rqp= (vb)->buf+(vb)->used) +#define MKQUERY_ADDB(b) *rqp++= (b) +#define MKQUERY_ADDW(w) (MKQUERY_ADDB(((w)>>8)&0x0ff), MKQUERY_ADDB((w)&0x0ff)) +#define MKQUERY_STOP(vb) ((vb)->used= rqp-(vb)->buf) + +static adns_status mkquery_header(adns_state ads, vbuf *vb, int *id_r, int qdlen) { + int id; + byte *rqp; + + if (!adns__vbuf_ensure(vb,DNS_HDRSIZE+qdlen+4)) return adns_s_nomemory; + + vb->used= 0; + MKQUERY_START(vb); + + *id_r= id= (ads->nextid++) & 0x0ffff; + MKQUERY_ADDW(id); + MKQUERY_ADDB(0x01); /* QR=Q(0), OPCODE=QUERY(0000), !AA, !TC, RD */ + MKQUERY_ADDB(0x00); /* !RA, Z=000, RCODE=NOERROR(0000) */ + MKQUERY_ADDW(1); /* QDCOUNT=1 */ + MKQUERY_ADDW(0); /* ANCOUNT=0 */ + MKQUERY_ADDW(0); /* NSCOUNT=0 */ + MKQUERY_ADDW(0); /* ARCOUNT=0 */ + + MKQUERY_STOP(vb); + + return adns_s_ok; +} + +static adns_status mkquery_footer(vbuf *vb, adns_rrtype type) { + byte *rqp; + + MKQUERY_START(vb); + MKQUERY_ADDW(type & adns__rrt_typemask); /* QTYPE */ + MKQUERY_ADDW(DNS_CLASS_IN); /* QCLASS=IN */ + MKQUERY_STOP(vb); + assert(vb->used <= vb->avail); + + return adns_s_ok; +} + +adns_status adns__mkquery(adns_state ads, vbuf *vb, int *id_r, + const char *owner, int ol, + const typeinfo *typei, adns_queryflags flags) { + int ll, c, nlabs; + byte label[255], *rqp; + const char *p, *pe; + adns_status st; + + st= mkquery_header(ads,vb,id_r,ol+2); if (st) return st; + + MKQUERY_START(vb); + + p= owner; pe= owner+ol; + nlabs= 0; + while (p!=pe) { + ll= 0; + while (p!=pe && (c= *p++)!='.') { + if (c=='\\') { + if (!(flags & adns_qf_quoteok_query)) return adns_s_querydomaininvalid; + if (ctype_digit(p[0])) { + if (ctype_digit(p[1]) && ctype_digit(p[2])) { + c= (*p++ - '0')*100 + (*p++ - '0')*10 + (*p++ - '0'); + if (c >= 256) return adns_s_querydomaininvalid; + } else { + return adns_s_querydomaininvalid; + } + } else if (!(c= *p++)) { + return adns_s_querydomaininvalid; + } + } + if (!(flags & adns_qf_quoteok_query)) { + if (c == '-') { + if (!ll) return adns_s_querydomaininvalid; + } else if (!ctype_alpha(c) && !ctype_digit(c)) { + return adns_s_querydomaininvalid; + } + } + if (ll == sizeof(label)) return adns_s_querydomaininvalid; + label[ll++]= c; + } + if (!ll) return adns_s_querydomaininvalid; + if (nlabs++ > 63) return adns_s_querydomaintoolong; + MKQUERY_ADDB(ll); + memcpy(rqp,label,ll); rqp+= ll; + } + MKQUERY_ADDB(0); + + MKQUERY_STOP(vb); + + st= mkquery_footer(vb,typei->type); + + return adns_s_ok; +} + +adns_status adns__mkquery_frdgram(adns_state ads, vbuf *vb, int *id_r, + const byte *qd_dgram, int qd_dglen, int qd_begin, + adns_rrtype type, adns_queryflags flags) { + byte *rqp; + findlabel_state fls; + int lablen, labstart; + adns_status st; + + st= mkquery_header(ads,vb,id_r,qd_dglen); if (st) return st; + + MKQUERY_START(vb); + + adns__findlabel_start(&fls,ads,-1,0,qd_dgram,qd_dglen,qd_dglen,qd_begin,0); + for (;;) { + st= adns__findlabel_next(&fls,&lablen,&labstart); assert(!st); + if (!lablen) break; + assert(lablen<255); + MKQUERY_ADDB(lablen); + memcpy(rqp,qd_dgram+labstart,lablen); + rqp+= lablen; + } + MKQUERY_ADDB(0); + + MKQUERY_STOP(vb); + + st= mkquery_footer(vb,type); + + return adns_s_ok; +} + +void adns__querysend_tcp(adns_query qu, struct timeval now) { + byte length[2]; + struct iovec iov[2]; + int wr, r; + adns_state ads; + + if (qu->ads->tcpstate != server_ok) return; + + assert(qu->state == query_tcpw); + + length[0]= (qu->query_dglen&0x0ff00U) >>8; + length[1]= (qu->query_dglen&0x0ff); + + ads= qu->ads; + if (!adns__vbuf_ensure(&ads->tcpsend,ads->tcpsend.used+qu->query_dglen+2)) return; + + qu->retries++; + + /* Reset idle timeout. */ + ads->tcptimeout.tv_sec= ads->tcptimeout.tv_usec= 0; + + if (ads->tcpsend.used) { + wr= 0; + } else { + iov[0].iov_base= length; + iov[0].iov_len= 2; + iov[1].iov_base= qu->query_dgram; + iov[1].iov_len= qu->query_dglen; + adns__sigpipe_protect(qu->ads); + wr= writev(qu->ads->tcpsocket,iov,2); + adns__sigpipe_unprotect(qu->ads); + if (wr < 0) { + if (!(errno == EAGAIN || errno == EINTR || errno == ENOSPC || + errno == ENOBUFS || errno == ENOMEM)) { + adns__tcp_broken(ads,"write",strerror(errno)); + return; + } + wr= 0; + } + } + + if (wr<2) { + r= adns__vbuf_append(&ads->tcpsend,length,2-wr); assert(r); + wr= 0; + } else { + wr-= 2; + } + if (wr<qu->query_dglen) { + r= adns__vbuf_append(&ads->tcpsend,qu->query_dgram+wr,qu->query_dglen-wr); assert(r); + } +} + +static void query_usetcp(adns_query qu, struct timeval now) { + qu->state= query_tcpw; + qu->timeout= now; + timevaladd(&qu->timeout,TCPWAITMS); + LIST_LINK_TAIL(qu->ads->tcpw,qu); + adns__querysend_tcp(qu,now); + adns__tcp_tryconnect(qu->ads,now); +} + +void adns__query_send(adns_query qu, struct timeval now) { + struct sockaddr_in servaddr; + int serv, r; + adns_state ads; + + assert(qu->state == query_tosend); + if ((qu->flags & adns_qf_usevc) || (qu->query_dglen > DNS_MAXUDP)) { + query_usetcp(qu,now); + return; + } + + if (qu->retries >= UDPMAXRETRIES) { + adns__query_fail(qu,adns_s_timeout); + return; + } + + serv= qu->udpnextserver; + memset(&servaddr,0,sizeof(servaddr)); + + ads= qu->ads; + servaddr.sin_family= AF_INET; + servaddr.sin_addr= ads->servers[serv].addr; + servaddr.sin_port= htons(DNS_PORT); + + r= sendto(ads->udpsocket,qu->query_dgram,qu->query_dglen,0, + (const struct sockaddr*)&servaddr,sizeof(servaddr)); + if (r<0 && errno == EMSGSIZE) { qu->retries= 0; query_usetcp(qu,now); return; } + if (r<0) adns__warn(ads,serv,0,"sendto failed: %s",strerror(errno)); + + qu->timeout= now; + timevaladd(&qu->timeout,UDPRETRYMS); + qu->udpsent |= (1<<serv); + qu->udpnextserver= (serv+1)%ads->nservers; + qu->retries++; + LIST_LINK_TAIL(ads->udpw,qu); +} diff --git a/adns-0.6/src/tvarith.h b/adns-0.6/src/tvarith.h new file mode 100644 index 0000000..202da1f --- /dev/null +++ b/adns-0.6/src/tvarith.h @@ -0,0 +1,41 @@ +/* + * tvarith.h + * - static inline functions for doing arithmetic on timevals + */ +/* + * This file is + * Copyright (C) 1997-1999 Ian Jackson <ian@davenant.greenend.org.uk> + * + * It is part of adns, which is + * Copyright (C) 1997-1999 Ian Jackson <ian@davenant.greenend.org.uk> + * Copyright (C) 1999 Tony Finch <dot@dotat.at> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef ADNS_TVARITH_H_INCLUDED +#define ADNS_TVARITH_H_INCLUDED + +static inline void timevaladd(struct timeval *tv_io, long ms) { + struct timeval tmp; + assert(ms>=0); + tmp= *tv_io; + tmp.tv_usec += (ms%1000)*1000; + tmp.tv_sec += ms/1000; + if (tmp.tv_usec >= 1000000) { tmp.tv_sec++; tmp.tv_usec -= 1000000; } + *tv_io= tmp; +} + +#endif diff --git a/adns-0.6/src/types.c b/adns-0.6/src/types.c new file mode 100644 index 0000000..668986f --- /dev/null +++ b/adns-0.6/src/types.c @@ -0,0 +1,1034 @@ +/* + * types.c + * - RR-type-specific code, and the machinery to call it + */ +/* + * This file is + * Copyright (C) 1997-1999 Ian Jackson <ian@davenant.greenend.org.uk> + * + * It is part of adns, which is + * Copyright (C) 1997-1999 Ian Jackson <ian@davenant.greenend.org.uk> + * Copyright (C) 1999 Tony Finch <dot@dotat.at> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include <stdlib.h> + +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> + +#include "internal.h" + +#define R_NOMEM return adns_s_nomemory +#define CSP_ADDSTR(s) do { if (!adns__vbuf_appendstr(vb,(s))) R_NOMEM; } while (0) + +/* + * order of sections: + * + * _string (pap) + * _textdata, _qstring (csp) + * _str (mf,cs) + * _intstr (mf,csp,cs) + * _manyistr (mf,cs) + * _txt (pa) + * _inaddr (pa,dip,di) + * _addr (pa,di,csp,cs) + * _domain (pap) + * _host_raw (pa) + * _hostaddr (pap,pa,dip,di,mfp,mf,csp,cs +pap_findaddrs) + * _mx_raw (pa,di) + * _mx (pa,di) + * _inthostaddr (mf,cs) + * _ptr (pa) + * _strpair (mf,cs) + * _intstrpair (mf,cs) + * _hinfo (pa) + * _mailbox (pap +pap_mailbox822) + * _rp (pa) + * _soa (pa,mf,cs) + * _flat (mf) + * + * within each section: + * pap_* + * pa_* + * dip_* + * di_* + * mfp_* + * mf_* + * csp_* + * cs_* + */ + +/* + * _qstring (pap,csp) + */ + +static adns_status pap_qstring(const parseinfo *pai, int *cbyte_io, int max, + int *len_r, char **str_r) { + /* Neither len_r nor str_r may be null. + * End of datagram (overrun) is indicated by returning adns_s_invaliddata; + */ + const byte *dgram= pai->dgram; + int l, cbyte; + char *str; + + cbyte= *cbyte_io; + + if (cbyte >= max) return adns_s_invaliddata; + GET_B(cbyte,l); + if (cbyte+l > max) return adns_s_invaliddata; + + str= adns__alloc_interim(pai->qu, l+1); + if (!str) R_NOMEM; + + str[l]= 0; + memcpy(str,dgram+cbyte,l); + + *len_r= l; + *str_r= str; + *cbyte_io= cbyte+l; + + return adns_s_ok; +} + +static adns_status csp_qstring(vbuf *vb, const char *dp, int len) { + unsigned char ch; + char buf[10]; + int cn; + + CSP_ADDSTR("\""); + for (cn=0; cn<len; cn++) { + ch= *dp++; + if (ch == '\\') { + CSP_ADDSTR("\\\\"); + } else if (ch == '"') { + CSP_ADDSTR("\\\""); + } else if (ch >= 32 && ch <= 126) { + if (!adns__vbuf_append(vb,&ch,1)) R_NOMEM; + } else { + sprintf(buf,"\\x%02x",ch); + CSP_ADDSTR(buf); + } + } + CSP_ADDSTR("\""); + + return adns_s_ok; +} + +/* + * _str (mf) + */ + +static void mf_str(adns_query qu, void *datap) { + char **rrp= datap; + + adns__makefinal_str(qu,rrp); +} + +/* + * _intstr (mf) + */ + +static void mf_intstr(adns_query qu, void *datap) { + adns_rr_intstr *rrp= datap; + + adns__makefinal_str(qu,&rrp->str); +} + +/* + * _manyistr (mf) + */ + +static void mf_manyistr(adns_query qu, void *datap) { + adns_rr_intstr **rrp= datap; + adns_rr_intstr *te, *table; + void *tablev; + int tc; + + for (tc=0, te= *rrp; te->i >= 0; te++, tc++); + tablev= *rrp; + adns__makefinal_block(qu,&tablev,sizeof(*te)*(tc+1)); + *rrp= table= tablev; + for (te= *rrp; te->i >= 0; te++) + adns__makefinal_str(qu,&te->str); +} + +/* + * _txt (pa,cs) + */ + +static adns_status pa_txt(const parseinfo *pai, int cbyte, int max, void *datap) { + adns_rr_intstr **rrp= datap, *table, *te; + const byte *dgram= pai->dgram; + int ti, tc, l, startbyte; + adns_status st; + + startbyte= cbyte; + if (cbyte >= max) return adns_s_invaliddata; + tc= 0; + while (cbyte < max) { + GET_B(cbyte,l); + cbyte+= l; + tc++; + } + if (cbyte != max || !tc) return adns_s_invaliddata; + + table= adns__alloc_interim(pai->qu,sizeof(*table)*(tc+1)); + if (!table) R_NOMEM; + + for (cbyte=startbyte, ti=0, te=table; ti<tc; ti++, te++) { + st= pap_qstring(pai, &cbyte, max, &te->i, &te->str); + if (st) return st; + } + assert(cbyte == max); + + te->i= -1; + te->str= 0; + + *rrp= table; + return adns_s_ok; +} + +static adns_status cs_txt(vbuf *vb, const void *datap) { + const adns_rr_intstr *const *rrp= datap; + const adns_rr_intstr *current; + adns_status st; + int spc; + + for (current= *rrp, spc=0; current->i >= 0; current++, spc=1) { + if (spc) CSP_ADDSTR(" "); + st= csp_qstring(vb,current->str,current->i); if (st) return st; + } + return adns_s_ok; +} + +/* + * _hinfo (cs) + */ + +static adns_status cs_hinfo(vbuf *vb, const void *datap) { + const adns_rr_intstrpair *rrp= datap; + adns_status st; + + st= csp_qstring(vb,rrp->array[0].str,rrp->array[0].i); if (st) return st; + CSP_ADDSTR(" "); + st= csp_qstring(vb,rrp->array[1].str,rrp->array[1].i); if (st) return st; + return adns_s_ok; +} + +/* + * _inaddr (pa,dip,di) + */ + +static adns_status pa_inaddr(const parseinfo *pai, int cbyte, int max, void *datap) { + struct in_addr *storeto= datap; + + if (max-cbyte != 4) return adns_s_invaliddata; + memcpy(storeto, pai->dgram + cbyte, 4); + return adns_s_ok; +} + +static int search_sortlist(adns_state ads, struct in_addr ad) { + const struct sortlist *slp; + int i; + + for (i=0, slp=ads->sortlist; + i<ads->nsortlist && !((ad.s_addr & slp->mask.s_addr) == slp->base.s_addr); + i++, slp++); + return i; +} + +static int dip_inaddr(adns_state ads, struct in_addr a, struct in_addr b) { + int ai, bi; + + if (!ads->nsortlist) return 0; + + ai= search_sortlist(ads,a); + bi= search_sortlist(ads,b); + return bi<ai; +} + +static int di_inaddr(adns_state ads, const void *datap_a, const void *datap_b) { + const struct in_addr *ap= datap_a, *bp= datap_b; + + return dip_inaddr(ads,*ap,*bp); +} + +static adns_status cs_inaddr(vbuf *vb, const void *datap) { + const struct in_addr *rrp= datap, rr= *rrp; + const char *ia; + + ia= inet_ntoa(rr); assert(ia); + CSP_ADDSTR(ia); + return adns_s_ok; +} + +/* + * _addr (pa,di,csp,cs) + */ + +static adns_status pa_addr(const parseinfo *pai, int cbyte, int max, void *datap) { + adns_rr_addr *storeto= datap; + const byte *dgram= pai->dgram; + + if (max-cbyte != 4) return adns_s_invaliddata; + storeto->len= sizeof(storeto->addr.inet); + memset(&storeto->addr,0,sizeof(storeto->addr.inet)); + storeto->addr.inet.sin_family= AF_INET; + memcpy(&storeto->addr.inet.sin_addr,dgram+cbyte,4); + return adns_s_ok; +} + +static int di_addr(adns_state ads, const void *datap_a, const void *datap_b) { + const adns_rr_addr *ap= datap_a, *bp= datap_b; + + assert(ap->addr.sa.sa_family == AF_INET); + return dip_inaddr(ads, ap->addr.inet.sin_addr, bp->addr.inet.sin_addr); +} + +static int div_addr(void *context, const void *datap_a, const void *datap_b) { + const adns_state ads= context; + + return di_addr(ads, datap_a, datap_b); +} + +static adns_status csp_addr(vbuf *vb, const adns_rr_addr *rrp) { + const char *ia; + static char buf[30]; + + switch (rrp->addr.inet.sin_family) { + case AF_INET: + CSP_ADDSTR("INET "); + ia= inet_ntoa(rrp->addr.inet.sin_addr); assert(ia); + CSP_ADDSTR(ia); + break; + default: + sprintf(buf,"AF=%u",rrp->addr.sa.sa_family); + CSP_ADDSTR(buf); + break; + } + return adns_s_ok; +} + +static adns_status cs_addr(vbuf *vb, const void *datap) { + const adns_rr_addr *rrp= datap; + + return csp_addr(vb,rrp); +} + +/* + * _domain (pap,csp,cs) + * _dom_raw (pa) + */ + +static adns_status pap_domain(const parseinfo *pai, int *cbyte_io, int max, + char **domain_r, parsedomain_flags flags) { + adns_status st; + char *dm; + + st= adns__parse_domain(pai->qu->ads, pai->serv, pai->qu, &pai->qu->vb, flags, + pai->dgram,pai->dglen, cbyte_io, max); + if (st) return st; + if (!pai->qu->vb.used) return adns_s_invaliddata; + + dm= adns__alloc_interim(pai->qu, pai->qu->vb.used+1); + if (!dm) R_NOMEM; + + dm[pai->qu->vb.used]= 0; + memcpy(dm,pai->qu->vb.buf,pai->qu->vb.used); + + *domain_r= dm; + return adns_s_ok; +} + +static adns_status csp_domain(vbuf *vb, const char *domain) { + CSP_ADDSTR(domain); + if (!*domain) CSP_ADDSTR("."); + return adns_s_ok; +} + +static adns_status cs_domain(vbuf *vb, const void *datap) { + const char *const *domainp= datap; + return csp_domain(vb,*domainp); +} + +static adns_status pa_dom_raw(const parseinfo *pai, int cbyte, int max, void *datap) { + char **rrp= datap; + adns_status st; + + st= pap_domain(pai, &cbyte, max, rrp, pdf_quoteok); + if (st) return st; + + if (cbyte != max) return adns_s_invaliddata; + return adns_s_ok; +} + +/* + * _host_raw (pa) + */ + +static adns_status pa_host_raw(const parseinfo *pai, int cbyte, int max, void *datap) { + char **rrp= datap; + adns_status st; + + st= pap_domain(pai, &cbyte, max, rrp, + pai->qu->flags & adns_qf_quoteok_anshost ? pdf_quoteok : 0); + if (st) return st; + + if (cbyte != max) return adns_s_invaliddata; + return adns_s_ok; +} + +/* + * _hostaddr (pap,pa,dip,di,mfp,mf,csp,cs +icb_hostaddr, pap_findaddrs) + */ + +static adns_status pap_findaddrs(const parseinfo *pai, adns_rr_hostaddr *ha, + int *cbyte_io, int count, int dmstart) { + int rri, naddrs; + int type, class, rdlen, rdstart, ownermatched; + unsigned long ttl; + adns_status st; + + for (rri=0, naddrs=-1; rri<count; rri++) { + st= adns__findrr_anychk(pai->qu, pai->serv, pai->dgram, pai->dglen, cbyte_io, + &type, &class, &ttl, &rdlen, &rdstart, + pai->dgram, pai->dglen, dmstart, &ownermatched); + if (st) return st; + if (!ownermatched || class != DNS_CLASS_IN || type != adns_r_a) { + if (naddrs>0) break; else continue; + } + if (naddrs == -1) { + naddrs= 0; + } + if (!adns__vbuf_ensure(&pai->qu->vb, (naddrs+1)*sizeof(adns_rr_addr))) R_NOMEM; + adns__update_expires(pai->qu,ttl,pai->now); + st= pa_addr(pai, rdstart,rdstart+rdlen, + pai->qu->vb.buf + naddrs*sizeof(adns_rr_addr)); + if (st) return st; + naddrs++; + } + if (naddrs >= 0) { + ha->addrs= adns__alloc_interim(pai->qu, naddrs*sizeof(adns_rr_addr)); + if (!ha->addrs) R_NOMEM; + memcpy(ha->addrs, pai->qu->vb.buf, naddrs*sizeof(adns_rr_addr)); + ha->naddrs= naddrs; + ha->astatus= adns_s_ok; + + adns__isort(ha->addrs, naddrs, sizeof(adns_rr_addr), pai->qu->vb.buf, + div_addr, pai->ads); + } + return adns_s_ok; +} + +static void icb_hostaddr(adns_query parent, adns_query child) { + adns_answer *cans= child->answer; + adns_rr_hostaddr *rrp= child->ctx.info.hostaddr; + adns_state ads= parent->ads; + adns_status st; + + st= cans->status; + rrp->astatus= st; + rrp->naddrs= (st>0 && st<=adns_s_max_tempfail) ? -1 : cans->nrrs; + rrp->addrs= cans->rrs.addr; + adns__transfer_interim(child, parent, rrp->addrs, rrp->naddrs*sizeof(adns_rr_addr)); + + if (parent->children.head) { + LIST_LINK_TAIL(ads->childw,parent); + } else { + adns__query_done(parent); + } +} + +static adns_status pap_hostaddr(const parseinfo *pai, int *cbyte_io, + int max, adns_rr_hostaddr *rrp) { + adns_status st; + int dmstart, cbyte; + qcontext ctx; + int id; + adns_query nqu; + adns_queryflags nflags; + + dmstart= cbyte= *cbyte_io; + st= pap_domain(pai, &cbyte, max, &rrp->host, + pai->qu->flags & adns_qf_quoteok_anshost ? pdf_quoteok : 0); + if (st) return st; + *cbyte_io= cbyte; + + rrp->astatus= adns_s_ok; + rrp->naddrs= -1; + rrp->addrs= 0; + + cbyte= pai->nsstart; + + st= pap_findaddrs(pai, rrp, &cbyte, pai->nscount, dmstart); + if (st) return st; + if (rrp->naddrs != -1) return adns_s_ok; + + st= pap_findaddrs(pai, rrp, &cbyte, pai->arcount, dmstart); + if (st) return st; + if (rrp->naddrs != -1) return adns_s_ok; + + st= adns__mkquery_frdgram(pai->ads, &pai->qu->vb, &id, + pai->dgram, pai->dglen, dmstart, + adns_r_addr, adns_qf_quoteok_query); + if (st) return st; + + ctx.ext= 0; + ctx.callback= icb_hostaddr; + ctx.info.hostaddr= rrp; + + nflags= adns_qf_quoteok_query; + if (!(pai->qu->flags & adns_qf_cname_loose)) nflags |= adns_qf_cname_forbid; + + st= adns__internal_submit(pai->ads, &nqu, adns__findtype(adns_r_addr), + &pai->qu->vb, id, nflags, pai->now, &ctx); + if (st) return st; + + nqu->parent= pai->qu; + LIST_LINK_TAIL_PART(pai->qu->children,nqu,siblings.); + + return adns_s_ok; +} + +static adns_status pa_hostaddr(const parseinfo *pai, int cbyte, int max, void *datap) { + adns_rr_hostaddr *rrp= datap; + adns_status st; + + st= pap_hostaddr(pai, &cbyte, max, rrp); + if (st) return st; + if (cbyte != max) return adns_s_invaliddata; + + return adns_s_ok; +} + +static int dip_hostaddr(adns_state ads, const adns_rr_hostaddr *ap, const adns_rr_hostaddr *bp) { + if (ap->astatus != bp->astatus) return ap->astatus; + if (ap->astatus) return 0; + + assert(ap->addrs[0].addr.sa.sa_family == AF_INET); + assert(bp->addrs[0].addr.sa.sa_family == AF_INET); + return dip_inaddr(ads, + ap->addrs[0].addr.inet.sin_addr, + bp->addrs[0].addr.inet.sin_addr); +} + +static int di_hostaddr(adns_state ads, const void *datap_a, const void *datap_b) { + const adns_rr_hostaddr *ap= datap_a, *bp= datap_b; + + return dip_hostaddr(ads, ap,bp); +} + +static void mfp_hostaddr(adns_query qu, adns_rr_hostaddr *rrp) { + void *tablev; + + adns__makefinal_str(qu,&rrp->host); + tablev= rrp->addrs; + adns__makefinal_block(qu, &tablev, rrp->naddrs*sizeof(*rrp->addrs)); + rrp->addrs= tablev; +} + +static void mf_hostaddr(adns_query qu, void *datap) { + adns_rr_hostaddr *rrp= datap; + + mfp_hostaddr(qu,rrp); +} + +static adns_status csp_hostaddr(vbuf *vb, const adns_rr_hostaddr *rrp) { + const char *errstr; + adns_status st; + char buf[20]; + int i; + + st= csp_domain(vb,rrp->host); if (st) return st; + + CSP_ADDSTR(" "); + CSP_ADDSTR(adns_errtypeabbrev(rrp->astatus)); + + sprintf(buf," %d ",rrp->astatus); + CSP_ADDSTR(buf); + + CSP_ADDSTR(adns_errabbrev(rrp->astatus)); + CSP_ADDSTR(" "); + + errstr= adns_strerror(rrp->astatus); + st= csp_qstring(vb,errstr,strlen(errstr)); if (st) return st; + + if (rrp->naddrs >= 0) { + CSP_ADDSTR(" ("); + for (i=0; i<rrp->naddrs; i++) { + CSP_ADDSTR(" "); + st= csp_addr(vb,&rrp->addrs[i]); + } + CSP_ADDSTR(" )"); + } else { + CSP_ADDSTR(" ?"); + } + return adns_s_ok; +} + +static adns_status cs_hostaddr(vbuf *vb, const void *datap) { + const adns_rr_hostaddr *rrp= datap; + + return csp_hostaddr(vb,rrp); +} + +/* + * _mx_raw (pa,di) + */ + +static adns_status pa_mx_raw(const parseinfo *pai, int cbyte, int max, void *datap) { + const byte *dgram= pai->dgram; + adns_rr_intstr *rrp= datap; + adns_status st; + int pref; + + if (cbyte+2 > max) return adns_s_invaliddata; + GET_W(cbyte,pref); + rrp->i= pref; + st= pap_domain(pai, &cbyte, max, &rrp->str, + pai->qu->flags & adns_qf_quoteok_anshost ? pdf_quoteok : 0); + if (st) return st; + + if (cbyte != max) return adns_s_invaliddata; + return adns_s_ok; +} + +static int di_mx_raw(adns_state ads, const void *datap_a, const void *datap_b) { + const adns_rr_intstr *ap= datap_a, *bp= datap_b; + + if (ap->i < bp->i) return 0; + if (ap->i > bp->i) return 1; + return 0; +} + +/* + * _mx (pa,di) + */ + +static adns_status pa_mx(const parseinfo *pai, int cbyte, int max, void *datap) { + const byte *dgram= pai->dgram; + adns_rr_inthostaddr *rrp= datap; + adns_status st; + int pref; + + if (cbyte+2 > max) return adns_s_invaliddata; + GET_W(cbyte,pref); + rrp->i= pref; + st= pap_hostaddr(pai, &cbyte, max, &rrp->ha); + if (st) return st; + + if (cbyte != max) return adns_s_invaliddata; + return adns_s_ok; +} + +static int di_mx(adns_state ads, const void *datap_a, const void *datap_b) { + const adns_rr_inthostaddr *ap= datap_a, *bp= datap_b; + + if (ap->i < bp->i) return 0; + if (ap->i > bp->i) return 1; + return dip_hostaddr(ads, &ap->ha, &bp->ha); +} + +/* + * _inthostaddr (mf,cs) + */ + +static void mf_inthostaddr(adns_query qu, void *datap) { + adns_rr_inthostaddr *rrp= datap; + + mfp_hostaddr(qu,&rrp->ha); +} + +static adns_status cs_inthostaddr(vbuf *vb, const void *datap) { + const adns_rr_inthostaddr *rrp= datap; + char buf[10]; + + sprintf(buf,"%u ",rrp->i); + CSP_ADDSTR(buf); + + return csp_hostaddr(vb,&rrp->ha); +} + +/* + * _inthost (cs) + */ + +static adns_status cs_inthost(vbuf *vb, const void *datap) { + const adns_rr_intstr *rrp= datap; + char buf[10]; + + sprintf(buf,"%u ",rrp->i); + CSP_ADDSTR(buf); + return csp_domain(vb,rrp->str); +} + +/* + * _ptr (pa, +icb_ptr) + */ + +static void icb_ptr(adns_query parent, adns_query child) { + adns_answer *cans= child->answer; + const adns_rr_addr *queried, *found; + adns_state ads= parent->ads; + int i; + + if (cans->status == adns_s_nxdomain || cans->status == adns_s_nodata) { + adns__query_fail(parent,adns_s_inconsistent); + return; + } else if (cans->status) { + adns__query_fail(parent,cans->status); + return; + } + + queried= &parent->ctx.info.ptr_parent_addr; + for (i=0, found=cans->rrs.addr; i<cans->nrrs; i++, found++) { + if (queried->len == found->len && + !memcmp(&queried->addr,&found->addr,queried->len)) { + if (!parent->children.head) { + adns__query_done(parent); + return; + } else { + LIST_LINK_TAIL(ads->childw,parent); + return; + } + } + } + + adns__query_fail(parent,adns_s_inconsistent); +} + +static adns_status pa_ptr(const parseinfo *pai, int dmstart, int max, void *datap) { + static const char *(expectdomain[])= { DNS_INADDR_ARPA }; + + char **rrp= datap; + adns_status st; + adns_rr_addr *ap; + findlabel_state fls; + char *ep; + byte ipv[4]; + char labbuf[4]; + int cbyte, i, lablen, labstart, l, id; + adns_query nqu; + qcontext ctx; + + cbyte= dmstart; + st= pap_domain(pai, &cbyte, max, rrp, + pai->qu->flags & adns_qf_quoteok_anshost ? pdf_quoteok : 0); + if (st) return st; + if (cbyte != max) return adns_s_invaliddata; + + ap= &pai->qu->ctx.info.ptr_parent_addr; + if (!ap->len) { + adns__findlabel_start(&fls, pai->ads, -1, pai->qu, + pai->qu->query_dgram, pai->qu->query_dglen, + pai->qu->query_dglen, DNS_HDRSIZE, 0); + for (i=0; i<4; i++) { + st= adns__findlabel_next(&fls,&lablen,&labstart); assert(!st); + if (lablen<=0 || lablen>3) return adns_s_querydomainwrong; + memcpy(labbuf, pai->qu->query_dgram + labstart, lablen); labbuf[lablen]= 0; + ipv[3-i]= strtoul(labbuf,&ep,10); if (*ep) return adns_s_querydomainwrong; + if (lablen>1 && pai->qu->query_dgram[labstart]=='0') + return adns_s_querydomainwrong; + } + for (i=0; i<sizeof(expectdomain)/sizeof(*expectdomain); i++) { + st= adns__findlabel_next(&fls,&lablen,&labstart); assert(!st); + l= strlen(expectdomain[i]); + if (lablen != l || memcmp(pai->qu->query_dgram + labstart, expectdomain[i], l)) + return adns_s_querydomainwrong; + } + st= adns__findlabel_next(&fls,&lablen,0); assert(!st); + if (lablen) return adns_s_querydomainwrong; + + ap->len= sizeof(struct sockaddr_in); + memset(&ap->addr,0,sizeof(ap->addr.inet)); + ap->addr.inet.sin_family= AF_INET; + ap->addr.inet.sin_addr.s_addr= + htonl((ipv[0]<<24) | (ipv[1]<<16) | (ipv[2]<<8) | (ipv[3])); + } + + st= adns__mkquery_frdgram(pai->ads, &pai->qu->vb, &id, + pai->dgram, pai->dglen, dmstart, + adns_r_addr, adns_qf_quoteok_query); + if (st) return st; + + ctx.ext= 0; + ctx.callback= icb_ptr; + memset(&ctx.info,0,sizeof(ctx.info)); + st= adns__internal_submit(pai->ads, &nqu, adns__findtype(adns_r_addr), + &pai->qu->vb, id, + adns_qf_quoteok_query, pai->now, &ctx); + if (st) return st; + + nqu->parent= pai->qu; + LIST_LINK_TAIL_PART(pai->qu->children,nqu,siblings.); + return adns_s_ok; +} + +/* + * _strpair (mf) + */ + +static void mf_strpair(adns_query qu, void *datap) { + adns_rr_strpair *rrp= datap; + + adns__makefinal_str(qu,&rrp->array[0]); + adns__makefinal_str(qu,&rrp->array[1]); +} + +/* + * _intstrpair (mf) + */ + +static void mf_intstrpair(adns_query qu, void *datap) { + adns_rr_intstrpair *rrp= datap; + + adns__makefinal_str(qu,&rrp->array[0].str); + adns__makefinal_str(qu,&rrp->array[1].str); +} + +/* + * _hinfo (pa) + */ + +static adns_status pa_hinfo(const parseinfo *pai, int cbyte, int max, void *datap) { + adns_rr_intstrpair *rrp= datap; + adns_status st; + int i; + + for (i=0; i<2; i++) { + st= pap_qstring(pai, &cbyte, max, &rrp->array[i].i, &rrp->array[i].str); + if (st) return st; + } + + if (cbyte != max) return adns_s_invaliddata; + + return adns_s_ok; +} + +/* + * _mailbox (pap,cs) + */ + +static adns_status pap_mailbox822(const parseinfo *pai, int *cbyte_io, int max, + char **mb_r) { + int lablen, labstart, i, needquote, c, r, neednorm; + const unsigned char *p; + char *str; + findlabel_state fls; + adns_status st; + vbuf *vb; + + vb= &pai->qu->vb; + vb->used= 0; + adns__findlabel_start(&fls, pai->ads, + -1, pai->qu, + pai->dgram, pai->dglen, max, + *cbyte_io, cbyte_io); + st= adns__findlabel_next(&fls,&lablen,&labstart); + if (!lablen) { + adns__vbuf_appendstr(vb,"."); + goto x_ok; + } + + neednorm= 1; + for (i=0, needquote=0, p= pai->dgram+labstart; i<lablen; i++) { + c= *p++; + if ((c&~128) < 32 || (c&~128) == 127) return adns_s_invaliddata; + if (c == '.' && !neednorm) neednorm= 1; + else if (c==' ' || ctype_822special(c)) needquote++; + else neednorm= 0; + } + + if (needquote || neednorm) { + r= adns__vbuf_ensure(vb, lablen+needquote+4); if (!r) R_NOMEM; + adns__vbuf_appendq(vb,"\"",1); + for (i=0, needquote=0, p= pai->dgram+labstart; i<lablen; i++, p++) { + c= *p; + if (c == '"' || c=='\\') adns__vbuf_appendq(vb,"\\",1); + adns__vbuf_appendq(vb,p,1); + } + adns__vbuf_appendq(vb,"\"",1); + } else { + r= adns__vbuf_append(vb, pai->dgram+labstart, lablen); if (!r) R_NOMEM; + } + + r= adns__vbuf_appendstr(vb,"@"); if (!r) R_NOMEM; + + st= adns__parse_domain_more(&fls,pai->ads, pai->qu,vb,0, pai->dgram); + if (st) return st; + + x_ok: + str= adns__alloc_interim(pai->qu, vb->used+1); if (!str) R_NOMEM; + memcpy(str,vb->buf,vb->used); + str[vb->used]= 0; + *mb_r= str; + return adns_s_ok; +} + +static adns_status pap_mailbox(const parseinfo *pai, int *cbyte_io, int max, + char **mb_r) { + if (pai->qu->typei->type & adns__qtf_mail822) { + return pap_mailbox822(pai, cbyte_io, max, mb_r); + } else { + return pap_domain(pai, cbyte_io, max, mb_r, pdf_quoteok); + } +} + +static adns_status csp_mailbox(vbuf *vb, const char *mailbox) { + return csp_domain(vb,mailbox); +} + +/* + * _rp (pa,cs) + */ + +static adns_status pa_rp(const parseinfo *pai, int cbyte, int max, void *datap) { + adns_rr_strpair *rrp= datap; + adns_status st; + + st= pap_mailbox(pai, &cbyte, max, &rrp->array[0]); + if (st) return st; + + st= pap_domain(pai, &cbyte, max, &rrp->array[1], pdf_quoteok); + if (st) return st; + + if (cbyte != max) return adns_s_invaliddata; + return adns_s_ok; +} + +static adns_status cs_rp(vbuf *vb, const void *datap) { + const adns_rr_strpair *rrp= datap; + adns_status st; + + st= csp_mailbox(vb,rrp->array[0]); if (st) return st; + CSP_ADDSTR(" "); + st= csp_domain(vb,rrp->array[1]); if (st) return st; + + return adns_s_ok; +} + +/* + * _soa (pa,mf,cs) + */ + +static adns_status pa_soa(const parseinfo *pai, int cbyte, int max, void *datap) { + adns_rr_soa *rrp= datap; + const byte *dgram= pai->dgram; + adns_status st; + int msw, lsw, i; + + st= pap_domain(pai, &cbyte, max, &rrp->mname, + pai->qu->flags & adns_qf_quoteok_anshost ? pdf_quoteok : 0); + if (st) return st; + + st= pap_mailbox(pai, &cbyte, max, &rrp->rname); + if (st) return st; + + if (cbyte+20 != max) return adns_s_invaliddata; + + for (i=0; i<5; i++) { + GET_W(cbyte,msw); + GET_W(cbyte,lsw); + (&rrp->serial)[i]= (msw<<16) | lsw; + } + + return adns_s_ok; +} + +static void mf_soa(adns_query qu, void *datap) { + adns_rr_soa *rrp= datap; + + adns__makefinal_str(qu,&rrp->mname); + adns__makefinal_str(qu,&rrp->rname); +} + +static adns_status cs_soa(vbuf *vb, const void *datap) { + const adns_rr_soa *rrp= datap; + char buf[20]; + int i; + adns_status st; + + st= csp_domain(vb,rrp->mname); if (st) return st; + CSP_ADDSTR(" "); + st= csp_mailbox(vb,rrp->rname); if (st) return st; + + for (i=0; i<5; i++) { + sprintf(buf," %lu",(&rrp->serial)[i]); + CSP_ADDSTR(buf); + } + + return adns_s_ok; +} + +/* + * _flat (mf) + */ + +static void mf_flat(adns_query qu, void *data) { } + +/* + * Now the table. + */ + +#define TYPESZ_M(member) (sizeof(*((adns_answer*)0)->rrs.member)) + +#define DEEP_MEMB(memb) TYPESZ_M(memb), mf_##memb, cs_##memb +#define FLAT_MEMB(memb) TYPESZ_M(memb), mf_flat, cs_##memb + +#define DEEP_TYPE(code,rrt,fmt,memb,parser,comparer,printer) \ + { adns_r_##code, rrt, fmt, TYPESZ_M(memb), mf_##memb, printer, parser, comparer } +#define FLAT_TYPE(code,rrt,fmt,memb,parser,comparer,printer) \ + { adns_r_##code, rrt, fmt, TYPESZ_M(memb), mf_flat, printer, parser, comparer } + +static const typeinfo typeinfos[] = { +/* Must be in ascending order of rrtype ! */ +/* mem-mgmt code rrt fmt member parser comparer printer */ + +FLAT_TYPE(a, "A", 0, inaddr, pa_inaddr, di_inaddr, cs_inaddr ), +DEEP_TYPE(ns_raw, "NS", "raw", str, pa_host_raw,0, cs_domain ), +DEEP_TYPE(cname, "CNAME", 0, str, pa_dom_raw, 0, cs_domain ), +DEEP_TYPE(soa_raw,"SOA", "raw", soa, pa_soa, 0, cs_soa ), +DEEP_TYPE(ptr_raw,"PTR", "raw", str, pa_host_raw,0, cs_domain ), +DEEP_TYPE(hinfo, "HINFO", 0, intstrpair, pa_hinfo, 0, cs_hinfo ), +DEEP_TYPE(mx_raw, "MX", "raw", intstr, pa_mx_raw, di_mx_raw, cs_inthost ), +DEEP_TYPE(txt, "TXT", 0, manyistr, pa_txt, 0, cs_txt ), +DEEP_TYPE(rp_raw, "RP", "raw", strpair, pa_rp, 0, cs_rp ), + +FLAT_TYPE(addr, "A", "addr", addr, pa_addr, di_addr, cs_addr ), +DEEP_TYPE(ns, "NS", "+addr", hostaddr, pa_hostaddr,di_hostaddr,cs_hostaddr ), +DEEP_TYPE(ptr, "PTR","checked",str, pa_ptr, 0, cs_domain ), +DEEP_TYPE(mx, "MX", "+addr", inthostaddr,pa_mx, di_mx, cs_inthostaddr), + +DEEP_TYPE(soa, "SOA","822", soa, pa_soa, 0, cs_soa ), +DEEP_TYPE(rp, "RP", "822", strpair, pa_rp, 0, cs_rp ), +}; + +const typeinfo *adns__findtype(adns_rrtype type) { + const typeinfo *begin, *end, *mid; + + begin= typeinfos; end= typeinfos+(sizeof(typeinfos)/sizeof(typeinfo)); + + while (begin < end) { + mid= begin + ((end-begin)>>1); + if (mid->type == type) return mid; + if (type > mid->type) begin= mid+1; + else end= mid; + } + return 0; +} |