X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=adns.git;a=blobdiff_plain;f=src%2Finternal.h;h=29f4dd23e7735c077ee8f02323f4e5ab8f95bf3f;hp=12ecd6f59be44a0f11d34d42d8f658f9ad74b748;hb=7f5937118639156f1fb201c42298c00e1f54c3cc;hpb=28de64428cfdb63ad07acfcfc254907b608d5137 diff --git a/src/internal.h b/src/internal.h index 12ecd6f..29f4dd2 100644 --- a/src/internal.h +++ b/src/internal.h @@ -5,21 +5,25 @@ * - comments regarding library data structures */ /* - * This file is part of adns, which is Copyright (C) 1997-1999 Ian Jackson - * + * This file is part of adns, which is + * Copyright (C) 1997-2000,2003,2006 Ian Jackson + * Copyright (C) 1999-2000,2003,2006 Tony Finch + * Copyright (C) 1991 Massachusetts Institute of Technology + * (See the file INSTALL for full details.) + * * 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. + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef ADNS_INTERNAL_H_INCLUDED @@ -34,10 +38,16 @@ typedef unsigned char byte; #include #include #include +#include #include #include "adns.h" +#include "dlist.h" + +#ifdef ADNS_REGRESS_TEST +# include "hredirect.h" +#endif /* Configuration and constants */ @@ -45,11 +55,14 @@ typedef unsigned char byte; #define MAXSORTLIST 15 #define UDPMAXRETRIES 15 #define UDPRETRYMS 2000 -#define TCPMS 30000 +#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_MAXLABEL 63 #define DNS_MAXDOMAIN 255 #define DNS_HDRSIZE 12 #define DNS_IDOFFSET 0 @@ -59,6 +72,27 @@ typedef unsigned char byte; #define MAX_POLLFDS ADNS_POLLFDS_RECOMMENDED +/* Some preprocessor hackery */ + +#define GLUE(x, y) GLUE_(x, y) +#define GLUE_(x, y) x##y + +/* C99 macro `...' must match at least one argument, so the naive definition + * `#define CAR(car, ...) car' won't work. But it's easy to arrange for the + * tail to be nonempty if we're just going to discard it anyway. */ +#define CAR(...) CAR_(__VA_ARGS__, _) +#define CAR_(car, ...) car + +/* Extracting the tail of an argument list is rather more difficult. The + * following trick is based on one by Laurent Deniau to count the number of + * arguments to a macro, simplified in two ways: (a) it only handles up to + * eight arguments, and (b) it only needs to distinguish the one-argument + * case from many arguments. */ +#define CDR(...) CDR_(__VA_ARGS__, m, m, m, m, m, m, m, 1, _)(__VA_ARGS__) +#define CDR_(_1, _2, _3, _4, _5, _6, _7, _8, n, ...) CDR_##n +#define CDR_1(_) +#define CDR_m(_, ...) __VA_ARGS__ + typedef enum { cc_user, cc_entex, @@ -76,15 +110,6 @@ typedef enum { /* 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; @@ -99,8 +124,28 @@ typedef struct { struct timeval now; } parseinfo; +union checklabel_state { + struct { byte ipv[4]; } ptr; +}; + typedef struct { - adns_rrtype type; + void *ext; + void (*callback)(adns_query parent, adns_query child); + + union { + struct { + struct in_addr addr; + } ptr; + } tinfo; /* type-specific state for the query itself: zero-init if you + * don't know better. */ + + union { + adns_rr_hostaddr *hostaddr; + } pinfo; /* state for use by parent's callback function */ +} qcontext; + +typedef struct typeinfo { + adns_rrtype typekey; const char *rrtname; const char *fmtname; int rrsz; @@ -116,7 +161,8 @@ typedef struct { * and will not be null-terminated by convstring. */ - adns_status (*parse)(const parseinfo *pai, int cbyte, int max, void *store_r); + 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. * @@ -128,12 +174,39 @@ typedef struct { * nsstart is the offset of the authority section. */ - int (*diff_needswap)(adns_state ads, const void *datap_a, const void *datap_b); + 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. */ + + adns_status (*checklabel)(adns_state ads, adns_queryflags flags, + union checklabel_state *cls, qcontext *ctx, + int labnum, const char *label, int lablen); + /* Check a label from the query domain string. The label is not + * necessarily null-terminated. The hook can refuse the query's submission + * by returning a nonzero status. State can be stored in *cls between + * calls, and useful information can be stashed in ctx->tinfo, to be stored + * with the query (e.g., it will be available to the parse hook). This + * hook can detect a first call because labnum is zero, and a final call + * because lablen is zero. + */ + + void (*postsort)(adns_state ads, void *array, int nrrs, + const struct typeinfo *typei); + /* Called immediately after the RRs have been sorted, and may rearrange + * them. (This is really for the benefit of SRV's bizarre weighting + * stuff.) May be 0 to mean nothing needs to be done. + */ } typeinfo; +adns_status adns__ckl_hostname(adns_state ads, adns_queryflags flags, + union checklabel_state *cls, + qcontext *ctx, int labnum, + const char *label, int lablen); + /* implemented in query.c, used by types.c as default + * and as part of implementation for some fancier types + * doesn't require any state */ + typedef struct allocnode { struct allocnode *next, *back; } allocnode; @@ -147,18 +220,9 @@ union maxalign { 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_tcpwait, query_tcpsent, query_child, query_done } state; + 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; @@ -169,7 +233,7 @@ struct adns__query { 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 @@ -186,7 +250,7 @@ struct adns__query { * 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 . */ @@ -201,10 +265,10 @@ struct adns__query { * 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 id, flags, retries; int udpnextserver; - unsigned long udpsent, tcpfailed; /* bitmap indexed by server */ + unsigned long udpsent; /* bitmap indexed by server */ struct timeval timeout; time_t expires; /* Earliest expiry time of any record we used. */ @@ -213,43 +277,41 @@ struct adns__query { /* Possible states: * * state Queue child id nextudpserver udpsent tcpfailed - * + * * tosend NONE null >=0 0 zero zero - * tosend timew null >=0 any nonzero zero + * tosend udpw null >=0 any nonzero zero * tosend NONE null >=0 any nonzero zero - * - * tcpwait timew null >=0 irrelevant any any - * tcpsent timew null >=0 irrelevant any any - * + * + * 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 -----> | udp/NONE | + * START -----> | tosend/NONE | * +------------------------+ * / |\ \ * too big for UDP / UDP timeout \ \ send via UDP - * do this ASAP! / more retries \ \ do this ASAP! - * |_ desired \ _| - * +---------------+ +-----------+ - * | tcpwait/timew | ____ | udp/timew | - * +---------------+ \ +-----------+ - * | ^ | | | - * TCP conn'd; | | TCP died | | | - * send via TCP | | more | UDP timeout | | - * do this ASAP! | | servers | no more | | - * v | to try | retries | | - * +---------------+ | desired | | - * | tcpsent/timew | ____ | | | - * +---------------+ \| | | - * \ \ TCP died | TCP | | - * \ \ no more | timeout / | - * \ \ servers | / | - * \ \ to try | / | - * got \ \ v |_ / got + * 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 | / * \ +------------------+ / @@ -260,23 +322,34 @@ struct adns__query { * need child query/ies / \ no child query * / \ * |_ _| - * +--------------+ +----------------+ - * | child/childw | ----------------> | done/output OK | - * +--------------+ children done +----------------+ + * +---------------+ +----------------+ + * | childw/childw | ----------------> | done/output OK | + * +---------------+ children done +----------------+ */ }; +struct query_queue { adns_query head, tail; }; + struct adns__state { adns_initflags iflags; - FILE *diagfile; + adns_logcallbackfn *logfn; + void *logfndata; int configerrno; - struct { adns_query head, tail; } timew, childw, output; + struct query_queue udpw, tcpw, childw, output; adns_query forallnext; int nextid, udpsocket, tcpsocket; vbuf tcpsend, tcprecv; - int nservers, nsortlist, nsearchlist, searchndots, tcpserver; - enum adns__tcpstate { server_disconnected, server_connecting, server_ok } tcpstate; + 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), in which case it is the + * absolute time when we will close the connection. + */ struct sigaction stdsigpipe; sigset_t stdsigmask; struct pollfd pollfds_buf[MAX_POLLFDS]; @@ -287,15 +360,19 @@ struct adns__state { struct in_addr base, mask; } sortlist[MAXSORTLIST]; char **searchlist; + unsigned short rand48xsubi[3]; }; /* 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: */ +void adns__vlprintf(adns_state ads, const char *fmt, va_list al); +void adns__lprintf(adns_state ads, const char *fmt, + ...) PRINTFFORMAT(2,3); + void adns__vdiag(adns_state ads, const char *pfx, adns_initflags prevent, int serv, adns_query qu, const char *fmt, va_list al); @@ -307,7 +384,7 @@ 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_appendstr(vbuf *vb, const char *data); /* doesn't 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); @@ -315,7 +392,8 @@ 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); + 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. @@ -326,7 +404,7 @@ const char *adns__diag_domain(adns_state ads, int serv, adns_query qu, * 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); @@ -341,41 +419,38 @@ 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. + * 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); + const typeinfo *typei, adns_rrtype type, + 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, + 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__query_tcp(adns_query qu, struct timeval now); -/* Query must be in state tcpwait/timew; it will be moved to a new state - * if possible and no further processing can be done on it for now. - * (Resulting state is one of tcpwait/timew (if server not connected), - * tcpsent/timew, child/childw or done/output.) - * - * adns__tcp_tryconnect should already have been called - _tcp - * will only use an existing connection (if there is one), which it - * may break. If the conn list lost then the caller is responsible for any - * reestablishment and retry. +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.) + * (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. @@ -384,9 +459,10 @@ void adns__query_send(adns_query qu, struct timeval now); /* From query.c: */ adns_status adns__internal_submit(adns_state ads, adns_query *query_r, - const typeinfo *typei, vbuf *qumsg_vb, int id, + const typeinfo *typei, adns_rrtype type, + vbuf *qumsg_vb, int id, adns_queryflags flags, struct timeval now, - const qcontext *ctx); + 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. @@ -395,7 +471,8 @@ adns_status 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). * - * *ctx is copied byte-for-byte into the query. + * *ctx is copied byte-for-byte into the query. Before doing this, its tinfo + * field may be modified by type hooks. * * When the child query is done, ctx->callback will be called. The * child will already have been taken off both the global list of @@ -445,7 +522,8 @@ void *adns__alloc_preserved(adns_query qu, size_t sz); * answer->cname and answer->owner are _preserved. */ -void adns__transfer_interim(adns_query from, adns_query to, void *block, size_t sz); +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 @@ -482,11 +560,19 @@ void adns__reset_preserved(adns_query qu); 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: */ @@ -514,7 +600,8 @@ void adns__findlabel_start(findlabel_state *fls, adns_state ads, * 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); +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 @@ -545,7 +632,8 @@ typedef enum { 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); + 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. @@ -555,7 +643,8 @@ adns_status adns__parse_domain(adns_state ads, int serv, adns_query qu, */ adns_status adns__parse_domain_more(findlabel_state *fls, adns_state ads, - adns_query qu, vbuf *vb, parsedomain_flags flags, + 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 @@ -594,10 +683,11 @@ 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, unsigned long *ttl_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); + 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. * @@ -612,7 +702,8 @@ adns_status adns__findrr_anychk(adns_query qu, int serv, * untruncated. */ -void adns__update_expires(adns_query qu, unsigned long ttl, struct timeval now); +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. */ @@ -622,23 +713,36 @@ 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); +/* 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. */ +/* 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); + +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: */ @@ -646,61 +750,36 @@ void adns__consistency(adns_state ads, adns_query qu, consistency_checks cc); /* Useful static inline functions: */ -static inline void timevaladd(struct timeval *tv_io, long ms) { - struct timeval tmp; - assert(ms>=0); - tmp= *tv_io; - tmp.tv_usec += (ms%1000)*1000000; - tmp.tv_sec += ms/1000; - if (tmp.tv_usec >= 1000000) { tmp.tv_sec++; tmp.tv_usec -= 1000; } - *tv_io= tmp; +static inline int ctype_whitespace(int c) { + return c==' ' || c=='\n' || c=='\t'; } - -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 ctype_822special(int c) { + return strchr("()<>@,;:\\\".[]",c) != 0; +} +static inline int ctype_domainunquoted(int c) { + return ctype_alpha(c) || ctype_digit(c) || (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) ) \ +#define MEM_ROUND(sz) \ + (( ((sz)+sizeof(union maxalign)-1) / sizeof(union maxalign) ) \ * sizeof(union maxalign) ) -#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,) - #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), \ +#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