X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=adns.git;a=blobdiff_plain;f=src%2Finternal.h;h=5fdea898f3ec6a531aa938a41f5d28a860cf55a8;hp=92debfd4cb9dc028225b61ee33d12e5d51d67eae;hb=aedf10f2d0bb23797a821e9c5eec34aba862db53;hpb=09957b1ca58af52bbc90e88ec198f14b8a06c0f7 diff --git a/src/internal.h b/src/internal.h index 92debfd..5fdea89 100644 --- a/src/internal.h +++ b/src/internal.h @@ -5,7 +5,12 @@ * - comments regarding library data structures */ /* - * This file is part of adns, which is Copyright (C) 1997, 1998 Ian Jackson + * This file is + * Copyright (C) 1997-1999 Ian Jackson + * + * It is part of adns, which is + * Copyright (C) 1997-1999 Ian Jackson + * Copyright (C) 1999 Tony Finch * * 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 @@ -25,34 +30,47 @@ #ifndef ADNS_INTERNAL_H_INCLUDED #define ADNS_INTERNAL_H_INCLUDED -#define PRINTFFORMAT(a,b) __attribute__((format(printf,a,b))) +#include "config.h" typedef unsigned char byte; #include #include #include +#include +#include +#include #include #include "adns.h" +#include "dlist.h" /* Configuration and constants */ #define MAXSERVERS 5 #define MAXSORTLIST 15 -#define UDPMAXRETRIES /*15 fixme*/5 +#define UDPMAXRETRIES 15 #define UDPRETRYMS 2000 #define TCPMS 30000 -#define LOCALRESOURCEMS 20 +#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, @@ -146,14 +164,14 @@ typedef struct { struct adns__query { adns_state ads; - enum { query_udp, query_tcpwait, query_tcpsent, query_child, query_done } state; + enum { query_tosend, query_tcpwait, query_tcpsent, query_child, 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; + int interim_allocd, preserved_allocd; void *final_allocspace; - + const typeinfo *typei; byte *query_dgram; int query_dglen; @@ -171,33 +189,50 @@ struct adns__query { * 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, udpretries; int udpnextserver; unsigned long udpsent, tcpfailed; /* 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 sentudp failedtcp + * state Queue child id nextudpserver udpsent tcpfailed * - * udp NONE null >=0 0 zero zero - * udp timew null >=0 any nonzero zero - * udp NONE null >=0 any nonzero zero + * tosend NONE null >=0 0 zero zero + * tosend timew null >=0 any nonzero zero + * tosend NONE null >=0 any nonzero zero * - * tcpwait timew null >=0 irrelevant zero any - * tcpsent timew null >=0 irrelevant zero any + * tcpwait timew null >=0 irrelevant any any + * tcpsent timew 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. + * * +------------------------+ * START -----> | udp/NONE | * +------------------------+ @@ -242,22 +277,28 @@ struct adns__state { FILE *diagfile; int configerrno; struct { adns_query head, tail; } timew, childw, output; + adns_query forallnext; int nextid, udpsocket, tcpsocket; vbuf tcpsend, tcprecv; - int nservers, nsortlist, tcpserver; + int nservers, nsortlist, nsearchlist, searchndots, tcpserver, tcprecv_skip; enum adns__tcpstate { server_disconnected, server_connecting, server_ok } tcpstate; struct timeval tcptimeout; + 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 */ +void adns__checkqueues(adns_state ads); /* expensive walk, for checking */ /* From general.c: */ @@ -300,13 +341,22 @@ void adns__isort(void *array, int nobjs, int sz, void *tempbuf, * 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. + */ + /* 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, and returns id at *id_r. */ +/* 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, @@ -327,19 +377,22 @@ void adns__query_tcp(adns_query qu, struct timeval now); * reestablishment and retry. */ -void adns__query_udp(adns_query qu, struct timeval now); -/* Query must be in state udp/NONE; it will be moved to a new state, +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: */ -int adns__internal_submit(adns_state ads, adns_query *query_r, - const typeinfo *typei, vbuf *qumsg_vb, int id, - adns_queryflags flags, struct timeval now, - adns_status failstat, const qcontext *ctx); +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. @@ -348,27 +401,54 @@ int adns__internal_submit(adns_state ads, adns_query *query_r, * 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). * - * If failstat is nonzero then if we are successful in creating the query - * it is immediately failed with code failstat (but _submit still succeds). - * * *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 its memory 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. + * 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. * - * adns__alloc_interim_{only,fail}(qu,0) will not return 0, - * but it will not necessarily return a distinct pointer each time. + * 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); @@ -379,6 +459,10 @@ void adns__transfer_interim(adns_query from, adns_query to, void *block, size_t * * 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); @@ -394,12 +478,12 @@ void *adns__alloc_final(adns_query qu, size_t sz); void adns__makefinal_block(adns_query qu, void **blpp, size_t sz); void adns__makefinal_str(adns_query qu, char **strp); -void adns__reset_cnameonly(adns_query qu); -/* Resets all of the memory management stuff etc. to - * take account of only the CNAME. Used when we find an error somewhere - * and want to just report the error (with perhaps CNAME info), and also - * when we're halfway through RRs in a datagram and discover that we - * need to retry the query. +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); @@ -408,7 +492,7 @@ 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, struct timeval now); + int serv, int viatcp, struct timeval now); /* From types.c: */ @@ -476,18 +560,28 @@ adns_status adns__parse_domain(adns_state ads, int serv, adns_query qu, * 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, int *rdlen_r, int *rdstart_r, + 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 and RRdata length and start are returned iff - * the corresponding pointer variables are not null. type_r and - * class_r may not be null. + * 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 @@ -506,7 +600,8 @@ adns_status adns__findrr(adns_query qu, int serv, adns_status adns__findrr_anychk(adns_query qu, int serv, const byte *dgram, int dglen, int *cbyte_io, - int *type_r, int *class_r, int *rdlen_r, int *rdstart_r, + 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 @@ -523,15 +618,44 @@ adns_status adns__findrr_anychk(adns_query qu, int serv, * 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); +void adns__tcp_closenext(adns_state ads); 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. */ +/* 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); +void adns__timeouts(adns_state ads, int act, + struct timeval **tv_io, struct timeval *tvbuf, + struct timeval now); +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); + +/* From check.c: */ + +void adns__consistency(adns_state ads, adns_query qu, consistency_checks cc); /* Useful static inline functions: */ @@ -548,8 +672,11 @@ static inline void timevaladd(struct timeval *tv_io, long ms) { 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'); + 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 */ @@ -557,29 +684,14 @@ static inline int ctype_alpha(int c) { (( ((sz)+sizeof(union maxalign)-1) / sizeof(union maxalign) ) \ * sizeof(union maxalign) ) -#define LIST_INIT(list) ((list).head= (list).tail= 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,) - #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