home *** CD-ROM | disk | FTP | other *** search
/ Media Share 9 / MEDIASHARE_09.ISO / hamradio / s920603.zip / DOMAIN.C < prev    next >
C/C++ Source or Header  |  1992-05-12  |  42KB  |  1,845 lines

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