home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
HAM Radio 1
/
HamRadio.cdr
/
misc
/
src0131
/
domain.c
< prev
next >
Wrap
C/C++ Source or Header
|
1991-01-26
|
40KB
|
1,706 lines
/*
* DOMAIN.C -- domain name system stub resolver
*
* Original code by Phil Karn, KA9Q.
*
* 04-90 -- Bill Simpson added address->name resolution, time-to-live,
* thru memory caching, generalized multi-record multi-type searches,
* 10-90 and many minor changes to conform more closely to the RFCs.
*/
#include <stdio.h>
#include <ctype.h>
#include <time.h>
#include <sys/stat.h>
#include "global.h"
#include "mbuf.h"
#include "netuser.h"
#include "ip.h"
#include "socket.h"
#include "cmdparse.h"
#include "proc.h"
#include "domain.h"
#include "commands.h"
#include "files.h"
#undef DEBUG /* for certain trace messages */
#undef DEBUG_PAIN /* for painful debugging */
extern int main_exit; /* from main program (flag) */
static struct rr *Dcache = NULLRR; /* Cache of resource records */
static int Dcache_size = 20; /* size limit */
static time_t Dcache_time = 0L; /* timestamp */
static int Dfile_clean = FALSE; /* discard expired records (flag) */
static int Dfile_reading = 0; /* read interlock (count) */
static int Dfile_writing = 0; /* write interlock (count) */
struct proc *Dfile_updater = NULLPROC;
static int32 Dfile_wait_absolute = 0L; /* timeout Clock time */
static int Dfile_wait_relative = 300; /* timeout file activity (seconds) */
static struct dserver *Dservers = NULLDOM; /* List of potential servers */
static int Dserver_retries = 2; /* Attempts to reach servers */
static char *Dsuffix = NULLCHAR; /* Default suffix for names without periods */
static int Dtrace = FALSE;
static char *Dtypes[] = {
"",
"A",
"NS",
"MD",
"MF",
"CNAME",
"SOA",
"MB",
"MG",
"MR",
"NULL",
"WKS",
"PTR",
"HINFO",
"MINFO",
"MX",
"TXT"
};
static int Ndtypes = 17;
static char delim[] = " \t\r\n";
static int docache __ARGS((int argc,char *argv[],void *p));
static int dosuffix __ARGS((int argc,char *argv[],void *p));
static int docacheclean __ARGS((int argc,char *argv[],void *p));
static int docachelist __ARGS((int argc,char *argv[],void *p));
static int docachesize __ARGS((int argc,char *argv[],void *p));
static int docachewait __ARGS((int argc,char *argv[],void *p));
static void dlist_add __ARGS((struct dserver *dp));
static void dlist_drop __ARGS((struct dserver *dp));
static int dodnsadd __ARGS((int argc,char *argv[],void *p));
static int dodnsdrop __ARGS((int argc,char *argv[],void *p));
static int dodnslist __ARGS((int argc,char *argv[],void *p));
static int dodnsretry __ARGS((int argc,char *argv[],void *p));
static int dodnstrace __ARGS((int argc,char *argv[],void *p));
static int check_ttl __ARGS((struct rr *rrlp));
static int compare_rr __ARGS((struct rr *search_rrp,struct rr *target_rrp));
static int compare_rr_list __ARGS((struct rr *rrlp,struct rr *target_rrp));
static struct rr *copy_rr __ARGS((struct rr *rrp));
static struct rr *copy_rr_list __ARGS((struct rr *rrlp));
static struct rr *make_rr __ARGS((int source,
char *dname,int16 class,int16 type,int32 ttl,int16 rdl,void *data));
static void dcache_add __ARGS((struct rr *rrlp));
static void dcache_drop __ARGS((struct rr *rrp));
static struct rr *dcache_search __ARGS((struct rr *rrlp));
static void dcache_update __ARGS((struct rr *rrlp));
static struct rr *get_rr __ARGS((FILE *fp, struct rr *lastrrp));
static void put_rr __ARGS((FILE *fp,struct rr *rrp));
static struct rr *dfile_search __ARGS((struct rr *rrlp));
static void dfile_update __ARGS((int s,void *unused,void *p));
static void dumpdomain __ARGS((struct dhdr *dhp,int32 rtt));
static int dns_makequery __ARGS((int16 op,struct rr *rrp,
char *buffer,int16 buflen));
static void dns_query __ARGS((struct rr *rrlp));
static int isaddr __ARGS((char *s));
static struct rr *resolver __ARGS((struct rr *rrlp));
/**
** Domain Resolver Commands
**/
static struct cmds Dcmds[] = {
"addserver", dodnsadd, 0, 2, "add <hostid>",
"dropserver", dodnsdrop, 0, 2, "drop <hostid>",
"list", dodnslist, 0, 0, NULLCHAR,
"retry", dodnsretry, 0, 0, NULLCHAR,
"suffix", dosuffix, 0, 0, NULLCHAR,
"trace", dodnstrace, 0, 0, NULLCHAR,
"cache", docache, 0, 0, NULLCHAR,
NULLCHAR,
};
static struct cmds Dcachecmds[] = {
"clean", docacheclean, 0, 0, NULLCHAR,
"list", docachelist, 0, 0, NULLCHAR,
"size", docachesize, 0, 0, NULLCHAR,
"wait", docachewait, 0, 0, NULLCHAR,
NULLCHAR,
};
int
dodomain(argc,argv,p)
int argc;
char *argv[];
void *p;
{
return subcmd(Dcmds,argc,argv,p);
}
static int
docache(argc,argv,p)
int argc;
char *argv[];
void *p;
{
return subcmd(Dcachecmds,argc,argv,p);
}
static int
dosuffix(argc,argv,p)
int argc;
char *argv[];
void *p;
{
if(argc < 2){
if(Dsuffix != NULLCHAR)
tprintf("%s\n",Dsuffix);
return 0;
}
free(Dsuffix);
Dsuffix = strdup(argv[1]);
return 0;
}
static int
docacheclean(argc,argv,p)
int argc;
char *argv[];
void *p;
{
return setbool( &Dfile_clean, "discard expired records", argc,argv );
}
static int
docachelist(argc,argv,p)
int argc;
char *argv[];
void *p;
{
struct rr *rrp;
(void)dcache_search(NULLRR); /* update ttl */
rflush();
for(rrp=Dcache;rrp!=NULLRR;rrp=rrp->next)
{
put_rr(stdout,rrp);
}
return 0;
}
static int
docachesize(argc,argv,p)
int argc;
char *argv[];
void *p;
{
int newsize;
int oldsize;
int result;
newsize = oldsize = Dcache_size;
result = setint( &newsize, "memory cache size", argc,argv );
if(newsize > 0){
Dcache_size = newsize;
if(newsize < oldsize){
(void)dcache_search(NULLRR); /* update size */
}
}
return result;
}
static int
docachewait(argc,argv,p)
int argc;
char *argv[];
void *p;
{
return setint( &Dfile_wait_relative, "time before file update (seconds)", argc,argv );
}
static void
dlist_add(dp)
register struct dserver *dp;
{
dp->prev = NULLDOM;
dp->next = Dservers;
if(Dservers != NULLDOM)
Dservers->prev = dp;
Dservers = dp;
}
static void
dlist_drop(dp)
register struct dserver *dp;
{
if(dp->prev != NULLDOM)
dp->prev->next = dp->next;
else
Dservers = dp->next;
if(dp->next != NULLDOM)
dp->next->prev = dp->prev;
}
static int
dodnsadd(argc,argv,p)
int argc;
char *argv[];
void *p;
{
struct dserver *dp;
int32 address;
if((address = resolve(argv[1])) == 0L){
tprintf("Resolver %s unknown\n",argv[1]);
return 1;
}
dp = (struct dserver *)callocw(1,sizeof(struct dserver));
dp->address = address;
dp->srtt = 5000L; /* 5 sec */
dp->mdev = 0;
dp->timeout = 2 * dp->mdev + dp->srtt + 3;
dlist_add(dp);
return 0;
}
static int
dodnsdrop(argc,argv,p)
int argc;
char *argv[];
void *p;
{
struct dserver *dp;
int32 addr;
addr = resolve(argv[1]);
for(dp = Dservers;dp != NULLDOM;dp = dp->next)
if(addr == dp->address)
break;
if(dp == NULLDOM){
tprintf("Not found\n");
return 1;
}
dlist_drop(dp);
free((char *)dp);
return 0;
}
static int
dodnslist(argc,argv,p)
int argc;
char *argv[];
void *p;
{
register struct dserver *dp;
tprintf("Server address srtt mdev timeout queries responses\n");
for(dp = Dservers;dp != NULLDOM;dp = dp->next){
tprintf("%-20s%8lu%8lu%10lu%10lu%10lu\n",
inet_ntoa(dp->address),
dp->srtt,dp->mdev,dp->timeout,
dp->queries,dp->responses);
}
return 0;
}
static int
dodnsretry(argc,argv,p)
int argc;
char *argv[];
void *p;
{
return setint( &Dserver_retries, "server retries", argc,argv );
}
static int
dodnstrace(argc,argv,p)
int argc;
char *argv[];
void *p;
{
return setbool(&Dtrace,"server trace",argc,argv);
}
/**
** Domain Resource Record Utilities
**/
/* check list of resource records for any expired ones.
* returns number of expired records.
*/
static int
check_ttl(rrlp)
register struct rr *rrlp;
{
int count = 0;
while(rrlp != NULLRR){
if(rrlp->ttl == 0L)
count++;
rrlp = rrlp->next;
}
return count;
}
/* Compare two resource records.
* returns 0 if match, nonzero otherwise.
*/
static int
compare_rr(search_rrp,target_rrp)
register struct rr *search_rrp,*target_rrp;
{
int i;
if(search_rrp == NULLRR || target_rrp == NULLRR)
return -32765;
if(search_rrp->class != target_rrp->class)
return -32763;
if(search_rrp->type != target_rrp->type
&& (search_rrp->source != RR_QUERY
|| (target_rrp->type != TYPE_CNAME
&& target_rrp->type != TYPE_PTR)))
return -32761;
if(search_rrp->source != RR_INQUERY){
if((i = strlen(search_rrp->name)) != strlen(target_rrp->name))
return -32759;
if((i = strnicmp(search_rrp->name,target_rrp->name,i)) != 0)
return i;
/* match negative records so that they are replaced */
if(target_rrp->rdlength == 0)
return 0;
}
/* if a query has gotten this far, match it */
if(search_rrp->source == RR_QUERY)
return 0;
/* ensure negative records don't replace older records */
if(search_rrp->rdlength == 0)
return -32757;
/* match expired records so that they are replaced */
if(search_rrp->source != RR_INQUERY){
if(target_rrp->ttl == 0L)
return 0;
}
/* Note: rdlengths are not compared because they vary depending
* on the representation (ASCII or encoded) this record was
* generated from.
*/
switch(search_rrp->type){
case TYPE_A:
i = search_rrp->rdata.addr != target_rrp->rdata.addr;
break;
case TYPE_CNAME:
case TYPE_MB:
case TYPE_MG:
case TYPE_MR:
case TYPE_NS:
case TYPE_PTR:
case TYPE_TXT:
i = stricmp(search_rrp->rdata.data,target_rrp->rdata.data);
break;
case TYPE_HINFO:
i = strcmp(search_rrp->rdata.hinfo.cpu,target_rrp->rdata.hinfo.cpu) ||
strcmp(search_rrp->rdata.hinfo.os,target_rrp->rdata.hinfo.os);
break;
case TYPE_MX:
i = stricmp(search_rrp->rdata.mx.exch,target_rrp->rdata.mx.exch);
break;
case TYPE_SOA:
i = search_rrp->rdata.soa.serial != target_rrp->rdata.soa.serial;
break;
default:
i = -32755; /* unsupported */
}
return i;
}
static int
compare_rr_list(rrlp,target_rrp)
register struct rr *rrlp,*target_rrp;
{
while(rrlp != NULLRR){
if(compare_rr(rrlp,target_rrp) == 0)
return 0;
#ifdef DEBUG_PAIN
if(Dtrace)
printf("%15d %s\n",
compare_rr(rrlp,target_rrp),
target_rrp->name);
#endif
rrlp = rrlp->next;
}
return -32767;
}
/* Make a new copy of a resource record */
static struct rr *
copy_rr(rrp)
register struct rr *rrp;
{
register struct rr *newrr;
if(rrp == NULLRR)
return NULLRR;
newrr = (struct rr *)callocw(1,sizeof(struct rr));
newrr->source = rrp->source;
newrr->name = strdup(rrp->name);
newrr->type = rrp->type;
newrr->class = rrp->class;
newrr->ttl = rrp->ttl;
if((newrr->rdlength = rrp->rdlength) == 0)
return newrr;
switch(rrp->type){
case TYPE_A:
newrr->rdata.addr = rrp->rdata.addr;
break;
case TYPE_CNAME:
case TYPE_MB:
case TYPE_MG:
case TYPE_MR:
case TYPE_NS:
case TYPE_PTR:
case TYPE_TXT:
newrr->rdata.name = strdup(rrp->rdata.name);
break;
case TYPE_HINFO:
newrr->rdata.hinfo.cpu = strdup(rrp->rdata.hinfo.cpu);
newrr->rdata.hinfo.os = strdup(rrp->rdata.hinfo.os);
break;
case TYPE_MX:
newrr->rdata.mx.pref = rrp->rdata.mx.pref;
newrr->rdata.mx.exch = strdup(rrp->rdata.mx.exch);
break;
case TYPE_SOA:
newrr->rdata.soa.mname = strdup(rrp->rdata.soa.mname);
newrr->rdata.soa.rname = strdup(rrp->rdata.soa.rname);
newrr->rdata.soa.serial = rrp->rdata.soa.serial;
newrr->rdata.soa.refresh = rrp->rdata.soa.refresh;
newrr->rdata.soa.retry = rrp->rdata.soa.retry;
newrr->rdata.soa.expire = rrp->rdata.soa.expire;
newrr->rdata.soa.minimum = rrp->rdata.soa.minimum;
break;
}
return newrr;
}
static struct rr *
copy_rr_list(rrlp)
register struct rr *rrlp;
{
register struct rr **rrpp;
struct rr *result_rrlp;
rrpp = &result_rrlp;
while(rrlp != NULLRR){
*rrpp = copy_rr(rrlp);
rrpp = &(*rrpp)->next;
rrlp = rrlp->next;
}
*rrpp = NULLRR;
return result_rrlp;
}
/* Free (list of) resource records */
void
free_rr(rrlp)
register struct rr *rrlp;
{
register struct rr *rrp;
while((rrp = rrlp) != NULLRR){
rrlp = rrlp->next;
free(rrp->comment);
free(rrp->name);
if(rrp->rdlength > 0){
switch(rrp->type){
case TYPE_A:
break; /* Nothing allocated in rdata section */
case TYPE_CNAME:
case TYPE_MB:
case TYPE_MG:
case TYPE_MR:
case TYPE_NS:
case TYPE_PTR:
case TYPE_TXT:
free(rrp->rdata.name);
break;
case TYPE_HINFO:
free(rrp->rdata.hinfo.cpu);
free(rrp->rdata.hinfo.os);
break;
case TYPE_MX:
free(rrp->rdata.mx.exch);
break;
case TYPE_SOA:
free(rrp->rdata.soa.mname);
free(rrp->rdata.soa.rname);
break;
}
}
free((char *)rrp);
}
}
static struct rr *
make_rr(source,dname,dclass,dtype,ttl,rdl,data)
int source;
char *dname;
int16 dclass;
int16 dtype;
int32 ttl;
int16 rdl;
void *data;
{
register struct rr *newrr;
newrr = (struct rr *)callocw(1,sizeof(struct rr));
newrr->source = source;
newrr->name = strdup(dname);
newrr->class = dclass;
newrr->type = dtype;
newrr->ttl = ttl;
if((newrr->rdlength = rdl) == 0)
return newrr;
switch(dtype){
case TYPE_A:
{
register int32 *ap = (int32 *)data;
newrr->rdata.addr = *ap;
break;
}
case TYPE_CNAME:
case TYPE_MB:
case TYPE_MG:
case TYPE_MR:
case TYPE_NS:
case TYPE_PTR:
case TYPE_TXT:
{
newrr->rdata.name = strdup((char *)data);
break;
}
case TYPE_HINFO:
{
register struct hinfo *hinfop = (struct hinfo *)data;
newrr->rdata.hinfo.cpu = strdup(hinfop->cpu);
newrr->rdata.hinfo.os = strdup(hinfop->os);
break;
}
case TYPE_MX:
{
register struct mx *mxp = (struct mx *)data;
newrr->rdata.mx.pref = mxp->pref;
newrr->rdata.mx.exch = strdup(mxp->exch);
break;
}
case TYPE_SOA:
{
register struct soa *soap = (struct soa *)data;
newrr->rdata.soa.mname = strdup(soap->mname);
newrr->rdata.soa.rname = strdup(soap->rname);
newrr->rdata.soa.serial = soap->serial;
newrr->rdata.soa.refresh = soap->refresh;
newrr->rdata.soa.retry = soap->retry;
newrr->rdata.soa.expire = soap->expire;
newrr->rdata.soa.minimum = soap->minimum;
break;
}
}
return newrr;
}
/**
** Domain Cache Utilities
**/
static void
dcache_add(rrlp)
register struct rr *rrlp;
{
register struct rr *last_rrp;
struct rr *save_rrp;
if(rrlp == NULLRR)
return;
save_rrp = rrlp;
last_rrp = NULLRR;
while(rrlp != NULLRR){
rrlp->last = last_rrp;
last_rrp = rrlp;
rrlp = rrlp->next;
}
last_rrp->next = Dcache;
if(Dcache != NULLRR)
Dcache->last = last_rrp;
Dcache = save_rrp;
}
static void
dcache_drop(rrp)
register struct rr *rrp;
{
if(rrp->last != NULLRR)
rrp->last->next = rrp->next;
else
Dcache = rrp->next;
if(rrp->next != NULLRR)
rrp->next->last = rrp->last;
rrp->last =
rrp->next = NULLRR;
}
/* Search cache for resource records, removing them from the cache.
* Also, timeout cache entries, and trim cache to size.
* (Calling with NULLRR is legal -- will timeout & trim only.)
* Note that an answer from the cache cannot be authoritative, because
* we cannot guarantee that all the entries remain from a previous request.
* Returns RR list, or NULLRR if no record found.
*/
static struct rr *
dcache_search(rrlp)
struct rr *rrlp;
{
register struct rr *rrp, *test_rrp;
struct rr **rrpp, *result_rrlp;
int32 elapsed;
time_t now;
int count = 0;
#ifdef DEBUG
if(Dtrace && rrlp != NULLRR){
printf("dcache_search: searching for %s\n",rrlp->name);
}
#endif
elapsed = (int32)(time(&now) - Dcache_time);
Dcache_time = now;
rrpp = &result_rrlp;
for(rrp = Dcache; (test_rrp = rrp) != NULLRR;){
rrp = rrp->next;
/* timeout entries */
if(test_rrp->ttl > 0L
&& (test_rrp->ttl -= elapsed) <= 0L)
test_rrp->ttl = 0L;
if(compare_rr_list(rrlp,test_rrp) == 0){
dcache_drop( *rrpp = test_rrp );
rrpp = &(*rrpp)->next;
} else if(test_rrp->source == RR_FILE && ++count > Dcache_size){
dcache_drop(test_rrp);
free_rr(test_rrp);
}
}
*rrpp = NULLRR;
return result_rrlp;
}
/* Move a list of resource records to the cache, removing duplicates. */
static void
dcache_update(rrlp)
register struct rr *rrlp;
{
if(rrlp == NULLRR)
return;
free_rr(dcache_search(rrlp)); /* remove duplicates, first */
dcache_add(rrlp);
}
/**
** File Utilities
**/
static struct rr *
get_rr(fp,lastrrp)
FILE *fp;
struct rr *lastrrp;
{
char *line,*lp,*strtok();
struct rr *rrp;
char *name,*ttl,*class,*type,*data;
int i;
line = mallocw(256);
if(fgets(line,256,fp) == NULL){
free(line);
return NULLRR;
}
rrp = (struct rr *)callocw(1,sizeof(struct rr));
rrp->source = RR_FILE;
if(line[0] == '\0' || line[0] == '#' || line[0] == ';'){
rrp->comment = line;
return rrp;
}
if(!isspace(line[0]) || lastrrp == NULLRR){
name = strtok(line,delim);
lp = NULLCHAR;
}
else { /* Name field is missing */
name = lastrrp->name;
lp = line;
}
if(name == NULLCHAR || (name != NULLCHAR && (i = strlen(name)) == 0)){
rrp->comment = strdup("\n");
free(line);
return rrp;
}
if(name[i-1] != '.'){
/* Tack on a trailing period if it's not there */
/* !!! need to implement $ORIGIN suffix here */
rrp->name = mallocw(i+2);
strcpy(rrp->name,name);
strcat(rrp->name,".");
} else
rrp->name = strdup(name);
ttl = strtok(lp,delim);
if(ttl == NULLCHAR || (!isdigit(ttl[0]) && ttl[0] != '-')){
/* Optional ttl field is missing */
rrp->ttl = TTL_MISSING;
class = ttl;
} else {
rrp->ttl = atol(ttl);
class = strtok(NULLCHAR,delim);
}
if(class == NULLCHAR){
/* we're in trouble, but keep processing */
rrp->class = CLASS_MISSING;
type = class;
} else if(class[0] == '<'){
rrp->class = atoi(&class[1]);
type = strtok(NULLCHAR,delim);
} else if(stricmp(class,"IN") == 0){
rrp->class = CLASS_IN;
type = strtok(NULLCHAR,delim);
} else {
/* Optional class field is missing; assume IN */
rrp->class = CLASS_IN;
type = class;
}
if(type == NULLCHAR){
/* we're in trouble, but keep processing */
rrp->type = TYPE_MISSING;
data = type;
} else if(type[0] == '{'){
rrp->type = atoi(&class[1]);
data = strtok(NULLCHAR,delim);
} else {
rrp->type = TYPE_MISSING;
for(i=1;i<Ndtypes;i++){
if(stricmp(type,Dtypes[i]) == 0){
rrp->type = i;
data = strtok(NULLCHAR,delim);
break;
}
}
}
if(rrp->type == TYPE_MISSING){
data = NULLCHAR;
}
if(data == NULLCHAR){
/* Empty record, just return */
free(line);
return rrp;
}
switch(rrp->type){
case TYPE_A:
rrp->rdlength = 4;
rrp->rdata.addr = aton(data);
break;
case TYPE_CNAME:
case TYPE_MB:
case TYPE_MG:
case TYPE_MR:
case TYPE_NS:
case TYPE_PTR:
case TYPE_TXT:
rrp->rdlength = strlen(data);
rrp->rdata.name = strdup(data);
break;
case TYPE_HINFO:
rrp->rdlength = strlen(data);
rrp->rdata.hinfo.cpu = strdup(data);
if((data = strtok(NULLCHAR,delim)) != NULLCHAR){
rrp->rdlength += strlen(data);
rrp->rdata.hinfo.os = strdup(data);
}
break;
case TYPE_MX:
rrp->rdata.mx.pref = atoi(data);
rrp->rdlength = 2;
/* Get domain name of exchanger */
if((data = strtok(NULLCHAR,delim)) != NULLCHAR){
rrp->rdlength += strlen(data);
rrp->rdata.mx.exch = strdup(data);
}
break;
case TYPE_SOA:
/* Get domain name of master name server */
rrp->rdlength = strlen(data);
rrp->rdata.soa.mname = strdup(data);
/* Get domain name of irresponsible person */
if((data = strtok(NULLCHAR,delim)) != NULLCHAR){
rrp->rdata.soa.rname = strdup(data);
rrp->rdlength += strlen(data);
}
data = strtok(NULLCHAR,delim);
rrp->rdata.soa.serial = atol(data);
data = strtok(NULLCHAR,delim);
rrp->rdata.soa.refresh = atol(data);
data = strtok(NULLCHAR,delim);
rrp->rdata.soa.retry = atol(data);
data = strtok(NULLCHAR,delim);
rrp->rdata.soa.expire = atol(data);
data = strtok(NULLCHAR,delim);
rrp->rdata.soa.minimum = atol(data);
rrp->rdlength += 20;
break;
}
/* !!! need to handle trailing comments */
free(line);
return rrp;
}
/* Print a resource record */
static void
put_rr(fp,rrp)
FILE *fp;
struct rr *rrp;
{
if(fp == NULLFILE || rrp == NULLRR)
return;
if(rrp->name == NULLCHAR && rrp->comment != NULLCHAR){
fprintf(fp,"%s",rrp->comment);
return;
}
fprintf(fp,"%s",rrp->name);
if(rrp->ttl != TTL_MISSING)
fprintf(fp,"\t%ld",rrp->ttl);
if(rrp->class == CLASS_IN)
fprintf(fp,"\tIN");
else
fprintf(fp,"\t<%u>",rrp->class);
if(rrp->type < Ndtypes)
fprintf(fp,"\t%s",Dtypes[rrp->type]);
else
fprintf(fp,"\t{%u}",rrp->type);
if(rrp->rdlength == 0){
/* Null data portion, indicates nonexistent record */
/* or unsupported type. Hopefully, these will filter */
/* as time goes by. */
fprintf(fp,"\n");
return;
}
switch(rrp->type){
case TYPE_A:
fprintf(fp,"\t%s\n",inet_ntoa(rrp->rdata.addr));
break;
case TYPE_CNAME:
case TYPE_MB:
case TYPE_MG:
case TYPE_MR:
case TYPE_NS:
case TYPE_PTR:
case TYPE_TXT:
/* These are all printable text strings */
fprintf(fp,"\t%s\n",rrp->rdata.data);
break;
case TYPE_HINFO:
fprintf(fp,"\t%s\t%s\n",
rrp->rdata.hinfo.cpu,
rrp->rdata.hinfo.os);
break;
case TYPE_MX:
fprintf(fp,"\t%u\t%s\n",
rrp->rdata.mx.pref,
rrp->rdata.mx.exch);
break;
case TYPE_SOA:
fprintf(fp,"\t%s\t%s\t%lu\t%lu\t%lu\t%lu\t%lu\n",
rrp->rdata.soa.mname,rrp->rdata.soa.rname,
rrp->rdata.soa.serial,rrp->rdata.soa.refresh,
rrp->rdata.soa.retry,rrp->rdata.soa.expire,
rrp->rdata.soa.minimum);
break;
default:
fprintf(fp,"\n");
break;
}
}
/* Search local database for resource records.
* Returns RR list, or NULLRR if no record found.
*/
static struct rr *
dfile_search(rrlp)
struct rr *rrlp;
{
register struct rr *frrp;
struct rr **rrpp, *result_rrlp, *oldrrp;
int32 elapsed;
FILE *dbase;
struct stat dstat;
#ifdef DEBUG
if(Dtrace){
printf("dfile_search: searching for %s\n",rrlp->name);
}
#endif
while(Dfile_writing > 0)
pwait(&Dfile_reading);
Dfile_reading++;
if((dbase = fopen(Dfile,READ_TEXT)) == NULLFILE){
Dfile_reading--;
return NULLRR;
}
if(fstat(fileno(dbase),&dstat) != 0){
tprintf("dfile_search: can't get file status\n");
fclose(dbase);
Dfile_reading--;
return NULLRR;
}
if((elapsed = (int32)(Dcache_time - (time_t)dstat.st_ctime)) < 0L)
elapsed = -elapsed; /* arbitrary time mismatch */
result_rrlp = NULLRR; /* for contiguous test below */
oldrrp = NULLRR;
rrpp = &result_rrlp;
while((frrp = get_rr(dbase,oldrrp)) != NULLRR){
free_rr(oldrrp);
if(frrp->type != TYPE_MISSING
&& frrp->rdlength > 0
&& compare_rr_list(rrlp,frrp) == 0){
if(frrp->ttl > 0L
&& (frrp->ttl -= elapsed) <= 0L)
frrp->ttl = 0L;
*rrpp = frrp;
rrpp = &(*rrpp)->next;
oldrrp = copy_rr(frrp);
} else {
oldrrp = frrp;
/*
All records of the same name and the same type
are contiguous. Therefore, for a single query,
we can stop searching. Multiple queries must
read the whole file.
*/
if(rrlp->next == NULLRR && result_rrlp != NULLRR)
break;
}
if(!main_exit)
pwait(NULL); /* run multiple sessions */
}
free_rr(oldrrp);
*rrpp = NULLRR;
fclose(dbase);
if(--Dfile_reading <= 0){
Dfile_reading = 0;
psignal(&Dfile_writing,0);
}
return result_rrlp;
}
/* Process which will add new resource records from the cache
* to the local file, eliminating duplicates while it goes.
*/
static void
dfile_update(s,unused,p)
int s;
void *unused;
void *p;
{
struct rr **rrpp, *rrlp, *oldrrp;
char *newname;
FILE *old_fp, *new_fp;
struct stat old_stat, new_stat;
log(-1,"update Domain.txt initiated");
close_s(Curproc->input);
Curproc->input = -1;
close_s(Curproc->output);
Curproc->output = -1;
newname = strdup(Dfile);
strcpy(&newname[strlen(newname)-3],"tmp");
while(Dfile_wait_absolute != 0L && !main_exit){
register struct rr *frrp;
int32 elapsed;
while(Dfile_wait_absolute != 0L){
elapsed = Dfile_wait_absolute - secclock();
Dfile_wait_absolute = 0L;
if(elapsed > 0L && !main_exit){
alarm(elapsed*1000);
pwait(&Dfile_wait_absolute);
alarm(0L);
}
}
log(-1,"update Domain.txt");
/* create new file for copy */
if((new_fp = fopen(newname,WRITE_TEXT)) == NULLFILE){
printf("dfile_update: can't create %s!\n",newname);
break;
}
if(fstat(fileno(new_fp),&new_stat) != 0){
printf("dfile_update: can't get new_file status!\n");
fclose(new_fp);
break;
}
pwait(NULL); /* file operations can be slow */
/* timeout the cache one last time before writing */
(void)dcache_search(NULLRR);
/* copy new RRs out to the new file */
/* (can't wait here, the cache might change) */
rrpp = &rrlp;
for(frrp = Dcache; frrp != NULLRR; frrp = frrp->next ){
switch(frrp->source){
case RR_QUESTION:
case RR_ANSWER:
case RR_AUTHORITY:
case RR_ADDITIONAL:
*rrpp = copy_rr(frrp);
if(frrp->type != TYPE_MISSING
&& frrp->rdlength > 0)
put_rr(new_fp,frrp);
rrpp = &(*rrpp)->next;
frrp->source = RR_FILE;
break;
}
}
*rrpp = NULLRR;
/* open up the old file, concurrently with everyone else */
if((old_fp = fopen(Dfile,READ_TEXT)) == NULLFILE){
/* great! no old file, so we're ready to go. */
fclose(new_fp);
rename(newname,Dfile);
free_rr(rrlp);
break;
}
if(fstat(fileno(old_fp),&old_stat) != 0){
printf("dfile_update: can't get old_file status!\n");
fclose(new_fp);
fclose(old_fp);
free_rr(rrlp);
break;
}
if((elapsed = (int32)(new_stat.st_ctime - old_stat.st_ctime)) < 0L)
elapsed = -elapsed; /* file times are inconsistant */
/* Now append any non-duplicate records */
oldrrp = NULLRR;
while((frrp = get_rr(old_fp,oldrrp)) != NULLRR){
free_rr(oldrrp);
if(frrp->name == NULLCHAR
&& frrp->comment != NULLCHAR)
put_rr(new_fp,frrp);
if(frrp->type != TYPE_MISSING
&& frrp->rdlength > 0
&& compare_rr_list(rrlp,frrp) != 0){
if(frrp->ttl > 0L
&& (frrp->ttl -= elapsed) <= 0L)
frrp->ttl = 0L;
if(frrp->ttl != 0 || !Dfile_clean)
put_rr(new_fp,frrp);
}
oldrrp = frrp;
if(!main_exit)
pwait(NULL); /* run in background */
}
free_rr(oldrrp);
fclose(new_fp);
fclose(old_fp);
free_rr(rrlp);
/* wait for everyone else to finish reading */
Dfile_writing++;
while(Dfile_reading > 0)
pwait(&Dfile_writing);
unlink(Dfile);
rename(newname,Dfile);
Dfile_writing = 0;
psignal(&Dfile_reading,0);
}
free(newname);
log(-1,"update Domain.txt finished");
Dfile_updater = NULLPROC;
}
/**
** Domain Server Utilities
**/
static void
dumpdomain(dhp,rtt)
struct dhdr *dhp;
int32 rtt;
{
struct rr *rrp;
printf("response id %u (rtt %lu sec) qr %u opcode %u aa %u tc %u rd %u ra %u rcode %u\n",
dhp->id,(long)rtt /1000L,
dhp->qr,dhp->opcode,dhp->aa,dhp->tc,dhp->rd,
dhp->ra,dhp->rcode);
printf("%u questions:\n",dhp->qdcount);
for(rrp = dhp->questions; rrp != NULLRR; rrp = rrp->next){
printf("%s type %s class %u\n",rrp->name,
Dtypes[rrp->type],rrp->class);
}
printf("%u answers:\n",dhp->ancount);
for(rrp = dhp->answers; rrp != NULLRR; rrp = rrp->next){
put_rr(stdout,rrp);
}
printf("%u authority:\n",dhp->nscount);
for(rrp = dhp->authority; rrp != NULLRR; rrp = rrp->next){
put_rr(stdout,rrp);
}
printf("%u additional:\n",dhp->arcount);
for(rrp = dhp->additional; rrp != NULLRR; rrp = rrp->next){
put_rr(stdout,rrp);
}
fflush(stdout);
}
static int
dns_makequery(op,srrp,buffer,buflen)
int16 op; /* operation */
struct rr *srrp;/* Search RR */
char *buffer; /* Area for query */
int16 buflen; /* Length of same */
{
char *cp,*cp1;
char *dname, *sname;
int16 parameter;
int16 dlen,len;
cp = buffer;
/* Use millisecond clock for timestamping */
cp = put16(cp,(int16)msclock());
parameter = (op << 11)
| 0x0100; /* Recursion desired */
cp = put16(cp,parameter);
cp = put16(cp,1);
cp = put16(cp,0);
cp = put16(cp,0);
cp = put16(cp,0);
sname = strdup(srrp->name);
dname = sname;
dlen = strlen(dname);
for(;;){
/* Look for next dot */
cp1 = strchr(dname,'.');
if(cp1 != NULLCHAR)
len = cp1-dname; /* More to come */
else
len = dlen; /* Last component */
*cp++ = len; /* Write length of component */
if(len == 0)
break;
/* Copy component up to (but not including) dot */
strncpy(cp,dname,len);
cp += len;
if(cp1 == NULLCHAR){
*cp++ = 0; /* Last one; write null and finish */
break;
}
dname += len+1;
dlen -= len+1;
}
free(sname);
cp = put16(cp,srrp->type);
cp = put16(cp,srrp->class);
return cp - buffer;
}
/* domain server resolution loop
* returns: any answers in cache.
* (future features)
* multiple queries.
* inverse queries.
*/
static void
dns_query(rrlp)
struct rr *rrlp;
{
struct mbuf *bp;
struct dhdr *dhp;
struct dserver *dp; /* server list */
int32 rtt,abserr;
int tried = 0; /* server list has been retried (count) */
if((dp = Dservers) == NULLDOM){
return;
}
for(;;){
char *buf;
int len;
struct sockaddr_in server_in;
int s;
int rval;
dp->queries++;
s = socket(AF_INET,SOCK_DGRAM,0);
server_in.sin_family = AF_INET;
server_in.sin_port = IPPORT_DOMAIN;
server_in.sin_addr.s_addr = dp->address;
if(Dtrace){
printf("dns_query: querying server %s for %s\n",
inet_ntoa(dp->address),rrlp->name);
}
buf = mallocw(512);
len = dns_makequery(0,rrlp,buf,512);
sendto(s,buf,len,0,(char *)&server_in,sizeof(server_in));
free(buf);
alarm(max(dp->timeout,100));
/* Wait for something to happen */
rval = recv_mbuf(s,&bp,0,NULLCHAR,0);
alarm(0L);
close_s(s);
if(Dtrace){
printf("dns_query: received message length %d, errno %d\n", rval, errno );
}
if(rval > 0)
break;
if(errno == EABORT){
return; /* Killed by "reset" command */
}
/* Timeout; back off this one and try another server */
dp->timeout <<= 1;
if((dp = dp->next) == NULLDOM){
dp = Dservers;
if(Dserver_retries > 0 && ++tried > Dserver_retries)
return;
}
}
/* got a response */
dp->responses++;
dhp = (struct dhdr *) mallocw(sizeof(struct dhdr));
ntohdomain(dhp,&bp); /* Convert to local format */
/* Compute and update the round trip time */
rtt = (int32) ((int16)msclock() - dhp->id);
abserr = rtt > dp->srtt ? rtt - dp->srtt : dp->srtt - rtt;
dp->srtt = ((AGAIN-1) * dp->srtt + rtt + (AGAIN/2)) >> LAGAIN;
dp->mdev = ((DGAIN-1) * dp->mdev + abserr + (DGAIN/2)) >> LDGAIN;
dp->timeout = 4 * dp->mdev + dp->srtt;
/* move to top of list for next time */
if(dp->prev != NULLDOM){
dlist_drop(dp);
dlist_add(dp);
}
if(Dtrace)
dumpdomain(dhp,rtt);
/* Add negative reply to answers. This assumes that there was
* only one question, which is true for all questions we send.
*/
if(dhp->aa && (dhp->rcode == NAME_ERROR || dhp->ancount == 0)){
register struct rr *rrp;
long ttl = 600L; /* Default TTL for negative records */
/* look for SOA ttl */
for(rrp = dhp->authority; rrp != NULLRR; rrp = rrp->next){
if(rrp->type == TYPE_SOA)
ttl = rrp->ttl;
}
/* make the questions the negative answers */
for(rrp = dhp->questions; rrp != NULLRR; rrp = rrp->next)
rrp->ttl = ttl;
} else {
free_rr(dhp->questions);
dhp->questions = NULLRR;
}
/* post in reverse order to maintain original order */
dcache_update(dhp->additional);
dcache_update(dhp->authority);
dcache_update(dhp->answers);
dcache_update(dhp->questions);
Dfile_wait_absolute = secclock() + Dfile_wait_relative;
if(Dfile_updater == NULLPROC){
Dfile_updater = newproc("domain update",
512,dfile_update,0,NULL,NULL,0);
}
#ifdef DEBUG
if(Dtrace)
keywait(NULLCHAR,1); /* so we can look around */
#endif
free((char *)dhp);
return;
}
/**
** Resolver Utilities
**/
/* Return TRUE if string appears to be an IP address in dotted decimal;
* return FALSE otherwise (i.e., if string is a domain name)
*/
static int
isaddr(s)
register char *s;
{
char c;
if(s == NULLCHAR)
return TRUE; /* Can't happen */
while((c = *s++) != '\0'){
if(c != '[' && c != ']' && !isdigit(c) && c != '.')
return FALSE;
}
return TRUE;
}
/* Search for resource records.
* Returns RR list, or NULLRR if no record found.
*/
static struct rr *
resolver(rrlp)
register struct rr *rrlp;
{
register struct rr *result_rrlp;
if((result_rrlp = dcache_search(rrlp)) == NULLRR){
result_rrlp = dfile_search(rrlp);
}
if(result_rrlp == NULLRR || check_ttl(result_rrlp) != 0){
dcache_add(result_rrlp); /* save any expired RRs */
dns_query(rrlp);
result_rrlp = dcache_search(rrlp);
}
dcache_add(copy_rr_list(result_rrlp));
return result_rrlp;
}
/* general entry point for address -> domain name resolution.
* Returns RR list, or NULLRR if no record found.
*/
struct rr *
inverse_a(ip_address)
int32 ip_address;
{
struct rr *prrp;
struct rr *result_rrlp;
char pname[30];
if(ip_address == 0L)
return NULLRR;
sprintf( pname, "%u.%u.%u.%u.IN-ADDR.ARPA.",
lobyte(loword(ip_address)),
hibyte(loword(ip_address)),
lobyte(hiword(ip_address)),
hibyte(hiword(ip_address)) );
prrp = make_rr(RR_QUERY,pname,CLASS_IN,TYPE_PTR,0,0,NULL);
prrp->next = /* make list to speed search */
make_rr(RR_INQUERY,NULLCHAR,CLASS_IN,TYPE_A,0,4,&ip_address);
result_rrlp = resolver(prrp);
free_rr(prrp);
return result_rrlp;
}
/* general entry point for domain name -> resource resolution.
* Returns RR list, or NULLRR if no record found.
*/
struct rr *
resolve_rr(dname,dtype)
char *dname;
int16 dtype;
{
struct rr *qrrp;
struct rr *result_rrlp;
char *sname, *tname;
int looping = MAXCNAME;
if(dname == NULLCHAR)
return NULLRR;
sname = strdup(dname);
if(strchr(sname,'.') == NULLCHAR && Dsuffix != NULLCHAR){
/* Append default suffix */
tname = mallocw(strlen(sname)+strlen(Dsuffix)+2);
sprintf(tname,"%s.%s",sname,Dsuffix);
free(sname);
sname = tname;
}
if(sname[strlen(sname)-1] != '.'){
/* Append trailing dot */
tname = mallocw(strlen(sname)+2);
sprintf(tname,"%s.",sname);
free(sname);
sname = tname;
}
qrrp = make_rr(RR_QUERY,sname,CLASS_IN,dtype,0,0,NULL);
free(sname);
while(looping > 0){
if((result_rrlp=resolver(qrrp)) == NULLRR
|| result_rrlp->type == dtype)
break;
#ifdef DEBUG
if(Dtrace)
put_rr(stdout,result_rrlp);
#endif
/* Should be CNAME or PTR record */
/* Replace name and try again */
free(qrrp->name);
qrrp->name = strdup(result_rrlp->rdata.name);
free_rr(result_rrlp);
result_rrlp = NULLRR;
looping--;
}
free_rr(qrrp);
return result_rrlp;
}
/* main entry point for address -> domain name resolution.
* Returns string, or NULLCHAR if no name found.
*/
char *
resolve_a(ip_address,shorten)
int32 ip_address; /* search address */
int shorten; /* return only first part of name (flag)*/
{
struct rr *save_rrlp, *rrlp;
char *result = NULLCHAR;
for( rrlp = save_rrlp = inverse_a(ip_address);
rrlp != NULLRR && result == NULLCHAR;
rrlp = rrlp->next ){
if(rrlp->rdlength > 0){
switch(rrlp->type){
case TYPE_PTR:
result = strdup(rrlp->rdata.name);
break;
case TYPE_A:
result = strdup(rrlp->name);
break;
}
}
}
free_rr(save_rrlp);
if(result != NULLCHAR && shorten){
int dot;
char *shortened;
if((dot = strcspn(result, ".")) == 0){
shortened = mallocw(dot+1);
strncpy(shortened, result, dot);
shortened[dot] = '\0';
free(result);
result = shortened;
}
}
return result;
}
/* Main entry point for domain name -> address resolution.
* Returns 0 if name is currently unresolvable.
*/
int32
resolve(name)
char *name;
{
register struct rr *rrlp;
int32 ip_address = 0;
if(name == NULLCHAR)
return 0;
if(isaddr(name))
return aton(name);
if((rrlp = resolve_rr(name,TYPE_A)) != NULLRR
&& rrlp->rdlength > 0)
ip_address = rrlp->rdata.addr;
/* multi-homed hosts are handled here */
if(rrlp != NULLRR && rrlp->next != NULLRR) {
register struct rr *rrp;
register struct route *rp;
int16 cost = MAXINT16;
rrp = rrlp;
while(rrp != NULLRR) { /* choose the best of a set of routes */
if(rrp->rdlength > 0 &&
(rp = rt_lookup(rrp->rdata.addr)) != NULLROUTE &&
rp->metric <= cost) {
ip_address = rrp->rdata.addr;
cost = rp->metric;
}
rrp = rrp->next;
}
}
free_rr(rrlp);
return ip_address;
}
/* Main entry point for MX record lookup.
* Returns 0 if name is currently unresolvable.
*/
int32
resolve_mx(name)
char *name;
{
register struct rr *rrp, *arrp;
char *sname, *tmp, *cp;
int32 addr, ip_address = 0;
int16 pref = MAXINT16;
if(name == NULLCHAR)
return 0;
if(isaddr(name)){
if((sname = resolve_a(aton(name),FALSE)) == NULLCHAR)
return 0;
}
else
sname = strdup(name);
cp = sname;
while(1){
rrp = arrp = resolve_rr(sname,TYPE_MX);
/* Search this list of rr's for an MX record */
while(rrp != NULLRR){
if(rrp->rdlength > 0 && rrp->rdata.mx.pref <= pref &&
(addr = resolve(rrp->rdata.mx.exch)) != 0L){
pref = rrp->rdata.mx.pref;
ip_address = addr;
}
rrp = rrp->next;
}
free_rr(arrp);
if(ip_address != 0)
break;
/* Compose wild card one level up */
if((cp = strchr(cp,'.')) == NULLCHAR)
break;
tmp = mallocw(strlen(cp)+2);
sprintf(tmp,"*%s",cp); /* wildcard expansion */
free(sname);
sname = tmp;
cp = sname + 2;
}
free(sname);
return ip_address;
}
/* Search for local records of the MB, MG and MR type. Returns list of
* matching records.
*/
struct rr *
resolve_mailb(name)
char *name; /* local username, without trailing dot */
{
register struct rr *result_rrlp;
struct rr *rrlp;
char *sname;
/* Append trailing dot */
sname = mallocw(strlen(name)+2);
sprintf(sname,"%s.",name);
rrlp = make_rr(RR_QUERY,sname,CLASS_IN,TYPE_MB,0,0,NULL);
rrlp->next = make_rr(RR_QUERY,sname,CLASS_IN,TYPE_MG,0,0,NULL);
rrlp->next->next = make_rr(RR_QUERY,sname,CLASS_IN,TYPE_MR,0,0,NULL);
free(sname);
if((result_rrlp = dcache_search(rrlp)) == NULLRR){
result_rrlp = dfile_search(rrlp);
}
free_rr(rrlp);
if(Dsuffix != NULLCHAR){
rrlp = result_rrlp;
while(rrlp != NULLRR){ /* add domain suffix to data */
if(rrlp->rdlength > 0 &&
rrlp->rdata.name[rrlp->rdlength-1] != '.'){
sname = mallocw(rrlp->rdlength +
strlen(Dsuffix)+2);
sprintf(sname,"%s.%s",rrlp->rdata.name,Dsuffix);
free(rrlp->rdata.name);
rrlp->rdata.name = sname;
rrlp->rdlength = strlen(sname);
}
rrlp = rrlp->next;
}
}
dcache_add(copy_rr_list(result_rrlp));
return result_rrlp;
}