diff options
Diffstat (limited to 'ircd/res.c')
-rw-r--r-- | ircd/res.c | 1697 |
1 files changed, 1697 insertions, 0 deletions
diff --git a/ircd/res.c b/ircd/res.c new file mode 100644 index 0000000..1be09c7 --- /dev/null +++ b/ircd/res.c @@ -0,0 +1,1697 @@ +/************************************************************************ + * IRC - Internet Relay Chat, ircd/res.c + * Copyright (C) 1992 Darren Reed + * + * 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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "os.h" +#include "s_defines.h" +#define RES_C +#include "s_externs.h" +#undef RES_C + +#ifndef lint +static char rcsid[] = "@(#)$Id: res.c,v 1.21 1999/07/02 17:31:17 kalt Exp $"; +#endif + +/* #undef DEBUG /* because there is a lot of debug code in here :-) */ + +static char hostbuf[HOSTLEN+1+100]; /* +100 for INET6 */ +static char dot[] = "."; +static int incache = 0; +static CacheTable hashtable[ARES_CACSIZE]; +static aCache *cachetop = NULL; +static ResRQ *last, *first; + +static void rem_cache __P((aCache *)); +static void rem_request __P((ResRQ *)); +static int do_query_name __P((Link *, char *, ResRQ *)); +static int do_query_number __P((Link *, struct IN_ADDR *, ResRQ *)); +static void resend_query __P((ResRQ *)); +static int proc_answer __P((ResRQ *, HEADER *, char *, char *)); +static int query_name __P((char *, int, int, ResRQ *)); +static aCache *make_cache __P((ResRQ *)), *rem_list __P((aCache *)); +static aCache *find_cache_name __P((char *)); +static aCache *find_cache_number __P((ResRQ *, char *)); +static int add_request __P((ResRQ *)); +static ResRQ *make_request __P((Link *)); +static int send_res_msg __P((char *, int, int)); +static ResRQ *find_id __P((int)); +static int hash_number __P((unsigned char *)); +static void update_list __P((ResRQ *, aCache *)); +static int hash_name __P((char *)); +static int bad_hostname __P((char *, int)); + +static struct cacheinfo { + int ca_adds; + int ca_dels; + int ca_expires; + int ca_lookups; + int ca_na_hits; + int ca_nu_hits; + int ca_updates; +} cainfo; + +static struct resinfo { + int re_errors; + int re_nu_look; + int re_na_look; + int re_replies; + int re_requests; + int re_resends; + int re_sent; + int re_timeouts; + int re_shortttl; + int re_unkrep; +} reinfo; + +int init_resolver(op) +int op; +{ + int ret = 0; + +#ifdef LRAND48 + srand48(time(NULL)); +#endif + if (op & RES_INITLIST) + { + bzero((char *)&reinfo, sizeof(reinfo)); + first = last = NULL; + } + if (op & RES_CALLINIT) + { + ret = ircd_res_init(); + if (!ircd_res.nscount) + { + ircd_res.nscount = 1; +#ifdef INET6 + /* still IPv4 */ + ircd_res.nsaddr_list[0].sin_addr.s_addr = + inet_pton(AF_INET, "127.0.0.1", + &ircd_res.nsaddr_list[0].sin_addr.s_addr); +#else + ircd_res.nsaddr_list[0].sin_addr.s_addr = + inetaddr("127.0.0.1"); +#endif + } + } + + if (op & RES_INITSOCK) + { + int on = 0; + +#ifdef INET6 + /* still IPv4 */ + ret = resfd = socket(AF_INET, SOCK_DGRAM, 0); +#else + ret = resfd = socket(AF_INET, SOCK_DGRAM, 0); +#endif + (void) SETSOCKOPT(ret, SOL_SOCKET, SO_BROADCAST, &on, on); + } +#ifdef DEBUG + if (op & RES_INITDEBG); + ircd_res.options |= RES_DEBUG; +#endif + if (op & RES_INITCACH) + { + bzero((char *)&cainfo, sizeof(cainfo)); + bzero((char *)hashtable, sizeof(hashtable)); + } + if (op == 0) + ret = resfd; + return ret; +} + +static int add_request(new) +ResRQ *new; +{ + if (!new) + return -1; + if (!first) + first = last = new; + else + { + last->next = new; + last = new; + } + new->next = NULL; + reinfo.re_requests++; + return 0; +} + +/* + * remove a request from the list. This must also free any memory that has + * been allocated for temporary storage of DNS results. + */ +static void rem_request(old) +ResRQ *old; +{ + Reg ResRQ **rptr, *r2ptr = NULL; + Reg int i; + Reg char *s; + + if (!old) + return; + for (rptr = &first; *rptr; r2ptr = *rptr, rptr = &(*rptr)->next) + if (*rptr == old) + { + *rptr = old->next; + if (last == old) + last = r2ptr; + break; + } +#ifdef DEBUG + Debug((DEBUG_INFO,"rem_request:Remove %#x at %#x %#x", + old, *rptr, r2ptr)); +#endif + r2ptr = old; + if (r2ptr->he.h_name) + MyFree((char *)r2ptr->he.h_name); + for (i = 0; i < MAXALIASES; i++) + if ((s = r2ptr->he.h_aliases[i])) + MyFree(s); + if (r2ptr->name) + MyFree(r2ptr->name); + MyFree((char *)r2ptr); + + return; +} + +/* + * Create a DNS request record for the server. + */ +static ResRQ *make_request(lp) +Link *lp; +{ + Reg ResRQ *nreq; + + nreq = (ResRQ *)MyMalloc(sizeof(ResRQ)); + bzero((char *)nreq, sizeof(ResRQ)); + nreq->next = NULL; /* where NULL is non-zero ;) */ + nreq->sentat = timeofday; + nreq->retries = 3; + nreq->resend = 1; + nreq->srch = -1; + if (lp) + bcopy((char *)lp, (char *)&nreq->cinfo, sizeof(Link)); + else + bzero((char *)&nreq->cinfo, sizeof(Link)); + nreq->timeout = 4; /* start at 4 and exponential inc. */ + nreq->he.h_addrtype = AFINET; + nreq->he.h_name = NULL; + nreq->he.h_aliases[0] = NULL; + (void)add_request(nreq); + return nreq; +} + +/* + * Remove queries from the list which have been there too long without + * being resolved. + */ +time_t timeout_query_list(now) +time_t now; +{ + Reg ResRQ *rptr, *r2ptr; + Reg time_t next = 0, tout; + aClient *cptr; + + Debug((DEBUG_DNS,"timeout_query_list at %s",myctime(now))); + for (rptr = first; rptr; rptr = r2ptr) + { + r2ptr = rptr->next; + tout = rptr->sentat + rptr->timeout; + if (now >= tout) + if (--rptr->retries <= 0) + { +#ifdef DEBUG + Debug((DEBUG_ERROR,"timeout %x now %d cptr %x", + rptr, now, rptr->cinfo.value.cptr)); +#endif + reinfo.re_timeouts++; + cptr = rptr->cinfo.value.cptr; + switch (rptr->cinfo.flags) + { + case ASYNC_CLIENT : +#if defined(USE_IAUTH) + sendto_iauth("%d d", cptr->fd); +#endif + ClearDNS(cptr); + break; + case ASYNC_CONNECT : + sendto_flag(SCH_ERROR, + "Host %s unknown", + rptr->name); + break; + } + rem_request(rptr); + continue; + } + else + { + rptr->sentat = now; + rptr->timeout += rptr->timeout; + resend_query(rptr); + tout = now + rptr->timeout; +#ifdef DEBUG + Debug((DEBUG_INFO,"r %x now %d retry %d c %x", + rptr, now, rptr->retries, + rptr->cinfo.value.cptr)); +#endif + } + if (!next || tout < next) + next = tout; + } + return (next > now) ? next : (now + AR_TTL); +} + +/* + * del_queries - called by the server to cleanup outstanding queries for + * which there no longer exist clients or conf lines. + */ +void del_queries(cp) +char *cp; +{ + Reg ResRQ *rptr, *r2ptr; + + for (rptr = first; rptr; rptr = r2ptr) + { + r2ptr = rptr->next; + if (cp == rptr->cinfo.value.cp) + rem_request(rptr); + } +} + +/* + * sends msg to all nameservers found in the "ircd_res" structure. + * This should reflect /etc/resolv.conf. We will get responses + * which arent needed but is easier than checking to see if nameserver + * isnt present. Returns number of messages successfully sent to + * nameservers or -1 if no successful sends. + */ +static int send_res_msg(msg, len, rcount) +char *msg; +int len, rcount; +{ + Reg int i; + int sent = 0, max; + + if (!msg) + return -1; + + max = MIN(ircd_res.nscount, rcount); + if (ircd_res.options & RES_PRIMARY) + max = 1; + if (!max) + max = 1; + + for (i = 0; i < max; i++) + { +#ifdef INET6 + /* still IPv4 */ + ircd_res.nsaddr_list[i].sin_family = AF_INET; +#else + ircd_res.nsaddr_list[i].sin_family = AF_INET; +#endif +#ifdef INET6 + if (sendto(resfd, msg, len, 0, + (struct sockaddr *)&(ircd_res.nsaddr_list[i]), + sizeof(struct sockaddr)) == len) +#else + if (sendto(resfd, msg, len, 0, + (struct sockaddr *)&(ircd_res.nsaddr_list[i]), + sizeof(struct sockaddr)) == len) +#endif + + { + reinfo.re_sent++; + sent++; + } + else + Debug((DEBUG_ERROR,"s_r_m:sendto: %d on %d", + errno, resfd)); + } + + return (sent) ? sent : -1; +} + + +/* + * find a dns request id (id is determined by dn_mkquery) + */ +static ResRQ *find_id(id) +int id; +{ + Reg ResRQ *rptr; + + for (rptr = first; rptr; rptr = rptr->next) + if (rptr->id == id) + return rptr; + return NULL; +} + +struct hostent *gethost_byname(name, lp) +char *name; +Link *lp; +{ + Reg aCache *cp; + + reinfo.re_na_look++; + if ((cp = find_cache_name(name))) + return (struct hostent *)&(cp->he); + if (!lp) + return NULL; + (void)do_query_name(lp, name, NULL); + return NULL; +} + +struct hostent *gethost_byaddr(addr, lp) +char *addr; +Link *lp; +{ + aCache *cp; + + reinfo.re_nu_look++; + if ((cp = find_cache_number(NULL, addr))) + return (struct hostent *)&(cp->he); + if (!lp) + return NULL; + (void)do_query_number(lp, (struct IN_ADDR *)addr, NULL); + return NULL; +} + +static int do_query_name(lp, name, rptr) +Link *lp; +char *name; +Reg ResRQ *rptr; +{ + char hname[HOSTLEN+1]; + int len; + + strncpyzt(hname, name, sizeof(hname)); + len = strlen(hname); + + if (rptr && !index(hname, '.') && ircd_res.options & RES_DEFNAMES) + { + (void)strncat(hname, dot, sizeof(hname) - len - 1); + len++; + (void)strncat(hname, ircd_res.defdname, sizeof(hname) - len -1); + } + + /* + * Store the name passed as the one to lookup and generate other host + * names to pass onto the nameserver(s) for lookups. + */ + if (!rptr) + { + rptr = make_request(lp); +#ifdef INET6 + rptr->type = T_AAAA; +#else + rptr->type = T_A; +#endif + rptr->name = (char *)MyMalloc(strlen(name) + 1); + (void)strcpy(rptr->name, name); + } + Debug((DEBUG_DNS,"do_query_name(): %s ", hname)); +#ifdef INET6 + return (query_name(hname, C_IN, T_AAAA, rptr)); +#else + return (query_name(hname, C_IN, T_A, rptr)); +#endif +} + +/* + * Use this to do reverse IP# lookups. + */ +static int do_query_number(lp, numb, rptr) +Link *lp; +struct IN_ADDR *numb; +Reg ResRQ *rptr; +{ + char ipbuf[128]; + Reg u_char *cp; + +#ifdef INET6 + cp = (u_char *)numb->s6_addr; + if (cp[0]==0 && cp[1]==0 && cp[2]==0 && cp[3]==0 && cp[4]==0 && + cp[5]==0 && cp[6]==0 && cp[7]==0 && cp[8]==0 && cp[9]==0 && + ((cp[10]==0 && cp[11]==0) || (cp[10]==0xff && cp[11]==0xff))) + { + (void)sprintf(ipbuf, "%u.%u.%u.%u.in-addr.arpa.", + (u_int)(cp[15]), (u_int)(cp[14]), + (u_int)(cp[13]), (u_int)(cp[12])); + } + else + { + (void)sprintf(ipbuf, "%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.ip6.int.", + (u_int)(cp[15]&0xf), (u_int)(cp[15]>>4), + (u_int)(cp[14]&0xf), (u_int)(cp[14]>>4), + (u_int)(cp[13]&0xf), (u_int)(cp[13]>>4), + (u_int)(cp[12]&0xf), (u_int)(cp[12]>>4), + (u_int)(cp[11]&0xf), (u_int)(cp[11]>>4), + (u_int)(cp[10]&0xf), (u_int)(cp[10]>>4), + (u_int)(cp[9]&0xf), (u_int)(cp[9]>>4), + (u_int)(cp[8]&0xf), (u_int)(cp[8]>>4), + (u_int)(cp[7]&0xf), (u_int)(cp[7]>>4), + (u_int)(cp[6]&0xf), (u_int)(cp[6]>>4), + (u_int)(cp[5]&0xf), (u_int)(cp[5]>>4), + (u_int)(cp[4]&0xf), (u_int)(cp[4]>>4), + (u_int)(cp[3]&0xf), (u_int)(cp[3]>>4), + (u_int)(cp[2]&0xf), (u_int)(cp[2]>>4), + (u_int)(cp[1]&0xf), (u_int)(cp[1]>>4), + (u_int)(cp[0]&0xf), (u_int)(cp[0]>>4)); + } +#else + cp = (u_char *)&numb->s_addr; + (void)sprintf(ipbuf, "%u.%u.%u.%u.in-addr.arpa.", + (u_int)(cp[3]), (u_int)(cp[2]), + (u_int)(cp[1]), (u_int)(cp[0])); +#endif + + if (!rptr) + { + rptr = make_request(lp); + rptr->type = T_PTR; +#ifdef INET6 + bcopy(numb->s6_addr, rptr->addr.s6_addr, IN6ADDRSZ); + bcopy((char *)numb->s6_addr, + (char *)&rptr->he.h_addr, sizeof(struct in6_addr)); +#else + rptr->addr.s_addr = numb->s_addr; + bcopy((char *)&numb->s_addr, + (char *)&rptr->he.h_addr, sizeof(struct in_addr)); +#endif + rptr->he.h_length = sizeof(struct IN_ADDR); + } + return (query_name(ipbuf, C_IN, T_PTR, rptr)); +} + +/* + * generate a query based on class, type and name. + */ +static int query_name(name, class, type, rptr) +char *name; +int class, type; +ResRQ *rptr; +{ + struct timeval tv; + char buf[MAXPACKET]; + int r,s,k = 0; + HEADER *hptr; + + bzero(buf, sizeof(buf)); + r = ircd_res_mkquery(QUERY, name, class, type, NULL, 0, NULL, + (u_char *)buf, sizeof(buf)); + if (r <= 0) + { + h_errno = NO_RECOVERY; + return r; + } + hptr = (HEADER *)buf; +#ifdef LRAND48 + do { + hptr->id = htons(ntohs(hptr->id) + k + lrand48() & 0xffff); +#else + (void) gettimeofday(&tv, NULL); + do { + /* htons/ntohs can be assembler macros, which cannot + be nested. Thus two lines. -Vesa */ + u_short nstmp = ntohs(hptr->id) + k + + (u_short)(tv.tv_usec & 0xffff); + hptr->id = htons(nstmp); +#endif /* LRAND48 */ + k++; + } while (find_id(ntohs(hptr->id))); + rptr->id = ntohs(hptr->id); + rptr->sends++; + s = send_res_msg(buf, r, rptr->sends); + if (s == -1) + { + h_errno = TRY_AGAIN; + return -1; + } + else + rptr->sent += s; + return 0; +} + +static void resend_query(rptr) +ResRQ *rptr; +{ + if (rptr->resend == 0) + return; + reinfo.re_resends++; + switch(rptr->type) + { + case T_PTR: + (void)do_query_number(NULL, &rptr->addr, rptr); + break; +#ifdef INET6 + case T_AAAA: +#endif + case T_A: + (void)do_query_name(NULL, rptr->name, rptr); + break; + default: + break; + } + return; +} + +/* + * process name server reply. + */ +static int proc_answer(rptr, hptr, buf, eob) +ResRQ *rptr; +char *buf, *eob; +HEADER *hptr; +{ + Reg char *cp, **alias; + Reg struct hent *hp; + int class, type, dlen, len, ans = 0, n; + struct IN_ADDR dr, *adr; + + cp = buf + sizeof(HEADER); + hp = (struct hent *)&(rptr->he); + adr = &hp->h_addr; +#ifdef INET6 + while (adr->s6_laddr[0] | adr->s6_laddr[1] | adr->s6_laddr[2] | + adr->s6_laddr[3]) +#else + while (adr->s_addr) +#endif + adr++; + alias = hp->h_aliases; + while (*alias) + alias++; +#if SOLARIS_2 && !defined(__GNUC__) /* brain damaged compiler it seems */ + for (; hptr->qdcount > 0; hptr->qdcount--) +#else + while (hptr->qdcount-- > 0) +#endif + if ((n = __ircd_dn_skipname((u_char *)cp, (u_char *)eob)) == -1) + break; + else + cp += (n + QFIXEDSZ); + /* + * proccess each answer sent to us blech. + */ + while (hptr->ancount-- > 0 && cp && cp < eob) { + n = ircd_dn_expand((u_char *)buf, (u_char *)eob, (u_char *)cp, + hostbuf, sizeof(hostbuf)); + if (n <= 0) + break; + + cp += n; + type = (int)ircd_getshort((u_char *)cp); + cp += 2; /* INT16SZ */ + class = (int)ircd_getshort((u_char *)cp); + cp += 2; /* INT16SZ */ + rptr->ttl = ircd_getlong((u_char *)cp); + cp += 4; /* INT32SZ */ + dlen = (int)ircd_getshort((u_char *)cp); + cp += 2; /* INT16SZ */ + rptr->type = type; + + len = strlen(hostbuf); + /* name server never returns with trailing '.' */ + if (!index(hostbuf,'.') && (ircd_res.options & RES_DEFNAMES)) + { + (void)strcat(hostbuf, dot); + len++; + (void)strncat(hostbuf, ircd_res.defdname, + sizeof(hostbuf) - 1 - len); + len = MIN(len + strlen(ircd_res.defdname), + sizeof(hostbuf) - 1); + } + + switch(type) + { +#ifdef INET6 + case T_AAAA : +#endif + case T_A : +#ifdef INET6 + if (dlen != ((type==T_AAAA) ? sizeof(dr) : + sizeof(struct in_addr))) +#else + if (dlen != sizeof(dr)) +#endif + { + sendto_flag(SCH_ERROR, + "Bad IP length (%d) returned for %s", dlen, + hostbuf); + Debug((DEBUG_DNS, + "Bad IP length (%d) returned for %s", + dlen, hostbuf)); + return -2; + } + hp->h_length = dlen; + if (ans == 1) + hp->h_addrtype = (class == C_IN) ? + AFINET: AF_UNSPEC; +#ifdef INET6 + if (type == T_AAAA) + bcopy(cp, (char *)&dr, dlen); + else { + dr.s6_laddr[0]=dr.s6_laddr[1]=0; + dr.s6_laddr[2]=htonl(0xffff); + bcopy(cp, &dr.s6_laddr[3], INADDRSZ); + } + bcopy(dr.s6_addr, adr->s6_addr, IN6ADDRSZ); +#else + bcopy(cp, (char *)&dr, dlen); + adr->s_addr = dr.s_addr; +#endif +#ifdef INET6 + Debug((DEBUG_INFO,"got ip # %s for %s", + inet_ntop(AF_INET6, (char *)adr, mydummy, + MYDUMMY_SIZE), + hostbuf)); +#else + Debug((DEBUG_INFO,"got ip # %s for %s", + inetntoa((char *)adr), + hostbuf)); +#endif + if (!hp->h_name) + { + hp->h_name =(char *)MyMalloc(len+1); + (void)strcpy(hp->h_name, hostbuf); + } + ans++; + adr++; + cp += dlen; + break; + case T_PTR : + if((n = ircd_dn_expand((u_char *)buf, (u_char *)eob, + (u_char *)cp, hostbuf, + sizeof(hostbuf) )) < 0) + { + cp = NULL; + break; + } + cp += n; + len = strlen(hostbuf); + Debug((DEBUG_INFO, "got host %s (%d vs %d)", + hostbuf, len, strlen(hostbuf))); + if (bad_hostname(hostbuf, len)) + return -1; + /* + * copy the returned hostname into the host name + * or alias field if there is a known hostname + * already. + */ + if (hp->h_name) + { + Debug((DEBUG_INFO, "duplicate PTR ignored")); + } + else + { + hp->h_name = (char *)MyMalloc(len + 1); + (void)strcpy(hp->h_name, hostbuf); + } + ans++; + break; + case T_CNAME : + cp += dlen; + Debug((DEBUG_INFO,"got cname %s",hostbuf)); + if (bad_hostname(hostbuf, len)) + return -1; /* a break would be enough here */ + if (alias >= &(hp->h_aliases[MAXALIASES-1])) + break; + *alias = (char *)MyMalloc(len + 1); + (void)strcpy(*alias++, hostbuf); + *alias = NULL; + ans++; + break; + default : +#ifdef DEBUG + Debug((DEBUG_INFO,"proc_answer: type:%d for:%s", + type,hostbuf)); +#endif + break; + } + } + return ans; +} + +/* + * read a dns reply from the nameserver and process it. + */ +struct hostent *get_res(lp) +char *lp; +{ + static char buf[sizeof(HEADER) + MAXPACKET]; + Reg HEADER *hptr; + Reg ResRQ *rptr = NULL; + aCache *cp = NULL; +#ifdef INET6 + struct sockaddr_in sin; +#else + struct sockaddr_in sin; +#endif + int rc, a, max; + SOCK_LEN_TYPE len = sizeof(sin); + + (void)alarm((unsigned)4); +#ifdef INET6 + rc = recvfrom(resfd, buf, sizeof(buf), 0, (struct sockaddr *)&sin, &len); +#else + rc = recvfrom(resfd, buf, sizeof(buf), 0, (struct sockaddr *)&sin, &len); +#endif + + (void)alarm((unsigned)0); + if (rc <= sizeof(HEADER)) + goto getres_err; + /* + * convert DNS reply reader from Network byte order to CPU byte order. + */ + hptr = (HEADER *)buf; + hptr->id = ntohs(hptr->id); + hptr->ancount = ntohs(hptr->ancount); + hptr->qdcount = ntohs(hptr->qdcount); + hptr->nscount = ntohs(hptr->nscount); + hptr->arcount = ntohs(hptr->arcount); +#ifdef DEBUG + Debug((DEBUG_NOTICE, "get_res:id = %d rcode = %d ancount = %d", + hptr->id, hptr->rcode, hptr->ancount)); +#endif + reinfo.re_replies++; + /* + * response for an id which we have already received an answer for + * just ignore this response. + */ + rptr = find_id(hptr->id); + if (!rptr) + goto getres_err; + /* + * check against possibly fake replies + */ + max = MIN(ircd_res.nscount, rptr->sends); + if (!max) + max = 1; + + for (a = 0; a < max; a++) +#ifdef INET6 + if (!ircd_res.nsaddr_list[a].sin_addr.s_addr || + !bcmp((char *)&sin.sin_addr, + (char *)&ircd_res.nsaddr_list[a].sin_addr, + sizeof(struct in_addr))) +#else + if (!ircd_res.nsaddr_list[a].sin_addr.s_addr || + !bcmp((char *)&sin.sin_addr, + (char *)&ircd_res.nsaddr_list[a].sin_addr, + sizeof(struct in_addr))) +#endif + break; + if (a == max) + { + reinfo.re_unkrep++; + goto getres_err; + } + + if ((hptr->rcode != NOERROR) || (hptr->ancount == 0)) + { + switch (hptr->rcode) + { + case NXDOMAIN: + h_errno = TRY_AGAIN; + break; + case SERVFAIL: + h_errno = TRY_AGAIN; + break; + case NOERROR: + h_errno = NO_DATA; + break; + case FORMERR: + case NOTIMP: + case REFUSED: + default: + h_errno = NO_RECOVERY; + break; + } + reinfo.re_errors++; + /* + ** If a bad error was returned, we stop here and dont send + ** send any more (no retries granted). + */ + if (h_errno != TRY_AGAIN) + { + Debug((DEBUG_DNS, "Fatal DNS error %d for %d", + h_errno, hptr->rcode)); + rptr->resend = 0; + rptr->retries = 0; + } + goto getres_err; + } + a = proc_answer(rptr, hptr, buf, buf+rc); + if (a == -1) { + sendto_flag(SCH_ERROR, "Bad hostname returned from %s for %s", +#ifdef INET6 + inetntop(AF_INET, &sin.sin_addr, mydummy2, + MYDUMMY_SIZE), + inetntop(AF_INET6, rptr->he.h_addr.s6_addr, + mydummy, MYDUMMY_SIZE)); +#else + inetntoa((char *)&sin.sin_addr), + inetntoa((char *)&rptr->he.h_addr)); +#endif +#ifdef INET6 + Debug((DEBUG_DNS, "Bad hostname returned from %s for %s", + inet_ntop(AF_INET, &sin.sin_addr,mydummy2,MYDUMMY_SIZE), + inet_ntop(AF_INET6, rptr->he.h_addr.s6_addr, mydummy, + MYDUMMY_SIZE))); +#else + Debug((DEBUG_DNS, "Bad hostname returned from %s for %s", + inetntoa((char *)&sin.sin_addr), + inetntoa((char *)&rptr->he.h_addr))); +#endif + } +#ifdef DEBUG + Debug((DEBUG_INFO,"get_res:Proc answer = %d",a)); +#endif + if (a > 0 && rptr->type == T_PTR) + { + struct hostent *hp2 = NULL; + + if (BadPtr(rptr->he.h_name)) /* Kludge! 960907/Vesa */ + goto getres_err; + +#ifdef INET6 + Debug((DEBUG_DNS, "relookup %s <-> %s", + rptr->he.h_name, inet_ntop(AF_INET6, + (char *)&rptr->he.h_addr, + mydummy, MYDUMMY_SIZE))); +#else + Debug((DEBUG_DNS, "relookup %s <-> %s", + rptr->he.h_name, inetntoa((char *)&rptr->he.h_addr))); +#endif + /* + * Lookup the 'authoritive' name that we were given for the + * ip#. By using this call rather than regenerating the + * type we automatically gain the use of the cache with no + * extra kludges. + */ + if ((hp2 = gethost_byname(rptr->he.h_name, &rptr->cinfo))) + if (lp) + bcopy((char *)&rptr->cinfo, lp, sizeof(Link)); + /* + * If name wasn't found, a request has been queued and it will + * be the last one queued. This is rather nasty way to keep + * a host alias with the query. -avalon + */ + if (!hp2 && rptr->he.h_aliases[0]) + for (a = 0; rptr->he.h_aliases[a]; a++) + { + Debug((DEBUG_DNS, "Copied CNAME %s for %s", + rptr->he.h_aliases[a], + rptr->he.h_name)); + last->he.h_aliases[a] = rptr->he.h_aliases[a]; + rptr->he.h_aliases[a] = NULL; + } + + rem_request(rptr); + return hp2; + } + + if (a > 0) + { + if (lp) + bcopy((char *)&rptr->cinfo, lp, sizeof(Link)); + cp = make_cache(rptr); +#ifdef DEBUG + Debug((DEBUG_INFO,"get_res:cp=%#x rptr=%#x (made)",cp,rptr)); +#endif + + rem_request(rptr); + } + else + if (!rptr->sent) + rem_request(rptr); + return cp ? (struct hostent *)&cp->he : NULL; + +getres_err: + /* + * Reprocess an error if the nameserver didnt tell us to "TRY_AGAIN". + */ + if (rptr) + { + if (h_errno != TRY_AGAIN) + { + /* + * If we havent tried with the default domain and its + * set, then give it a try next. + */ + if (ircd_res.options & RES_DEFNAMES && ++rptr->srch == 0) + { + rptr->retries = ircd_res.retry; + rptr->sends = 0; + rptr->resend = 1; +#ifdef INET6 +/* Comment out this ifdef to get names like ::ffff:a.b.c.d */ + if(rptr->type == T_AAAA) + query_name(rptr->name, C_IN, T_A, rptr); + Debug((DEBUG_DNS,"getres_err: didn't work with T_AAAA, now also trying with T_A for %s",rptr->name)); +#endif + resend_query(rptr); + } + else + { +#ifdef INET6 +/* Comment out this ifdef to get names like ::ffff:a.b.c.d */ + if(rptr->type == T_AAAA) + query_name(rptr->name, C_IN, T_A, rptr); + Debug((DEBUG_DNS,"getres_err: didn't work with T_AAAA, now also trying with T_A for %s",rptr->name)); +#endif + resend_query(rptr); + } + } + else if (lp) + bcopy((char *)&rptr->cinfo, lp, sizeof(Link)); + } + return (struct hostent *)NULL; +} + +static int hash_number(ip) +Reg u_char *ip; +{ + Reg u_int hashv = 0; + + /* could use loop but slower */ + hashv += (int)*ip++; + hashv += hashv + (int)*ip++; + hashv += hashv + (int)*ip++; +#ifdef INET6 + hashv += hashv + (int)*ip++; + hashv += hashv + (int)*ip++; + hashv += hashv + (int)*ip++; + hashv += hashv + (int)*ip++; + hashv += hashv + (int)*ip++; + hashv += hashv + (int)*ip++; + hashv += hashv + (int)*ip++; + hashv += hashv + (int)*ip++; + hashv += hashv + (int)*ip++; + hashv += hashv + (int)*ip++; + hashv += hashv + (int)*ip++; + hashv += hashv + (int)*ip++; +#endif + hashv += hashv + (int)*ip; + hashv %= ARES_CACSIZE; + return (hashv); +} + +static int hash_name(name) +register char *name; +{ + Reg u_int hashv = 0; + + for (; *name && *name != '.'; name++) + hashv += *name; + hashv %= ARES_CACSIZE; + return (hashv); +} + +/* +** Add a new cache item to the queue and hash table. +*/ +static aCache *add_to_cache(ocp) +Reg aCache *ocp; +{ + Reg aCache *cp = NULL; + Reg int hashv; + +#ifdef DEBUG + Debug((DEBUG_INFO, + "add_to_cache:ocp %#x he %#x name %#x addrl %#x 0 %#x", + ocp, &ocp->he, ocp->he.h_name, ocp->he.h_addr_list, + ocp->he.h_addr_list[0])); +#endif + ocp->list_next = cachetop; + cachetop = ocp; + + hashv = hash_name(ocp->he.h_name); + ocp->hname_next = hashtable[hashv].name_list; + hashtable[hashv].name_list = ocp; + + hashv = hash_number((u_char *)ocp->he.h_addr); + ocp->hnum_next = hashtable[hashv].num_list; + hashtable[hashv].num_list = ocp; + +#ifdef DEBUG +#ifdef INET6 + Debug((DEBUG_INFO,"add_to_cache:added %s[%08x%08x%08x%08x] cache %#x.", + ocp->he.h_name, + ((struct in6_addr *)ocp->he.h_addr_list)->s6_laddr[0], + ((struct in6_addr *)ocp->he.h_addr_list)->s6_laddr[1], + ((struct in6_addr *)ocp->he.h_addr_list)->s6_laddr[2], + ((struct in6_addr *)ocp->he.h_addr_list)->s6_laddr[3], ocp)); +#else + Debug((DEBUG_INFO, "add_to_cache:added %s[%08x] cache %#x.", + ocp->he.h_name, ocp->he.h_addr_list[0], ocp)); +#endif + Debug((DEBUG_INFO, + "add_to_cache:h1 %d h2 %x lnext %#x namnext %#x numnext %#x", + hash_name(ocp->he.h_name), hashv, ocp->list_next, + ocp->hname_next, ocp->hnum_next)); +#endif + + /* + * LRU deletion of excessive cache entries. + */ + if (++incache > MAXCACHED) + { + for (cp = cachetop; cp->list_next; cp = cp->list_next) + ; + rem_cache(cp); + } + cainfo.ca_adds++; + + return ocp; +} + +/* +** update_list does not alter the cache structure passed. It is assumed that +** it already contains the correct expire time, if it is a new entry. Old +** entries have the expirey time updated. +*/ +static void update_list(rptr, cachep) +ResRQ *rptr; +aCache *cachep; +{ + Reg aCache **cpp, *cp = cachep; + Reg char *s, *t, **base; + Reg int i, j; + int addrcount; + + /* + ** search for the new cache item in the cache list by hostname. + ** If found, move the entry to the top of the list and return. + */ + cainfo.ca_updates++; + + for (cpp = &cachetop; *cpp; cpp = &((*cpp)->list_next)) + if (cp == *cpp) + break; + if (!*cpp) + return; + *cpp = cp->list_next; + cp->list_next = cachetop; + cachetop = cp; + if (!rptr) + return; + +#ifdef DEBUG + Debug((DEBUG_DEBUG,"u_l:cp %#x na %#x al %#x ad %#x", + cp,cp->he.h_name,cp->he.h_aliases,cp->he.h_addr)); + Debug((DEBUG_DEBUG,"u_l:rptr %#x h_n %#x", rptr, rptr->he.h_name)); +#endif + /* + * Compare the cache entry against the new record. Add any + * previously missing names for this entry. + */ + for (i = 0; cp->he.h_aliases[i]; i++) + ; + addrcount = i; + for (i = 0, s = rptr->he.h_name; s && i < MAXALIASES; + s = rptr->he.h_aliases[i++]) + { + for (j = 0, t = cp->he.h_name; t && j < MAXALIASES; + t = cp->he.h_aliases[j++]) + if (!mycmp(t, s)) + break; + if (!t && j < MAXALIASES-1) + { + base = cp->he.h_aliases; + + addrcount++; + base = (char **)MyRealloc((char *)base, + sizeof(char *) * (addrcount + 1)); + cp->he.h_aliases = base; +#ifdef DEBUG + Debug((DEBUG_DNS,"u_l:add name %s hal %x ac %d", + s, cp->he.h_aliases, addrcount)); +#endif + base[addrcount-1] = mystrdup(s); + base[addrcount] = NULL; + } + } +#ifdef INET6 + for (i = 0; cp->he.h_addr_list[i]; i++) +#else + for (i = 0; cp->he.h_addr_list[i]; i++) +#endif + ; + addrcount = i; + + /* + * Do the same again for IP#'s. + */ +#ifdef INET6 + for (s = (char *)rptr->he.h_addr.S_ADDR; + ((struct IN_ADDR *)s)->S_ADDR; s += sizeof(struct IN_ADDR)) +#else + for (s = (char *)&rptr->he.h_addr.S_ADDR; + ((struct IN_ADDR *)s)->S_ADDR; s += sizeof(struct IN_ADDR)) +#endif + { +#ifdef INET6 + for (i = 0; (t = cp->he.h_addr_list[i]); i++) +#else + for (i = 0; (t = cp->he.h_addr_list[i]); i++) +#endif + if (!bcmp(s, t, sizeof(struct IN_ADDR))) + break; + if (i >= MAXADDRS || addrcount >= MAXADDRS) + break; + /* + * Oh man this is bad...I *HATE* it. -avalon + * + * Whats it do ? Reallocate two arrays, one of pointers + * to "char *" and the other of IP addresses. Contents of + * the IP array *MUST* be preserved and the pointers into + * it recalculated. + */ + if (!t) + { + struct IN_ADDR **ab; + + ab = (struct IN_ADDR **)cp->he.h_addr_list; + addrcount++; + t = (char *)MyRealloc((char *)*ab, + addrcount * sizeof(struct IN_ADDR)); + base = (char **)MyRealloc((char *)ab, + (addrcount + 1) * sizeof(*ab)); + cp->he.h_addr_list = base; +#ifdef DEBUG + Debug((DEBUG_DNS,"u_l:add IP %x hal %x ac %d", + ntohl(((struct IN_ADDR *)s)->S_ADDR), + cp->he.h_addr_list, + addrcount)); +#endif + for (; addrcount; addrcount--) + { + *ab++ = (struct IN_ADDR *)t; + t += sizeof(struct IN_ADDR); + } + *ab = NULL; + bcopy(s, (char *)*--ab, sizeof(struct IN_ADDR)); + } + } + return; +} + +static aCache *find_cache_name(name) +char *name; +{ + Reg aCache *cp; + Reg char *s; + Reg int hashv, i; + + hashv = hash_name(name); + + cp = hashtable[hashv].name_list; +#ifdef DEBUG + Debug((DEBUG_DNS,"find_cache_name:find %s : hashv = %d",name,hashv)); +#endif + + for (; cp; cp = cp->hname_next) + for (i = 0, s = cp->he.h_name; s; s = cp->he.h_aliases[i++]) + if (mycmp(s, name) == 0) + { + cainfo.ca_na_hits++; + update_list(NULL, cp); + return cp; + } + + for (cp = cachetop; cp; cp = cp->list_next) + { + /* + * if no aliases or the hash value matches, we've already + * done this entry and all possiblilities concerning it. + */ + if (!*cp->he.h_aliases) + continue; + if (hashv == hash_name(cp->he.h_name)) + continue; + for (i = 0, s = cp->he.h_aliases[i]; s && i < MAXALIASES; i++) + if (!mycmp(name, s)) { + cainfo.ca_na_hits++; + update_list(NULL, cp); + return cp; + } + } + return NULL; +} + +/* + * find a cache entry by ip# and update its expire time + */ +static aCache *find_cache_number(rptr, numb) +ResRQ *rptr; +char *numb; +{ + Reg aCache *cp; + Reg int hashv,i; +#ifdef DEBUG + struct IN_ADDR *ip = (struct IN_ADDR *)numb; +#endif + + hashv = hash_number((u_char *)numb); + + cp = hashtable[hashv].num_list; +#ifdef DEBUG +#ifdef INET6 + Debug((DEBUG_DNS, + "find_cache_number:find %s[%08x%08x%08x%08x]: hashv = %d", + inet_ntop(AF_INET6, numb,mydummy,MYDUMMY_SIZE), ip->s6_laddr[0], + ip->s6_laddr[1], ip->s6_laddr[2], ip->s6_laddr[3], hashv)); +#else + Debug((DEBUG_DNS,"find_cache_number:find %s[%08x]: hashv = %d", + inetntoa(numb), ntohl(ip->s_addr), hashv)); +#endif +#endif + for (; cp; cp = cp->hnum_next) + { +#ifdef INET6 + for (i = 0; cp->he.h_addr_list[i]; i++) +#else + for (i = 0; cp->he.h_addr_list[i]; i++) +#endif + { + if (!bcmp(cp->he.h_addr_list[i], numb, + sizeof(struct IN_ADDR))) + { + cainfo.ca_nu_hits++; + update_list(rptr, cp); + return cp; + } + } + } + for (cp = cachetop; cp; cp = cp->list_next) + { + if (!cp->he.h_addr_list && !cp->he.h_aliases) + { + cp = rem_list(cp); + continue; + } + /* + * single address entry...would have been done by hashed + * search above... + */ +#ifdef INET6 + if (!cp->he.h_addr_list[1]) +#else + if (!cp->he.h_addr_list[1]) +#endif + continue; + /* + * if the first IP# has the same hashnumber as the IP# we + * are looking for, its been done already. + */ + if (hashv == hash_number((u_char *)cp->he.h_addr_list[0])) + continue; +#ifdef INET6 + for (i = 1; cp->he.h_addr_list[i]; i++) +#else + for (i = 1; cp->he.h_addr_list[i]; i++) +#endif + if (!bcmp(cp->he.h_addr_list[i], numb, + sizeof(struct IN_ADDR))) + { + cainfo.ca_nu_hits++; + update_list(rptr, cp); + return cp; + } + } + return NULL; +} + +static aCache *make_cache(rptr) +ResRQ *rptr; +{ + Reg aCache *cp; + Reg int i, n; + Reg struct hostent *hp; + Reg char *s, **t; + + /* + ** shouldn't happen but it just might... + */ + if (!rptr->he.h_name || !WHOSTENTP(rptr->he.h_addr.S_ADDR)) + return NULL; + /* + ** Make cache entry. First check to see if the cache already exists + ** and if so, return a pointer to it. + */ + for (i = 0; WHOSTENTP(rptr->he.h_addr_list[i].S_ADDR); i++) + if ((cp = find_cache_number(rptr, +#ifdef INET6 + (char *)(rptr->he.h_addr_list[i].S_ADDR)))) +#else + (char *)&(rptr->he.h_addr_list[i].S_ADDR)))) +#endif + return cp; + + /* + ** a matching entry wasnt found in the cache so go and make one up. + */ + cp = (aCache *)MyMalloc(sizeof(aCache)); + bzero((char *)cp, sizeof(aCache)); + hp = &cp->he; + for (i = 0; i < MAXADDRS - 1; i++) + if (!WHOSTENTP(rptr->he.h_addr_list[i].S_ADDR)) + break; + + /* + ** build two arrays, one for IP#'s, another of pointers to them. + */ + t = hp->h_addr_list = (char **)MyMalloc(sizeof(char *) * (i+1)); + bzero((char *)t, sizeof(char *) * (i+1)); + + s = (char *)MyMalloc(sizeof(struct IN_ADDR) * i); + bzero(s, sizeof(struct IN_ADDR) * i); + + for (n = 0; n < i; n++, s += sizeof(struct IN_ADDR)) + { + *t++ = s; + bcopy((char *)&rptr->he.h_addr_list[n], s, + sizeof(struct IN_ADDR)); + } + *t = (char *)NULL; + + /* + ** an array of pointers to CNAMEs. + */ + for (i = 0; i < MAXALIASES - 1; i++) + if (!rptr->he.h_aliases[i]) + break; + i++; + t = hp->h_aliases = (char **)MyMalloc(sizeof(char *) * i); + for (n = 0; n < i; n++, t++) + { + *t = rptr->he.h_aliases[n]; + rptr->he.h_aliases[n] = NULL; + } + + hp->h_addrtype = rptr->he.h_addrtype; + hp->h_length = rptr->he.h_length; + hp->h_name = rptr->he.h_name; + if (rptr->ttl < 600) + { + reinfo.re_shortttl++; + cp->ttl = 600; + } + else + cp->ttl = rptr->ttl; + cp->expireat = timeofday + cp->ttl; + rptr->he.h_name = NULL; +#ifdef DEBUG + Debug((DEBUG_INFO,"make_cache:made cache %#x", cp)); +#endif + return add_to_cache(cp); +} + +/* + * rem_list + */ +static aCache *rem_list(cp) +aCache *cp; +{ + aCache **cpp, *cr = cp->list_next; + + /* + * remove cache entry from linked list + */ + for (cpp = &cachetop; *cpp; cpp = &((*cpp)->list_next)) + if (*cpp == cp) + { + *cpp = cp->list_next; + MyFree((char *)cp); + break; + } + return cr; +} + + +/* + * rem_cache + * delete a cache entry from the cache structures and lists and return + * all memory used for the cache back to the memory pool. + */ +static void rem_cache(ocp) +aCache *ocp; +{ + Reg aCache **cp; + Reg struct hostent *hp = &ocp->he; + Reg int hashv; + Reg aClient *cptr; + +#ifdef DEBUG + Debug((DEBUG_DNS, "rem_cache: ocp %#x hp %#x l_n %#x aliases %#x", + ocp, hp, ocp->list_next, hp->h_aliases)); +#endif + /* + ** Cleanup any references to this structure by destroying the + ** pointer. + */ + for (hashv = highest_fd; hashv >= 0; hashv--) + if ((cptr = local[hashv]) && (cptr->hostp == hp)) + cptr->hostp = NULL; + /* + * remove cache entry from linked list + */ + for (cp = &cachetop; *cp; cp = &((*cp)->list_next)) + if (*cp == ocp) + { + *cp = ocp->list_next; + break; + } + /* + * remove cache entry from hashed name lists + */ + hashv = hash_name(hp->h_name); +#ifdef DEBUG + Debug((DEBUG_DEBUG,"rem_cache: h_name %s hashv %d next %#x first %#x", + hp->h_name, hashv, ocp->hname_next, + hashtable[hashv].name_list)); +#endif + for (cp = &hashtable[hashv].name_list; *cp; cp = &((*cp)->hname_next)) + if (*cp == ocp) + { + *cp = ocp->hname_next; + break; + } + /* + * remove cache entry from hashed number list + */ + hashv = hash_number((u_char *)hp->h_addr); +#ifdef DEBUG +# ifdef INET6 + Debug((DEBUG_DEBUG,"rem_cache: h_addr %s hashv %d next %#x first %#x", + inet_ntop(AF_INET6, hp->h_addr, mydummy, MYDUMMY_SIZE), + hashv, ocp->hnum_next, hashtable[hashv].num_list)); +# else + Debug((DEBUG_DEBUG,"rem_cache: h_addr %s hashv %d next %#x first %#x", + inetntoa(hp->h_addr), + hashv, ocp->hnum_next, hashtable[hashv].num_list)); +# endif +#endif + for (cp = &hashtable[hashv].num_list; *cp; cp = &((*cp)->hnum_next)) + if (*cp == ocp) + { + *cp = ocp->hnum_next; + break; + } + + /* + * free memory used to hold the various host names and the array + * of alias pointers. + */ + if (hp->h_name) + MyFree(hp->h_name); + if (hp->h_aliases) + { + for (hashv = 0; hp->h_aliases[hashv]; hashv++) + MyFree(hp->h_aliases[hashv]); + MyFree((char *)hp->h_aliases); + } + + /* + * free memory used to hold ip numbers and the array of them. + */ + if (hp->h_addr_list) + { + if (*hp->h_addr_list) + MyFree((char *)*hp->h_addr_list); + MyFree((char *)hp->h_addr_list); + } + + MyFree((char *)ocp); + + incache--; + cainfo.ca_dels++; + + return; +} + +/* + * removes entries from the cache which are older than their expirey times. + * returns the time at which the server should next poll the cache. + */ +time_t expire_cache(now) +time_t now; +{ + Reg aCache *cp, *cp2; + Reg time_t next = 0; + + for (cp = cachetop; cp; cp = cp2) + { + cp2 = cp->list_next; + + if (now >= cp->expireat) + { + cainfo.ca_expires++; + rem_cache(cp); + } + else if (!next || next > cp->expireat) + next = cp->expireat; + } + return (next > now) ? next : (now + AR_TTL); +} + +/* + * remove all dns cache entries. + */ +void flush_cache() +{ + Reg aCache *cp; + + while ((cp = cachetop)) + rem_cache(cp); +} + +int m_dns(cptr, sptr, parc, parv) +aClient *cptr, *sptr; +int parc; +char *parv[]; +{ + Reg aCache *cp; + Reg int i; + + if (parv[1] && *parv[1] == 'l') { + for(cp = cachetop; cp; cp = cp->list_next) + { + sendto_one(sptr, "NOTICE %s :Ex %d ttl %d host %s(%s)", + parv[0], cp->expireat - timeofday, cp->ttl, +#ifdef INET6 + cp->he.h_name, inetntop(AF_INET6, + cp->he.h_addr, + mydummy, + MYDUMMY_SIZE)); +#else + cp->he.h_name, inetntoa(cp->he.h_addr)); +#endif + for (i = 0; cp->he.h_aliases[i]; i++) + sendto_one(sptr,"NOTICE %s : %s = %s (CN)", + parv[0], cp->he.h_name, + cp->he.h_aliases[i]); +#ifdef INET6 + for (i = 1; cp->he.h_addr_list[i]; i++) { +#else + for (i = 1; cp->he.h_addr_list[i]; i++) { +#endif + sendto_one(sptr,"NOTICE %s : %s = %s (IP)", + parv[0], cp->he.h_name, +#ifdef INET6 + inetntop(AF_INET6, + cp->he.h_addr_list[i], + mydummy, MYDUMMY_SIZE)); +#else + inetntoa(cp->he.h_addr_list[i])); +#endif + } + } + return 2; + } + sendto_one(sptr,"NOTICE %s :Ca %d Cd %d Ce %d Cl %d Ch %d:%d Cu %d", + sptr->name, + cainfo.ca_adds, cainfo.ca_dels, cainfo.ca_expires, + cainfo.ca_lookups, + cainfo.ca_na_hits, cainfo.ca_nu_hits, cainfo.ca_updates); + + sendto_one(sptr,"NOTICE %s :Re %d Rl %d/%d Rp %d Rq %d", + sptr->name, reinfo.re_errors, reinfo.re_nu_look, + reinfo.re_na_look, reinfo.re_replies, reinfo.re_requests); + sendto_one(sptr,"NOTICE %s :Ru %d Rsh %d Rs %d(%d) Rt %d", sptr->name, + reinfo.re_unkrep, reinfo.re_shortttl, reinfo.re_sent, + reinfo.re_resends, reinfo.re_timeouts); + return 2; +} + +u_long cres_mem(sptr, nick) +aClient *sptr; +char *nick; +{ + register aCache *c = cachetop; + register struct hostent *h; + register int i; + u_long nm = 0, im = 0, sm = 0, ts = 0; + + for ( ;c ; c = c->list_next) + { + sm += sizeof(*c); + h = &c->he; +#ifdef INET6 + for (i = 0; h->h_addr_list[i]; i++) +#else + for (i = 0; h->h_addr_list[i]; i++) +#endif + { + im += sizeof(char *); + im += sizeof(struct IN_ADDR); + } + im += sizeof(char *); + for (i = 0; h->h_aliases[i]; i++) + { + nm += sizeof(char *); + nm += strlen(h->h_aliases[i]); + } + nm += i - 1; + nm += sizeof(char *); + if (h->h_name) + nm += strlen(h->h_name); + } + ts = ARES_CACSIZE * sizeof(CacheTable); + sendto_one(sptr, ":%s %d %s :RES table %d", + me.name, RPL_STATSDEBUG, nick, ts); + sendto_one(sptr, ":%s %d %s :Structs %d IP storage %d Name storage %d", + me.name, RPL_STATSDEBUG, nick, sm, im, nm); + return ts + sm + im + nm; +} + + +static int bad_hostname(name, len) +char *name; +int len; +{ + char *s, c; + + for (s = name; (c = *s) && len; s++, len--) + if (isspace(c) || (c == 0x7) || (c == ':') || + (c == '*') || (c == '?')) + return -1; + return 0; +} |