home *** CD-ROM | disk | FTP | other *** search
/ HAM Radio 1 / HamRadio.cdr / misc / tcpipsrc / domain.c < prev    next >
C/C++ Source or Header  |  1991-01-26  |  40KB  |  1,706 lines

  1. /*
  2.  *    DOMAIN.C -- domain name system stub resolver
  3.  *
  4.  *      Original code by Phil Karn, KA9Q.
  5.  *
  6.  *    04-90 -- Bill Simpson added address->name resolution, time-to-live,
  7.  *    thru     memory caching, generalized multi-record multi-type searches,
  8.  *    10-90     and many minor changes to conform more closely to the RFCs.
  9.  */
  10.  
  11. #include <stdio.h>
  12. #include <ctype.h>
  13. #include <time.h>
  14. #include <sys/stat.h>
  15. #include "global.h"
  16. #include "mbuf.h"
  17. #include "netuser.h"
  18. #include "ip.h"
  19. #include "socket.h"
  20. #include "cmdparse.h"
  21. #include "proc.h"
  22. #include "domain.h"
  23. #include "commands.h"
  24. #include "files.h"
  25.  
  26. #undef    DEBUG                /* for certain trace messages */
  27. #undef    DEBUG_PAIN            /* for painful debugging */
  28.  
  29. extern int main_exit;            /* from main program (flag) */
  30.  
  31. static struct rr *Dcache = NULLRR;    /* Cache of resource records */
  32. static int Dcache_size = 20;        /* size limit */
  33. static time_t Dcache_time = 0L;     /* timestamp */
  34.  
  35. static int Dfile_clean = FALSE;     /* discard expired records (flag) */
  36. static int Dfile_reading = 0;        /* read interlock (count) */
  37. static int Dfile_writing = 0;        /* write interlock (count) */
  38.  
  39. struct proc *Dfile_updater = NULLPROC;
  40. static int32 Dfile_wait_absolute = 0L;    /* timeout Clock time */
  41. static int Dfile_wait_relative = 300;    /* timeout file activity (seconds) */
  42.  
  43. static struct dserver *Dservers = NULLDOM; /* List of potential servers */
  44. static int Dserver_retries = 2;        /* Attempts to reach servers */
  45.  
  46. static char *Dsuffix = NULLCHAR;    /* Default suffix for names without periods */
  47. static int Dtrace = FALSE;
  48. static char *Dtypes[] = {
  49.     "",
  50.     "A",
  51.     "NS",
  52.     "MD",
  53.     "MF",
  54.     "CNAME",
  55.     "SOA",
  56.     "MB",
  57.     "MG",
  58.     "MR",
  59.     "NULL",
  60.     "WKS",
  61.     "PTR",
  62.     "HINFO",
  63.     "MINFO",
  64.     "MX",
  65.     "TXT"
  66. };
  67. static int Ndtypes = 17;
  68. static char delim[] = " \t\r\n";
  69.  
  70. static int docache __ARGS((int argc,char *argv[],void *p));
  71. static int dosuffix __ARGS((int argc,char *argv[],void *p));
  72.  
  73. static int docacheclean __ARGS((int argc,char *argv[],void *p));
  74. static int docachelist __ARGS((int argc,char *argv[],void *p));
  75. static int docachesize __ARGS((int argc,char *argv[],void *p));
  76. static int docachewait __ARGS((int argc,char *argv[],void *p));
  77.  
  78. static void dlist_add __ARGS((struct dserver *dp));
  79. static void dlist_drop __ARGS((struct dserver *dp));
  80. static int dodnsadd __ARGS((int argc,char *argv[],void *p));
  81. static int dodnsdrop __ARGS((int argc,char *argv[],void *p));
  82. static int dodnslist __ARGS((int argc,char *argv[],void *p));
  83. static int dodnsretry __ARGS((int argc,char *argv[],void *p));
  84. static int dodnstrace __ARGS((int argc,char *argv[],void *p));
  85.  
  86. static int check_ttl __ARGS((struct rr *rrlp));
  87. static int compare_rr __ARGS((struct rr *search_rrp,struct rr *target_rrp));
  88. static int compare_rr_list __ARGS((struct rr *rrlp,struct rr *target_rrp));
  89. static struct rr *copy_rr __ARGS((struct rr *rrp));
  90. static struct rr *copy_rr_list __ARGS((struct rr *rrlp));
  91. static struct rr *make_rr __ARGS((int source,
  92.     char *dname,int16 class,int16 type,int32 ttl,int16 rdl,void *data));
  93.  
  94. static void dcache_add __ARGS((struct rr *rrlp));
  95. static void dcache_drop __ARGS((struct rr *rrp));
  96. static struct rr *dcache_search __ARGS((struct rr *rrlp));
  97. static void dcache_update __ARGS((struct rr *rrlp));
  98.  
  99. static struct rr *get_rr __ARGS((FILE *fp, struct rr *lastrrp));
  100. static void put_rr __ARGS((FILE *fp,struct rr *rrp));
  101. static struct rr *dfile_search __ARGS((struct rr *rrlp));
  102. static void dfile_update __ARGS((int s,void *unused,void *p));
  103.  
  104. static void dumpdomain __ARGS((struct dhdr *dhp,int32 rtt));
  105. static int dns_makequery __ARGS((int16 op,struct rr *rrp,
  106.     char *buffer,int16 buflen));
  107. static void dns_query __ARGS((struct rr *rrlp));
  108.  
  109. static int isaddr __ARGS((char *s));
  110. static struct rr *resolver __ARGS((struct rr *rrlp));
  111.  
  112.  
  113. /**
  114.  **    Domain Resolver Commands
  115.  **/
  116.  
  117. static struct cmds Dcmds[] = {
  118.     "addserver",    dodnsadd,    0, 2, "add <hostid>",
  119.     "dropserver",    dodnsdrop,    0, 2, "drop <hostid>",
  120.     "list",        dodnslist,    0, 0, NULLCHAR,
  121.     "retry",    dodnsretry,    0, 0, NULLCHAR,
  122.     "suffix",    dosuffix,    0, 0, NULLCHAR,
  123.     "trace",    dodnstrace,    0, 0, NULLCHAR,
  124.     "cache",    docache,    0, 0, NULLCHAR,
  125.     NULLCHAR,
  126. };
  127.  
  128. static struct cmds Dcachecmds[] = {
  129.     "clean",    docacheclean,    0, 0, NULLCHAR,
  130.     "list",        docachelist,    0, 0, NULLCHAR,
  131.     "size",        docachesize,    0, 0, NULLCHAR,
  132.     "wait",        docachewait,    0, 0, NULLCHAR,
  133.     NULLCHAR,
  134. };
  135.  
  136. int
  137. dodomain(argc,argv,p)
  138. int argc;
  139. char *argv[];
  140. void *p;
  141. {
  142.     return subcmd(Dcmds,argc,argv,p);
  143. }
  144.  
  145. static int
  146. docache(argc,argv,p)
  147. int argc;
  148. char *argv[];
  149. void *p;
  150. {
  151.     return subcmd(Dcachecmds,argc,argv,p);
  152. }
  153.  
  154. static int
  155. dosuffix(argc,argv,p)
  156. int argc;
  157. char *argv[];
  158. void *p;
  159. {
  160.     if(argc < 2){
  161.         if(Dsuffix != NULLCHAR)
  162.             tprintf("%s\n",Dsuffix);
  163.         return 0;
  164.     }
  165.     free(Dsuffix);
  166.     Dsuffix = strdup(argv[1]);
  167.     return 0;
  168. }
  169.  
  170. static int
  171. docacheclean(argc,argv,p)
  172. int argc;
  173. char *argv[];
  174. void *p;
  175. {
  176.     return setbool( &Dfile_clean, "discard expired records", argc,argv );
  177. }
  178.  
  179. static int
  180. docachelist(argc,argv,p)
  181. int argc;
  182. char *argv[];
  183. void *p;
  184. {
  185.     struct rr *rrp;
  186.  
  187.     (void)dcache_search(NULLRR); /* update ttl */
  188.     rflush();
  189.     for(rrp=Dcache;rrp!=NULLRR;rrp=rrp->next)
  190.     {
  191.         put_rr(stdout,rrp);
  192.     }
  193.     return 0;
  194. }
  195.  
  196. static int
  197. docachesize(argc,argv,p)
  198. int argc;
  199. char *argv[];
  200. void *p;
  201. {
  202.     int newsize;
  203.     int oldsize;
  204.     int result;
  205.  
  206.     newsize = oldsize = Dcache_size;
  207.     result = setint( &newsize, "memory cache size", argc,argv );
  208.  
  209.     if(newsize > 0){
  210.         Dcache_size = newsize;
  211.         if(newsize < oldsize){
  212.             (void)dcache_search(NULLRR); /* update size */
  213.         }
  214.     }
  215.     return result;
  216. }
  217.  
  218. static int
  219. docachewait(argc,argv,p)
  220. int argc;
  221. char *argv[];
  222. void *p;
  223. {
  224.     return setint( &Dfile_wait_relative, "time before file update (seconds)", argc,argv );
  225. }
  226.  
  227. static void
  228. dlist_add(dp)
  229. register struct dserver *dp;
  230. {
  231.     dp->prev = NULLDOM;
  232.     dp->next = Dservers;
  233.     if(Dservers != NULLDOM)
  234.         Dservers->prev = dp;
  235.     Dservers = dp;
  236. }
  237.  
  238. static void
  239. dlist_drop(dp)
  240. register struct dserver *dp;
  241. {
  242.     if(dp->prev != NULLDOM)
  243.         dp->prev->next = dp->next;
  244.     else
  245.         Dservers = dp->next;
  246.     if(dp->next != NULLDOM)
  247.         dp->next->prev = dp->prev;
  248. }
  249.  
  250. static int
  251. dodnsadd(argc,argv,p)
  252. int argc;
  253. char *argv[];
  254. void *p;
  255. {
  256.     struct dserver *dp;
  257.     int32 address;
  258.  
  259.     if((address = resolve(argv[1])) == 0L){
  260.         tprintf("Resolver %s unknown\n",argv[1]);
  261.         return 1;
  262.     }
  263.     dp = (struct dserver *)callocw(1,sizeof(struct dserver));
  264.     dp->address = address;
  265.     dp->srtt = 5000L; /* 5 sec */
  266.     dp->mdev = 0;
  267.     dp->timeout = 2 * dp->mdev + dp->srtt + 3;
  268.     dlist_add(dp);
  269.     return 0;
  270. }
  271.  
  272. static int
  273. dodnsdrop(argc,argv,p)
  274. int argc;
  275. char *argv[];
  276. void *p;
  277. {
  278.     struct dserver *dp;
  279.     int32 addr;
  280.  
  281.     addr = resolve(argv[1]);
  282.     for(dp = Dservers;dp != NULLDOM;dp = dp->next)
  283.         if(addr == dp->address)
  284.             break;
  285.  
  286.     if(dp == NULLDOM){
  287.         tprintf("Not found\n");
  288.         return 1;
  289.     }
  290.  
  291.     dlist_drop(dp);
  292.     free((char *)dp);
  293.     return 0;
  294. }
  295.  
  296. static int
  297. dodnslist(argc,argv,p)
  298. int argc;
  299. char *argv[];
  300. void *p;
  301. {
  302.     register struct dserver *dp;
  303.  
  304.     tprintf("Server address          srtt    mdev   timeout   queries responses\n");
  305.     for(dp = Dservers;dp != NULLDOM;dp = dp->next){
  306.         tprintf("%-20s%8lu%8lu%10lu%10lu%10lu\n",
  307.          inet_ntoa(dp->address),
  308.          dp->srtt,dp->mdev,dp->timeout,
  309.          dp->queries,dp->responses);
  310.     }
  311.     return 0;
  312. }
  313.  
  314. static int
  315. dodnsretry(argc,argv,p)
  316. int argc;
  317. char *argv[];
  318. void *p;
  319. {
  320.     return setint( &Dserver_retries, "server retries", argc,argv );
  321. }
  322.  
  323. static int
  324. dodnstrace(argc,argv,p)
  325. int argc;
  326. char *argv[];
  327. void *p;
  328. {
  329.     return setbool(&Dtrace,"server trace",argc,argv);
  330. }
  331.  
  332.  
  333. /**
  334.  **    Domain Resource Record Utilities
  335.  **/
  336.  
  337. /* check list of resource records for any expired ones.
  338.  * returns number of expired records.
  339.  */
  340. static int
  341. check_ttl(rrlp)
  342. register struct rr *rrlp;
  343. {
  344.     int count = 0;
  345.  
  346.     while(rrlp != NULLRR){
  347.         if(rrlp->ttl == 0L)
  348.             count++;
  349.         rrlp = rrlp->next;
  350.     }
  351.     return count;
  352. }
  353.  
  354. /* Compare two resource records.
  355.  * returns 0 if match, nonzero otherwise.
  356.  */
  357. static int
  358. compare_rr(search_rrp,target_rrp)
  359. register struct rr *search_rrp,*target_rrp;
  360. {
  361.     int i;
  362.  
  363.     if(search_rrp == NULLRR || target_rrp == NULLRR)
  364.         return -32765;
  365.  
  366.     if(search_rrp->class != target_rrp->class)
  367.         return -32763;
  368.     if(search_rrp->type != target_rrp->type
  369.     && (search_rrp->source != RR_QUERY
  370.      || (target_rrp->type != TYPE_CNAME
  371.       && target_rrp->type != TYPE_PTR)))
  372.         return -32761;
  373.  
  374.     if(search_rrp->source != RR_INQUERY){
  375.         if((i = strlen(search_rrp->name)) != strlen(target_rrp->name))
  376.             return -32759;
  377.         if((i = strnicmp(search_rrp->name,target_rrp->name,i)) != 0)
  378.             return i;
  379.  
  380.         /* match negative records so that they are replaced */
  381.         if(target_rrp->rdlength == 0)
  382.             return 0;
  383.     }
  384.  
  385.     /* if a query has gotten this far, match it */
  386.     if(search_rrp->source == RR_QUERY)
  387.         return 0;
  388.  
  389.     /* ensure negative records don't replace older records */
  390.     if(search_rrp->rdlength == 0)
  391.         return -32757;
  392.  
  393.     /* match expired records so that they are replaced */
  394.     if(search_rrp->source != RR_INQUERY){
  395.         if(target_rrp->ttl == 0L)
  396.             return 0;
  397.     }
  398.  
  399.     /* Note: rdlengths are not compared because they vary depending
  400.      * on the representation (ASCII or encoded) this record was
  401.      * generated from.
  402.      */
  403.  
  404.     switch(search_rrp->type){
  405.     case TYPE_A:
  406.         i = search_rrp->rdata.addr != target_rrp->rdata.addr;
  407.         break;
  408.     case TYPE_CNAME:
  409.     case TYPE_MB:
  410.     case TYPE_MG:
  411.     case TYPE_MR:
  412.     case TYPE_NS:
  413.     case TYPE_PTR:
  414.     case TYPE_TXT:
  415.         i = stricmp(search_rrp->rdata.data,target_rrp->rdata.data);
  416.         break;
  417.     case TYPE_HINFO:
  418.         i = strcmp(search_rrp->rdata.hinfo.cpu,target_rrp->rdata.hinfo.cpu) ||
  419.             strcmp(search_rrp->rdata.hinfo.os,target_rrp->rdata.hinfo.os);
  420.         break;
  421.     case TYPE_MX:
  422.         i = stricmp(search_rrp->rdata.mx.exch,target_rrp->rdata.mx.exch);
  423.         break;
  424.     case TYPE_SOA:
  425.         i = search_rrp->rdata.soa.serial != target_rrp->rdata.soa.serial;
  426.         break;
  427.     default:
  428.         i = -32755;    /* unsupported */
  429.     }
  430.     return i;
  431. }
  432.  
  433. static int
  434. compare_rr_list(rrlp,target_rrp)
  435. register struct rr *rrlp,*target_rrp;
  436. {
  437.     while(rrlp != NULLRR){
  438.         if(compare_rr(rrlp,target_rrp) == 0)
  439.             return 0;
  440. #ifdef DEBUG_PAIN
  441.         if(Dtrace)
  442.             printf("%15d %s\n",
  443.                 compare_rr(rrlp,target_rrp),
  444.                 target_rrp->name);
  445. #endif
  446.         rrlp = rrlp->next;
  447.     }
  448.     return -32767;
  449. }
  450.  
  451. /* Make a new copy of a resource record */
  452. static struct rr *
  453. copy_rr(rrp)
  454. register struct rr *rrp;
  455. {
  456.     register struct rr *newrr;
  457.  
  458.     if(rrp == NULLRR)
  459.         return NULLRR;
  460.  
  461.     newrr = (struct rr *)callocw(1,sizeof(struct rr));
  462.     newrr->source =    rrp->source;
  463.     newrr->name =    strdup(rrp->name);
  464.     newrr->type =    rrp->type;
  465.     newrr->class =    rrp->class;
  466.     newrr->ttl =    rrp->ttl;
  467.     if((newrr->rdlength = rrp->rdlength) == 0)
  468.         return newrr;
  469.  
  470.     switch(rrp->type){
  471.     case TYPE_A:
  472.         newrr->rdata.addr = rrp->rdata.addr;
  473.         break;
  474.     case TYPE_CNAME:
  475.     case TYPE_MB:
  476.     case TYPE_MG:
  477.     case TYPE_MR:
  478.     case TYPE_NS:
  479.     case TYPE_PTR:
  480.     case TYPE_TXT:
  481.         newrr->rdata.name = strdup(rrp->rdata.name);
  482.         break;
  483.     case TYPE_HINFO:
  484.         newrr->rdata.hinfo.cpu = strdup(rrp->rdata.hinfo.cpu);
  485.         newrr->rdata.hinfo.os = strdup(rrp->rdata.hinfo.os);
  486.         break;
  487.     case TYPE_MX:
  488.         newrr->rdata.mx.pref = rrp->rdata.mx.pref;
  489.         newrr->rdata.mx.exch = strdup(rrp->rdata.mx.exch);
  490.         break;
  491.     case TYPE_SOA:
  492.         newrr->rdata.soa.mname =     strdup(rrp->rdata.soa.mname);
  493.         newrr->rdata.soa.rname =     strdup(rrp->rdata.soa.rname);
  494.         newrr->rdata.soa.serial =     rrp->rdata.soa.serial;
  495.         newrr->rdata.soa.refresh =     rrp->rdata.soa.refresh;
  496.         newrr->rdata.soa.retry =     rrp->rdata.soa.retry;
  497.         newrr->rdata.soa.expire =     rrp->rdata.soa.expire;
  498.         newrr->rdata.soa.minimum =     rrp->rdata.soa.minimum;
  499.         break;
  500.     }
  501.     return newrr;
  502. }
  503.  
  504. static struct rr *
  505. copy_rr_list(rrlp)
  506. register struct rr *rrlp;
  507. {
  508.     register struct rr **rrpp;
  509.     struct rr *result_rrlp;
  510.  
  511.     rrpp = &result_rrlp;
  512.     while(rrlp != NULLRR){
  513.         *rrpp = copy_rr(rrlp);
  514.         rrpp = &(*rrpp)->next;
  515.         rrlp = rrlp->next;
  516.     }
  517.     *rrpp = NULLRR;
  518.     return result_rrlp;
  519. }
  520.  
  521. /* Free (list of) resource records */
  522. void
  523. free_rr(rrlp)
  524. register struct rr *rrlp;
  525. {
  526.     register struct rr *rrp;
  527.  
  528.     while((rrp = rrlp) != NULLRR){
  529.         rrlp = rrlp->next;
  530.  
  531.         free(rrp->comment);
  532.         free(rrp->name);
  533.         if(rrp->rdlength > 0){
  534.             switch(rrp->type){
  535.             case TYPE_A:
  536.                 break;    /* Nothing allocated in rdata section */
  537.             case TYPE_CNAME:
  538.             case TYPE_MB:
  539.             case TYPE_MG:
  540.             case TYPE_MR:
  541.             case TYPE_NS:
  542.             case TYPE_PTR:
  543.             case TYPE_TXT:
  544.                 free(rrp->rdata.name);
  545.                 break;
  546.             case TYPE_HINFO:
  547.                 free(rrp->rdata.hinfo.cpu);
  548.                 free(rrp->rdata.hinfo.os);
  549.                 break;
  550.             case TYPE_MX:
  551.                 free(rrp->rdata.mx.exch);
  552.                 break;
  553.             case TYPE_SOA:
  554.                 free(rrp->rdata.soa.mname);
  555.                 free(rrp->rdata.soa.rname);
  556.                 break;
  557.             }
  558.         }
  559.         free((char *)rrp);
  560.     }
  561. }
  562.  
  563. static struct rr *
  564. make_rr(source,dname,dclass,dtype,ttl,rdl,data)
  565. int source;
  566. char *dname;
  567. int16 dclass;
  568. int16 dtype;
  569. int32 ttl;
  570. int16 rdl;
  571. void *data;
  572. {
  573.     register struct rr *newrr;
  574.  
  575.     newrr = (struct rr *)callocw(1,sizeof(struct rr));
  576.     newrr->source = source;
  577.     newrr->name = strdup(dname);
  578.     newrr->class = dclass;
  579.     newrr->type = dtype;
  580.     newrr->ttl = ttl;
  581.     if((newrr->rdlength = rdl) == 0)
  582.         return newrr;
  583.  
  584.     switch(dtype){
  585.     case TYPE_A:
  586.       {
  587.         register int32 *ap = (int32 *)data;
  588.         newrr->rdata.addr = *ap;
  589.         break;
  590.       }
  591.     case TYPE_CNAME:
  592.     case TYPE_MB:
  593.     case TYPE_MG:
  594.     case TYPE_MR:
  595.     case TYPE_NS:
  596.     case TYPE_PTR:
  597.     case TYPE_TXT:
  598.       {
  599.         newrr->rdata.name = strdup((char *)data);
  600.         break;
  601.       }
  602.     case TYPE_HINFO:
  603.       {
  604.         register struct hinfo *hinfop = (struct hinfo *)data;
  605.         newrr->rdata.hinfo.cpu = strdup(hinfop->cpu);
  606.         newrr->rdata.hinfo.os = strdup(hinfop->os);
  607.         break;
  608.       }
  609.     case TYPE_MX:
  610.       {
  611.         register struct mx *mxp = (struct mx *)data;
  612.         newrr->rdata.mx.pref = mxp->pref;
  613.         newrr->rdata.mx.exch = strdup(mxp->exch);
  614.         break;
  615.       }
  616.     case TYPE_SOA:
  617.       {
  618.         register struct soa *soap = (struct soa *)data;
  619.         newrr->rdata.soa.mname =     strdup(soap->mname);
  620.         newrr->rdata.soa.rname =     strdup(soap->rname);
  621.         newrr->rdata.soa.serial =     soap->serial;
  622.         newrr->rdata.soa.refresh =     soap->refresh;
  623.         newrr->rdata.soa.retry =     soap->retry;
  624.         newrr->rdata.soa.expire =     soap->expire;
  625.         newrr->rdata.soa.minimum =     soap->minimum;
  626.         break;
  627.       }
  628.     }
  629.     return newrr;
  630. }
  631.  
  632.  
  633. /**
  634.  **    Domain Cache Utilities
  635.  **/
  636.  
  637. static void
  638. dcache_add(rrlp)
  639. register struct rr *rrlp;
  640. {
  641.     register struct rr *last_rrp;
  642.     struct rr *save_rrp;
  643.  
  644.     if(rrlp == NULLRR)
  645.         return;
  646.  
  647.     save_rrp = rrlp;
  648.     last_rrp = NULLRR;
  649.     while(rrlp != NULLRR){
  650.         rrlp->last = last_rrp;
  651.         last_rrp = rrlp;
  652.         rrlp = rrlp->next;
  653.     }
  654.     last_rrp->next = Dcache;
  655.     if(Dcache != NULLRR)
  656.         Dcache->last = last_rrp;
  657.     Dcache = save_rrp;
  658. }
  659.  
  660. static void
  661. dcache_drop(rrp)
  662. register struct rr *rrp;
  663. {
  664.     if(rrp->last != NULLRR)
  665.         rrp->last->next = rrp->next;
  666.     else
  667.         Dcache = rrp->next;
  668.     if(rrp->next != NULLRR)
  669.         rrp->next->last = rrp->last;
  670.     rrp->last =
  671.     rrp->next = NULLRR;
  672. }
  673.  
  674. /* Search cache for resource records, removing them from the cache.
  675.  * Also, timeout cache entries, and trim cache to size.
  676.  * (Calling with NULLRR is legal -- will timeout & trim only.)
  677.  * Note that an answer from the cache cannot be authoritative, because
  678.  * we cannot guarantee that all the entries remain from a previous request.
  679.  * Returns RR list, or NULLRR if no record found.
  680.  */
  681. static struct rr *
  682. dcache_search(rrlp)
  683. struct rr *rrlp;
  684. {
  685.     register struct rr *rrp, *test_rrp;
  686.     struct rr **rrpp, *result_rrlp;
  687.     int32 elapsed;
  688.     time_t now;
  689.     int count = 0;
  690.  
  691. #ifdef DEBUG
  692.     if(Dtrace && rrlp != NULLRR){
  693.         printf("dcache_search: searching for %s\n",rrlp->name);
  694.     }
  695. #endif
  696.  
  697.     elapsed = (int32)(time(&now) - Dcache_time);
  698.     Dcache_time = now;
  699.  
  700.     rrpp = &result_rrlp;
  701.     for(rrp = Dcache; (test_rrp = rrp) != NULLRR;){
  702.         rrp = rrp->next;
  703.                     /* timeout entries */
  704.         if(test_rrp->ttl > 0L
  705.         && (test_rrp->ttl -= elapsed) <= 0L)
  706.             test_rrp->ttl = 0L;
  707.  
  708.         if(compare_rr_list(rrlp,test_rrp) == 0){
  709.             dcache_drop( *rrpp = test_rrp );
  710.             rrpp = &(*rrpp)->next;
  711.         } else if(test_rrp->source == RR_FILE && ++count > Dcache_size){
  712.             dcache_drop(test_rrp);
  713.             free_rr(test_rrp);
  714.         }
  715.     }
  716.     *rrpp = NULLRR;
  717.     return result_rrlp;
  718. }
  719.  
  720. /* Move a list of resource records to the cache, removing duplicates. */
  721. static void
  722. dcache_update(rrlp)
  723. register struct rr *rrlp;
  724. {
  725.     if(rrlp == NULLRR)
  726.         return;
  727.  
  728.     free_rr(dcache_search(rrlp));    /* remove duplicates, first */
  729.     dcache_add(rrlp);
  730. }
  731.  
  732.  
  733. /**
  734.  **    File Utilities
  735.  **/
  736.  
  737. static struct rr *
  738. get_rr(fp,lastrrp)
  739. FILE *fp;
  740. struct rr *lastrrp;
  741. {
  742.     char *line,*lp,*strtok();
  743.     struct rr *rrp;
  744.     char *name,*ttl,*class,*type,*data;
  745.     int i;
  746.  
  747.     line = mallocw(256);
  748.     if(fgets(line,256,fp) == NULL){
  749.         free(line);
  750.         return NULLRR;
  751.     }
  752.  
  753.     rrp = (struct rr *)callocw(1,sizeof(struct rr));
  754.     rrp->source = RR_FILE;
  755.  
  756.     if(line[0] == '\0' || line[0] == '#' || line[0] == ';'){
  757.         rrp->comment = line;
  758.         return rrp;
  759.     }
  760.  
  761.     if(!isspace(line[0]) || lastrrp == NULLRR){
  762.         name = strtok(line,delim);
  763.         lp = NULLCHAR;
  764.     }
  765.     else {    /* Name field is missing */
  766.         name = lastrrp->name;
  767.         lp = line;
  768.     }
  769.     if(name == NULLCHAR || (name != NULLCHAR && (i = strlen(name)) == 0)){
  770.         rrp->comment = strdup("\n");
  771.         free(line);
  772.         return rrp;
  773.     }
  774.  
  775.     if(name[i-1] != '.'){
  776.         /* Tack on a trailing period if it's not there */
  777.         /* !!! need to implement $ORIGIN suffix here */
  778.         rrp->name = mallocw(i+2);
  779.         strcpy(rrp->name,name);
  780.         strcat(rrp->name,".");
  781.     } else
  782.         rrp->name = strdup(name);
  783.  
  784.     ttl = strtok(lp,delim);
  785.  
  786.     if(ttl == NULLCHAR || (!isdigit(ttl[0]) && ttl[0] != '-')){
  787.         /* Optional ttl field is missing */
  788.         rrp->ttl = TTL_MISSING;
  789.         class = ttl;
  790.     } else {
  791.         rrp->ttl = atol(ttl);
  792.         class = strtok(NULLCHAR,delim);
  793.     }
  794.  
  795.     if(class == NULLCHAR){
  796.         /* we're in trouble, but keep processing */
  797.         rrp->class = CLASS_MISSING;
  798.         type = class;
  799.     } else if(class[0] == '<'){
  800.         rrp->class = atoi(&class[1]);
  801.         type = strtok(NULLCHAR,delim);
  802.     } else if(stricmp(class,"IN") == 0){
  803.         rrp->class = CLASS_IN;
  804.         type = strtok(NULLCHAR,delim);
  805.     } else {
  806.         /* Optional class field is missing; assume IN */
  807.         rrp->class = CLASS_IN;
  808.         type = class;
  809.     }
  810.  
  811.     if(type == NULLCHAR){
  812.         /* we're in trouble, but keep processing */
  813.         rrp->type = TYPE_MISSING;
  814.         data = type;
  815.     } else if(type[0] == '{'){
  816.         rrp->type = atoi(&class[1]);
  817.         data = strtok(NULLCHAR,delim);
  818.     } else {
  819.         rrp->type = TYPE_MISSING;
  820.         for(i=1;i<Ndtypes;i++){
  821.             if(stricmp(type,Dtypes[i]) == 0){
  822.                 rrp->type = i;
  823.                 data = strtok(NULLCHAR,delim);
  824.                 break;
  825.             }
  826.         }
  827.     }
  828.  
  829.     if(rrp->type == TYPE_MISSING){
  830.         data = NULLCHAR;
  831.     }
  832.  
  833.     if(data == NULLCHAR){
  834.         /* Empty record, just return */
  835.         free(line);
  836.         return rrp;
  837.     }
  838.     switch(rrp->type){
  839.     case TYPE_A:
  840.         rrp->rdlength = 4;
  841.         rrp->rdata.addr = aton(data);
  842.         break;
  843.     case TYPE_CNAME:
  844.     case TYPE_MB:
  845.     case TYPE_MG:
  846.     case TYPE_MR:
  847.     case TYPE_NS:
  848.     case TYPE_PTR:
  849.     case TYPE_TXT:
  850.         rrp->rdlength = strlen(data);
  851.         rrp->rdata.name = strdup(data);
  852.         break;
  853.     case TYPE_HINFO:
  854.         rrp->rdlength = strlen(data);
  855.         rrp->rdata.hinfo.cpu = strdup(data);
  856.         if((data = strtok(NULLCHAR,delim)) != NULLCHAR){
  857.             rrp->rdlength += strlen(data);
  858.             rrp->rdata.hinfo.os = strdup(data);
  859.         }
  860.         break;
  861.     case TYPE_MX:
  862.         rrp->rdata.mx.pref = atoi(data);
  863.         rrp->rdlength = 2;
  864.  
  865.         /* Get domain name of exchanger */
  866.         if((data = strtok(NULLCHAR,delim)) != NULLCHAR){
  867.             rrp->rdlength += strlen(data);
  868.             rrp->rdata.mx.exch = strdup(data);
  869.         }
  870.         break;
  871.     case TYPE_SOA:
  872.         /* Get domain name of master name server */
  873.         rrp->rdlength = strlen(data);
  874.         rrp->rdata.soa.mname = strdup(data);
  875.  
  876.         /* Get domain name of irresponsible person */
  877.         if((data = strtok(NULLCHAR,delim)) != NULLCHAR){
  878.             rrp->rdata.soa.rname = strdup(data);
  879.             rrp->rdlength += strlen(data);
  880.         }
  881.         data = strtok(NULLCHAR,delim);
  882.         rrp->rdata.soa.serial = atol(data);
  883.         data = strtok(NULLCHAR,delim);
  884.         rrp->rdata.soa.refresh = atol(data);
  885.         data = strtok(NULLCHAR,delim);
  886.         rrp->rdata.soa.retry = atol(data);
  887.         data = strtok(NULLCHAR,delim);
  888.         rrp->rdata.soa.expire = atol(data);
  889.         data = strtok(NULLCHAR,delim);
  890.         rrp->rdata.soa.minimum = atol(data);
  891.         rrp->rdlength += 20;
  892.         break;
  893.     }
  894.  
  895.     /* !!! need to handle trailing comments */
  896.     free(line);
  897.     return rrp;
  898. }
  899.  
  900. /* Print a resource record */
  901. static void
  902. put_rr(fp,rrp)
  903. FILE *fp;
  904. struct rr *rrp;
  905. {
  906.     if(fp == NULLFILE || rrp == NULLRR)
  907.         return;
  908.  
  909.     if(rrp->name == NULLCHAR && rrp->comment != NULLCHAR){
  910.         fprintf(fp,"%s",rrp->comment);
  911.         return;
  912.     }
  913.  
  914.     fprintf(fp,"%s",rrp->name);
  915.     if(rrp->ttl != TTL_MISSING)
  916.         fprintf(fp,"\t%ld",rrp->ttl);
  917.     if(rrp->class == CLASS_IN)
  918.         fprintf(fp,"\tIN");
  919.     else
  920.         fprintf(fp,"\t<%u>",rrp->class);
  921.     if(rrp->type < Ndtypes)
  922.         fprintf(fp,"\t%s",Dtypes[rrp->type]);
  923.     else
  924.         fprintf(fp,"\t{%u}",rrp->type);
  925.     if(rrp->rdlength == 0){
  926.         /* Null data portion, indicates nonexistent record */
  927.         /* or unsupported type.  Hopefully, these will filter */
  928.         /* as time goes by. */
  929.         fprintf(fp,"\n");
  930.         return;
  931.     }
  932.     switch(rrp->type){
  933.     case TYPE_A:
  934.         fprintf(fp,"\t%s\n",inet_ntoa(rrp->rdata.addr));
  935.         break;
  936.     case TYPE_CNAME:
  937.     case TYPE_MB:
  938.     case TYPE_MG:
  939.     case TYPE_MR:
  940.     case TYPE_NS:
  941.     case TYPE_PTR:
  942.     case TYPE_TXT:
  943.         /* These are all printable text strings */
  944.         fprintf(fp,"\t%s\n",rrp->rdata.data);
  945.         break;
  946.     case TYPE_HINFO:
  947.         fprintf(fp,"\t%s\t%s\n",
  948.          rrp->rdata.hinfo.cpu,
  949.          rrp->rdata.hinfo.os);
  950.         break;
  951.     case TYPE_MX:
  952.         fprintf(fp,"\t%u\t%s\n",
  953.          rrp->rdata.mx.pref,
  954.          rrp->rdata.mx.exch);
  955.         break;
  956.     case TYPE_SOA:
  957.         fprintf(fp,"\t%s\t%s\t%lu\t%lu\t%lu\t%lu\t%lu\n",
  958.          rrp->rdata.soa.mname,rrp->rdata.soa.rname,
  959.          rrp->rdata.soa.serial,rrp->rdata.soa.refresh,
  960.          rrp->rdata.soa.retry,rrp->rdata.soa.expire,
  961.          rrp->rdata.soa.minimum);
  962.         break;
  963.     default:
  964.         fprintf(fp,"\n");
  965.         break;
  966.     }
  967. }
  968.  
  969. /* Search local database for resource records.
  970.  * Returns RR list, or NULLRR if no record found.
  971.  */
  972. static struct rr *
  973. dfile_search(rrlp)
  974. struct rr *rrlp;
  975. {
  976.     register struct rr *frrp;
  977.     struct rr **rrpp, *result_rrlp, *oldrrp;
  978.     int32 elapsed;
  979.     FILE *dbase;
  980.     struct stat dstat;
  981.  
  982. #ifdef DEBUG
  983.     if(Dtrace){
  984.         printf("dfile_search: searching for %s\n",rrlp->name);
  985.     }
  986. #endif
  987.  
  988.     while(Dfile_writing > 0)
  989.         pwait(&Dfile_reading);
  990.     Dfile_reading++;
  991.  
  992.     if((dbase = fopen(Dfile,READ_TEXT)) == NULLFILE){
  993.         Dfile_reading--;
  994.         return NULLRR;
  995.     }
  996.     if(fstat(fileno(dbase),&dstat) != 0){
  997.         tprintf("dfile_search: can't get file status\n");
  998.         fclose(dbase);
  999.         Dfile_reading--;
  1000.         return NULLRR;
  1001.     }
  1002.     if((elapsed = (int32)(Dcache_time - (time_t)dstat.st_ctime)) < 0L)
  1003.         elapsed = -elapsed;    /* arbitrary time mismatch */
  1004.  
  1005.     result_rrlp = NULLRR;        /* for contiguous test below */
  1006.     oldrrp = NULLRR;
  1007.     rrpp = &result_rrlp;
  1008.     while((frrp = get_rr(dbase,oldrrp)) != NULLRR){
  1009.         free_rr(oldrrp);
  1010.         if(frrp->type != TYPE_MISSING
  1011.         && frrp->rdlength > 0
  1012.         && compare_rr_list(rrlp,frrp) == 0){
  1013.             if(frrp->ttl > 0L
  1014.             && (frrp->ttl -= elapsed) <= 0L)
  1015.                 frrp->ttl = 0L;
  1016.             *rrpp = frrp;
  1017.             rrpp = &(*rrpp)->next;
  1018.             oldrrp = copy_rr(frrp);
  1019.         } else {
  1020.             oldrrp = frrp;
  1021.             /*
  1022.                 All records of the same name and the same type
  1023.                 are contiguous.  Therefore, for a single query,
  1024.                 we can stop searching.  Multiple queries must
  1025.                 read the whole file.
  1026.             */
  1027.             if(rrlp->next == NULLRR && result_rrlp != NULLRR)
  1028.                 break;
  1029.         }
  1030.         if(!main_exit)
  1031.             pwait(NULL);    /* run multiple sessions */
  1032.     }
  1033.     free_rr(oldrrp);
  1034.     *rrpp = NULLRR;
  1035.  
  1036.     fclose(dbase);
  1037.  
  1038.     if(--Dfile_reading <= 0){
  1039.         Dfile_reading = 0;
  1040.         psignal(&Dfile_writing,0);
  1041.     }
  1042.  
  1043.     return result_rrlp;
  1044. }
  1045.  
  1046. /* Process which will add new resource records from the cache
  1047.  * to the local file, eliminating duplicates while it goes.
  1048.  */
  1049. static void
  1050. dfile_update(s,unused,p)
  1051. int s;
  1052. void *unused;
  1053. void *p;
  1054. {
  1055.     struct rr **rrpp, *rrlp, *oldrrp;
  1056.     char *newname;
  1057.     FILE *old_fp, *new_fp;
  1058.     struct stat old_stat, new_stat;
  1059.  
  1060.     log(-1,"update Domain.txt initiated");
  1061.  
  1062.     close_s(Curproc->input);
  1063.     Curproc->input = -1;
  1064.     close_s(Curproc->output);
  1065.     Curproc->output = -1;
  1066.  
  1067.     newname = strdup(Dfile);
  1068.     strcpy(&newname[strlen(newname)-3],"tmp");
  1069.  
  1070.     while(Dfile_wait_absolute != 0L && !main_exit){
  1071.         register struct rr *frrp;
  1072.         int32 elapsed;
  1073.  
  1074.         while(Dfile_wait_absolute != 0L){
  1075.             elapsed = Dfile_wait_absolute - secclock();
  1076.             Dfile_wait_absolute = 0L;
  1077.             if(elapsed > 0L && !main_exit){
  1078.                 alarm(elapsed*1000);
  1079.                 pwait(&Dfile_wait_absolute);
  1080.                 alarm(0L);
  1081.             }
  1082.         }
  1083.  
  1084.         log(-1,"update Domain.txt");
  1085.  
  1086.         /* create new file for copy */
  1087.         if((new_fp = fopen(newname,WRITE_TEXT)) == NULLFILE){
  1088.             printf("dfile_update: can't create %s!\n",newname);
  1089.             break;
  1090.         }
  1091.         if(fstat(fileno(new_fp),&new_stat) != 0){
  1092.             printf("dfile_update: can't get new_file status!\n");
  1093.             fclose(new_fp);
  1094.             break;
  1095.         }
  1096.  
  1097.         pwait(NULL);    /* file operations can be slow */
  1098.  
  1099.         /* timeout the cache one last time before writing */
  1100.         (void)dcache_search(NULLRR);
  1101.  
  1102.         /* copy new RRs out to the new file */
  1103.         /* (can't wait here, the cache might change) */
  1104.         rrpp = &rrlp;
  1105.         for(frrp = Dcache; frrp != NULLRR; frrp = frrp->next ){
  1106.             switch(frrp->source){
  1107.             case RR_QUESTION:
  1108.             case RR_ANSWER:
  1109.             case RR_AUTHORITY:
  1110.             case RR_ADDITIONAL:
  1111.                 *rrpp = copy_rr(frrp);
  1112.                 if(frrp->type != TYPE_MISSING
  1113.                 && frrp->rdlength > 0)
  1114.                     put_rr(new_fp,frrp);
  1115.                 rrpp = &(*rrpp)->next;
  1116.                 frrp->source = RR_FILE;
  1117.                 break;
  1118.             }
  1119.         }
  1120.         *rrpp = NULLRR;
  1121.  
  1122.         /* open up the old file, concurrently with everyone else */
  1123.         if((old_fp = fopen(Dfile,READ_TEXT)) == NULLFILE){
  1124.             /* great! no old file, so we're ready to go. */
  1125.             fclose(new_fp);
  1126.             rename(newname,Dfile);
  1127.             free_rr(rrlp);
  1128.             break;
  1129.         }
  1130.         if(fstat(fileno(old_fp),&old_stat) != 0){
  1131.             printf("dfile_update: can't get old_file status!\n");
  1132.             fclose(new_fp);
  1133.             fclose(old_fp);
  1134.             free_rr(rrlp);
  1135.             break;
  1136.         }
  1137.         if((elapsed = (int32)(new_stat.st_ctime - old_stat.st_ctime)) < 0L)
  1138.             elapsed = -elapsed;    /* file times are inconsistant */
  1139.  
  1140.         /* Now append any non-duplicate records */
  1141.         oldrrp = NULLRR;
  1142.         while((frrp = get_rr(old_fp,oldrrp)) != NULLRR){
  1143.             free_rr(oldrrp);
  1144.             if(frrp->name == NULLCHAR
  1145.             && frrp->comment != NULLCHAR)
  1146.                 put_rr(new_fp,frrp);
  1147.             if(frrp->type != TYPE_MISSING
  1148.             && frrp->rdlength > 0
  1149.             && compare_rr_list(rrlp,frrp) != 0){
  1150.                 if(frrp->ttl > 0L
  1151.                 && (frrp->ttl -= elapsed) <= 0L)
  1152.                     frrp->ttl = 0L;
  1153.                 if(frrp->ttl != 0 || !Dfile_clean)
  1154.                     put_rr(new_fp,frrp);
  1155.             }
  1156.             oldrrp = frrp;
  1157.             if(!main_exit)
  1158.                 pwait(NULL);    /* run in background */
  1159.         }
  1160.         free_rr(oldrrp);
  1161.         fclose(new_fp);
  1162.         fclose(old_fp);
  1163.         free_rr(rrlp);
  1164.  
  1165.         /* wait for everyone else to finish reading */
  1166.         Dfile_writing++;
  1167.         while(Dfile_reading > 0)
  1168.             pwait(&Dfile_writing);
  1169.  
  1170.         unlink(Dfile);
  1171.         rename(newname,Dfile);
  1172.  
  1173.         Dfile_writing = 0;
  1174.         psignal(&Dfile_reading,0);
  1175.     }
  1176.     free(newname);
  1177.  
  1178.     log(-1,"update Domain.txt finished");
  1179.     Dfile_updater = NULLPROC;
  1180. }
  1181.  
  1182.  
  1183. /**
  1184.  **    Domain Server Utilities
  1185.  **/
  1186.  
  1187. static void
  1188. dumpdomain(dhp,rtt)
  1189. struct dhdr *dhp;
  1190. int32 rtt;
  1191. {
  1192.     struct rr *rrp;
  1193.  
  1194.     printf("response id %u (rtt %lu sec) qr %u opcode %u aa %u tc %u rd %u ra %u rcode %u\n",
  1195.      dhp->id,(long)rtt /1000L,
  1196.      dhp->qr,dhp->opcode,dhp->aa,dhp->tc,dhp->rd,
  1197.      dhp->ra,dhp->rcode);
  1198.     printf("%u questions:\n",dhp->qdcount);
  1199.     for(rrp = dhp->questions; rrp != NULLRR; rrp = rrp->next){
  1200.         printf("%s type %s class %u\n",rrp->name,
  1201.          Dtypes[rrp->type],rrp->class);
  1202.     }
  1203.     printf("%u answers:\n",dhp->ancount);
  1204.     for(rrp = dhp->answers; rrp != NULLRR; rrp = rrp->next){
  1205.         put_rr(stdout,rrp);
  1206.     }
  1207.     printf("%u authority:\n",dhp->nscount);
  1208.     for(rrp = dhp->authority; rrp != NULLRR; rrp = rrp->next){
  1209.         put_rr(stdout,rrp);
  1210.     }
  1211.     printf("%u additional:\n",dhp->arcount);
  1212.     for(rrp = dhp->additional; rrp != NULLRR; rrp = rrp->next){
  1213.         put_rr(stdout,rrp);
  1214.     }
  1215.     fflush(stdout);
  1216. }
  1217.  
  1218. static int
  1219. dns_makequery(op,srrp,buffer,buflen)
  1220. int16 op;    /* operation */
  1221. struct rr *srrp;/* Search RR */
  1222. char *buffer;    /* Area for query */
  1223. int16 buflen;    /* Length of same */
  1224. {
  1225.     char *cp,*cp1;
  1226.     char *dname, *sname;
  1227.     int16 parameter;
  1228.     int16 dlen,len;
  1229.  
  1230.     cp = buffer;
  1231.     /* Use millisecond clock for timestamping */
  1232.     cp = put16(cp,(int16)msclock());
  1233.     parameter = (op << 11)
  1234.             | 0x0100;    /* Recursion desired */
  1235.     cp = put16(cp,parameter);
  1236.     cp = put16(cp,1);
  1237.     cp = put16(cp,0);
  1238.     cp = put16(cp,0);
  1239.     cp = put16(cp,0);
  1240.  
  1241.     sname = strdup(srrp->name);
  1242.     dname = sname;
  1243.     dlen = strlen(dname);
  1244.     for(;;){
  1245.         /* Look for next dot */
  1246.         cp1 = strchr(dname,'.');
  1247.         if(cp1 != NULLCHAR)
  1248.             len = cp1-dname;    /* More to come */
  1249.         else
  1250.             len = dlen;    /* Last component */
  1251.         *cp++ = len;        /* Write length of component */
  1252.         if(len == 0)
  1253.             break;
  1254.         /* Copy component up to (but not including) dot */
  1255.         strncpy(cp,dname,len);
  1256.         cp += len;
  1257.         if(cp1 == NULLCHAR){
  1258.             *cp++ = 0;    /* Last one; write null and finish */
  1259.             break;
  1260.         }
  1261.         dname += len+1;
  1262.         dlen -= len+1;
  1263.     }
  1264.     free(sname);
  1265.     cp = put16(cp,srrp->type);
  1266.     cp = put16(cp,srrp->class);
  1267.     return cp - buffer;
  1268. }
  1269.  
  1270. /* domain server resolution loop
  1271.  * returns: any answers in cache.
  1272.  *    (future features)
  1273.  *    multiple queries.
  1274.  *    inverse queries.
  1275.  */
  1276. static void
  1277. dns_query(rrlp)
  1278. struct rr *rrlp;
  1279. {
  1280.     struct mbuf *bp;
  1281.     struct dhdr *dhp;
  1282.     struct dserver *dp;    /* server list */
  1283.     int32 rtt,abserr;
  1284.     int tried = 0;        /* server list has been retried (count) */
  1285.  
  1286.     if((dp = Dservers) == NULLDOM){
  1287.         return;
  1288.     }
  1289.  
  1290.     for(;;){
  1291.         char *buf;
  1292.         int len;
  1293.         struct sockaddr_in server_in;
  1294.         int s;
  1295.         int rval;
  1296.  
  1297.         dp->queries++;
  1298.  
  1299.         s = socket(AF_INET,SOCK_DGRAM,0);
  1300.         server_in.sin_family = AF_INET;
  1301.         server_in.sin_port = IPPORT_DOMAIN;
  1302.         server_in.sin_addr.s_addr = dp->address;
  1303.  
  1304.         if(Dtrace){
  1305.             printf("dns_query: querying server %s for %s\n",
  1306.              inet_ntoa(dp->address),rrlp->name);
  1307.         }
  1308.  
  1309.         buf = mallocw(512);
  1310.         len = dns_makequery(0,rrlp,buf,512);
  1311.         sendto(s,buf,len,0,(char *)&server_in,sizeof(server_in));
  1312.         free(buf);
  1313.         alarm(max(dp->timeout,100));
  1314.         /* Wait for something to happen */
  1315.         rval = recv_mbuf(s,&bp,0,NULLCHAR,0);
  1316.         alarm(0L);
  1317.         close_s(s);
  1318.  
  1319.         if(Dtrace){
  1320.             printf("dns_query: received message length %d, errno %d\n", rval, errno );
  1321.         }
  1322.  
  1323.         if(rval > 0)
  1324.             break;
  1325.  
  1326.         if(errno == EABORT){
  1327.             return;        /* Killed by "reset" command */
  1328.         }
  1329.  
  1330.         /* Timeout; back off this one and try another server */
  1331.         dp->timeout <<= 1;
  1332.         if((dp = dp->next) == NULLDOM){
  1333.             dp = Dservers;
  1334.             if(Dserver_retries > 0 && ++tried > Dserver_retries)
  1335.                 return;
  1336.         }
  1337.     }
  1338.  
  1339.     /* got a response */
  1340.     dp->responses++;
  1341.     dhp = (struct dhdr *) mallocw(sizeof(struct dhdr));
  1342.     ntohdomain(dhp,&bp);    /* Convert to local format */
  1343.  
  1344.     /* Compute and update the round trip time */
  1345.     rtt = (int32) ((int16)msclock() - dhp->id);
  1346.     abserr = rtt > dp->srtt ? rtt - dp->srtt : dp->srtt - rtt;
  1347.     dp->srtt = ((AGAIN-1) * dp->srtt + rtt + (AGAIN/2)) >> LAGAIN;
  1348.     dp->mdev = ((DGAIN-1) * dp->mdev + abserr + (DGAIN/2)) >> LDGAIN;
  1349.     dp->timeout = 4 * dp->mdev + dp->srtt;
  1350.  
  1351.     /* move to top of list for next time */
  1352.     if(dp->prev != NULLDOM){
  1353.         dlist_drop(dp);
  1354.         dlist_add(dp);
  1355.     }
  1356.  
  1357.     if(Dtrace)
  1358.         dumpdomain(dhp,rtt);
  1359.  
  1360.     /* Add negative reply to answers.  This assumes that there was
  1361.      * only one question, which is true for all questions we send.
  1362.      */
  1363.     if(dhp->aa && (dhp->rcode == NAME_ERROR || dhp->ancount == 0)){
  1364.         register struct rr *rrp;
  1365.         long ttl = 600L; /* Default TTL for negative records */
  1366.  
  1367.         /* look for SOA ttl */
  1368.         for(rrp = dhp->authority; rrp != NULLRR; rrp = rrp->next){
  1369.             if(rrp->type == TYPE_SOA)
  1370.                 ttl = rrp->ttl;
  1371.         }
  1372.  
  1373.         /* make the questions the negative answers */
  1374.         for(rrp = dhp->questions; rrp != NULLRR; rrp = rrp->next)
  1375.             rrp->ttl = ttl;
  1376.     } else {
  1377.         free_rr(dhp->questions);
  1378.         dhp->questions = NULLRR;
  1379.     }
  1380.  
  1381.     /* post in reverse order to maintain original order */
  1382.     dcache_update(dhp->additional);
  1383.     dcache_update(dhp->authority);
  1384.     dcache_update(dhp->answers);
  1385.     dcache_update(dhp->questions);
  1386.  
  1387.     Dfile_wait_absolute = secclock() + Dfile_wait_relative;
  1388.     if(Dfile_updater == NULLPROC){
  1389.         Dfile_updater = newproc("domain update",
  1390.             512,dfile_update,0,NULL,NULL,0);
  1391.     }
  1392.  
  1393. #ifdef DEBUG
  1394.     if(Dtrace)
  1395.         keywait(NULLCHAR,1);    /* so we can look around */
  1396. #endif
  1397.     free((char *)dhp);
  1398.     return;
  1399. }
  1400.  
  1401.  
  1402. /**
  1403.  **    Resolver Utilities
  1404.  **/
  1405.  
  1406. /* Return TRUE if string appears to be an IP address in dotted decimal;
  1407.  * return FALSE otherwise (i.e., if string is a domain name)
  1408.  */
  1409. static int
  1410. isaddr(s)
  1411. register char *s;
  1412. {
  1413.     char c;
  1414.  
  1415.     if(s == NULLCHAR)
  1416.         return TRUE;       /* Can't happen */
  1417.  
  1418.     while((c = *s++) != '\0'){
  1419.         if(c != '[' && c != ']' && !isdigit(c) && c != '.')
  1420.             return FALSE;
  1421.     }
  1422.     return TRUE;
  1423. }
  1424.  
  1425. /* Search for resource records.
  1426.  * Returns RR list, or NULLRR if no record found.
  1427.  */
  1428. static struct rr *
  1429. resolver(rrlp)
  1430. register struct rr *rrlp;
  1431. {
  1432.     register struct rr *result_rrlp;
  1433.  
  1434.     if((result_rrlp = dcache_search(rrlp)) == NULLRR){
  1435.         result_rrlp = dfile_search(rrlp);
  1436.     }
  1437.     if(result_rrlp == NULLRR || check_ttl(result_rrlp) != 0){
  1438.         dcache_add(result_rrlp);     /* save any expired RRs */
  1439.         dns_query(rrlp);
  1440.         result_rrlp = dcache_search(rrlp);
  1441.     }
  1442.     dcache_add(copy_rr_list(result_rrlp));
  1443.     return result_rrlp;
  1444. }
  1445.  
  1446. /* general entry point for address -> domain name resolution.
  1447.  * Returns RR list, or NULLRR if no record found.
  1448.  */
  1449. struct rr *
  1450. inverse_a(ip_address)
  1451. int32 ip_address;
  1452. {
  1453.     struct rr *prrp;
  1454.     struct rr *result_rrlp;
  1455.     char pname[30];
  1456.  
  1457.     if(ip_address == 0L)
  1458.         return NULLRR;
  1459.  
  1460.     sprintf( pname, "%u.%u.%u.%u.IN-ADDR.ARPA.",
  1461.             lobyte(loword(ip_address)),
  1462.             hibyte(loword(ip_address)),
  1463.             lobyte(hiword(ip_address)),
  1464.             hibyte(hiword(ip_address)) );
  1465.  
  1466.     prrp = make_rr(RR_QUERY,pname,CLASS_IN,TYPE_PTR,0,0,NULL);
  1467.  
  1468.     prrp->next =         /* make list to speed search */
  1469.         make_rr(RR_INQUERY,NULLCHAR,CLASS_IN,TYPE_A,0,4,&ip_address);
  1470.  
  1471.     result_rrlp = resolver(prrp);
  1472.  
  1473.     free_rr(prrp);
  1474.     return result_rrlp;
  1475. }
  1476.  
  1477. /* general entry point for domain name -> resource resolution.
  1478.  * Returns RR list, or NULLRR if no record found.
  1479.  */
  1480. struct rr *
  1481. resolve_rr(dname,dtype)
  1482. char *dname;
  1483. int16 dtype;
  1484. {
  1485.     struct rr *qrrp;
  1486.     struct rr *result_rrlp;
  1487.     char *sname, *tname;
  1488.     int looping = MAXCNAME;
  1489.  
  1490.     if(dname == NULLCHAR)
  1491.         return NULLRR;
  1492.  
  1493.     sname = strdup(dname);
  1494.     if(strchr(sname,'.') == NULLCHAR && Dsuffix != NULLCHAR){
  1495.         /* Append default suffix */
  1496.         tname = mallocw(strlen(sname)+strlen(Dsuffix)+2);
  1497.         sprintf(tname,"%s.%s",sname,Dsuffix);
  1498.         free(sname);
  1499.         sname = tname;
  1500.     }
  1501.     if(sname[strlen(sname)-1] != '.'){
  1502.         /* Append trailing dot */
  1503.         tname = mallocw(strlen(sname)+2);
  1504.         sprintf(tname,"%s.",sname);
  1505.         free(sname);
  1506.         sname = tname;
  1507.     }
  1508.  
  1509.     qrrp = make_rr(RR_QUERY,sname,CLASS_IN,dtype,0,0,NULL);
  1510.     free(sname);
  1511.  
  1512.     while(looping > 0){
  1513.         if((result_rrlp=resolver(qrrp)) == NULLRR
  1514.         || result_rrlp->type == dtype)
  1515.             break;
  1516. #ifdef DEBUG
  1517.         if(Dtrace)
  1518.             put_rr(stdout,result_rrlp);
  1519. #endif
  1520.         /* Should be CNAME or PTR record */
  1521.         /* Replace name and try again */
  1522.         free(qrrp->name);
  1523.         qrrp->name = strdup(result_rrlp->rdata.name);
  1524.         free_rr(result_rrlp);
  1525.         result_rrlp = NULLRR;
  1526.         looping--;
  1527.     }
  1528.     free_rr(qrrp);
  1529.     return result_rrlp;
  1530. }
  1531.  
  1532. /* main entry point for address -> domain name resolution.
  1533.  * Returns string, or NULLCHAR if no name found.
  1534.  */
  1535. char *
  1536. resolve_a(ip_address,shorten)
  1537. int32 ip_address;        /* search address */
  1538. int shorten;            /* return only first part of name (flag)*/
  1539. {
  1540.     struct rr *save_rrlp, *rrlp;
  1541.     char *result = NULLCHAR;
  1542.  
  1543.     for( rrlp = save_rrlp = inverse_a(ip_address);
  1544.          rrlp != NULLRR && result == NULLCHAR;
  1545.          rrlp = rrlp->next ){
  1546.         if(rrlp->rdlength > 0){
  1547.             switch(rrlp->type){
  1548.             case TYPE_PTR:
  1549.                 result = strdup(rrlp->rdata.name);
  1550.                 break;
  1551.             case TYPE_A:
  1552.                 result = strdup(rrlp->name);
  1553.                 break;
  1554.             }
  1555.         }
  1556.     }
  1557.     free_rr(save_rrlp);
  1558.  
  1559.     if(result != NULLCHAR && shorten){
  1560.         int dot;
  1561.         char *shortened;
  1562.  
  1563.         if((dot = strcspn(result, ".")) == 0){
  1564.             shortened = mallocw(dot+1);
  1565.             strncpy(shortened, result, dot);
  1566.             shortened[dot] = '\0';
  1567.             free(result);
  1568.             result = shortened;
  1569.         }
  1570.     }
  1571.     return result;
  1572. }
  1573.  
  1574. /* Main entry point for domain name -> address resolution.
  1575.  * Returns 0 if name is currently unresolvable.
  1576.  */
  1577. int32
  1578. resolve(name)
  1579. char *name;
  1580. {
  1581.     register struct rr *rrlp;
  1582.     int32 ip_address = 0;
  1583.  
  1584.     if(name == NULLCHAR)
  1585.         return 0;
  1586.  
  1587.     if(isaddr(name))
  1588.         return aton(name);
  1589.  
  1590.     if((rrlp = resolve_rr(name,TYPE_A)) != NULLRR
  1591.      && rrlp->rdlength > 0)
  1592.         ip_address = rrlp->rdata.addr;
  1593.  
  1594.     /* multi-homed hosts are handled here */
  1595.     if(rrlp != NULLRR && rrlp->next != NULLRR) {
  1596.         register struct rr *rrp;
  1597.         register struct route *rp;
  1598.         int16 cost = MAXINT16;
  1599.         rrp = rrlp;
  1600.         while(rrp != NULLRR) { /* choose the best of a set of routes */
  1601.             if(rrp->rdlength > 0 &&
  1602.                (rp = rt_lookup(rrp->rdata.addr)) != NULLROUTE &&
  1603.                rp->metric <= cost) {
  1604.                 ip_address = rrp->rdata.addr;
  1605.                 cost = rp->metric;
  1606.             }
  1607.             rrp = rrp->next;
  1608.         }
  1609.     }
  1610.  
  1611.     free_rr(rrlp);
  1612.     return ip_address;
  1613. }
  1614.  
  1615.  
  1616. /* Main entry point for MX record lookup.
  1617.  * Returns 0 if name is currently unresolvable.
  1618.  */
  1619. int32
  1620. resolve_mx(name)
  1621. char *name;
  1622. {
  1623.     register struct rr *rrp, *arrp;
  1624.     char *sname, *tmp, *cp;
  1625.     int32 addr, ip_address = 0;
  1626.     int16 pref = MAXINT16;
  1627.  
  1628.     if(name == NULLCHAR)
  1629.         return 0;
  1630.  
  1631.     if(isaddr(name)){
  1632.         if((sname = resolve_a(aton(name),FALSE)) == NULLCHAR)
  1633.             return 0;
  1634.     }
  1635.     else
  1636.         sname = strdup(name);
  1637.  
  1638.     cp = sname;
  1639.     while(1){
  1640.         rrp = arrp = resolve_rr(sname,TYPE_MX);
  1641.         /* Search this list of rr's for an MX record */
  1642.         while(rrp != NULLRR){
  1643.             if(rrp->rdlength > 0 && rrp->rdata.mx.pref <= pref &&
  1644.                (addr = resolve(rrp->rdata.mx.exch)) != 0L){
  1645.                 pref = rrp->rdata.mx.pref;
  1646.                 ip_address = addr;
  1647.             }
  1648.             rrp = rrp->next;
  1649.         }
  1650.         free_rr(arrp);
  1651.         if(ip_address != 0)
  1652.             break;
  1653.         /* Compose wild card one level up */
  1654.         if((cp = strchr(cp,'.')) == NULLCHAR)
  1655.             break;
  1656.         tmp = mallocw(strlen(cp)+2);
  1657.         sprintf(tmp,"*%s",cp);        /* wildcard expansion */
  1658.         free(sname);
  1659.         sname = tmp;
  1660.         cp = sname + 2;
  1661.     }
  1662.     free(sname);
  1663.     return ip_address;
  1664. }
  1665.  
  1666. /* Search for local records of the MB, MG and MR type. Returns list of
  1667.  * matching records.
  1668.  */
  1669. struct rr *
  1670. resolve_mailb(name)
  1671. char *name;        /* local username, without trailing dot */
  1672. {
  1673.     register struct rr *result_rrlp;
  1674.     struct rr *rrlp;
  1675.     char *sname;
  1676.  
  1677.     /* Append trailing dot */
  1678.     sname = mallocw(strlen(name)+2);
  1679.     sprintf(sname,"%s.",name);
  1680.     rrlp = make_rr(RR_QUERY,sname,CLASS_IN,TYPE_MB,0,0,NULL);
  1681.     rrlp->next = make_rr(RR_QUERY,sname,CLASS_IN,TYPE_MG,0,0,NULL);
  1682.     rrlp->next->next = make_rr(RR_QUERY,sname,CLASS_IN,TYPE_MR,0,0,NULL);
  1683.     free(sname);
  1684.     if((result_rrlp = dcache_search(rrlp)) == NULLRR){
  1685.         result_rrlp = dfile_search(rrlp);
  1686.     }
  1687.     free_rr(rrlp);
  1688.     if(Dsuffix != NULLCHAR){
  1689.         rrlp = result_rrlp;
  1690.         while(rrlp != NULLRR){    /* add domain suffix to data */
  1691.             if(rrlp->rdlength > 0 &&
  1692.                rrlp->rdata.name[rrlp->rdlength-1] != '.'){
  1693.                 sname = mallocw(rrlp->rdlength +
  1694.                     strlen(Dsuffix)+2);
  1695.                 sprintf(sname,"%s.%s",rrlp->rdata.name,Dsuffix);
  1696.                 free(rrlp->rdata.name);
  1697.                 rrlp->rdata.name = sname;
  1698.                 rrlp->rdlength = strlen(sname);
  1699.             }
  1700.             rrlp = rrlp->next;
  1701.         }
  1702.     }
  1703.     dcache_add(copy_rr_list(result_rrlp));
  1704.     return result_rrlp;
  1705. }
  1706.