home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
High Voltage Shareware
/
high1.zip
/
high1
/
DIR3
/
KA9Q212.ZIP
/
DOMAIN.C
< prev
next >
Wrap
C/C++ Source or Header
|
1993-07-16
|
59KB
|
2,514 lines
/*
* DOMAIN.C -- domain name system stub resolver
*
* Original code by Phil Karn, KA9Q.
*
* Apr 90 Bill Simpson added address->name resolution, time-to-live,
* thru memory caching, generalized multi-record multi-type searches,
* Oct 90 and many minor changes to conform more closely to the RFCs.
* Feb 91 Bill Simpson added "query" command and TYPE_ANY processing.
* Jul 91 Bill Simpson added "more" sessions for query and cache list.
*/
/****************************************************************************
* $Id: domain.c 1.4 93/07/16 11:43:46 ROOT_DOS Exp $
* 07 Jun 92 1.2 GT Reset DNS timeout on DNS failure. *
* Oct 92 - mt@kram.demon.co.uk (G7LEU) merged NOS WG7J104 DNS code. *
* Feb 93 - lonewolf@sphere.demon.co.uk fixed DNS reply to TYPE_ANY query.
* added domain qtype <n> to set the query type used by
* domain query. Useful if your server doesn't understand TYPE_ANY
* changed all remaining printf to tprintf.
* 17 May 93 1.4 GT Fix warnings. *
****************************************************************************/
#include <stdio.h>
#include <ctype.h>
#include <time.h>
#include <sys/stat.h>
#include <dir.h>
#include <dos.h>
#include <string.h>
#include <stdarg.h>
#include "global.h"
#include "config.h"
#include "mbuf.h"
#include "proc.h"
#include "ip.h"
#include "netuser.h"
#include "session.h"
#include "socket.h"
#include "cmdparse.h"
#include "commands.h"
#include "files.h"
#include "main.h"
#include "domain.h"
#undef DEBUG /* for certain trace messages */
#undef DEBUG_PAIN /* for painful debugging */
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 int32 Dserver_maxwait = 0L; /* maximum server timeout limit (seconds) */
static char *Dsuffix = NULLCHAR; /* Default suffix for names without periods */
static int Dtrace = FALSE;
static int qtype = 255;
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";
#ifdef DSERVER
static struct DOM_FILE {
struct DOM_FILE *next;
char type;
char *filename;
char *domain;
int32 source;
struct soa dom_soa;
} *dom_file = NULL;
struct proc *Drx;
int Dsocket;
extern char *Dzones;
extern char *Dboot;
#endif
extern int are_we_an_mx;
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 dodnsmaxw __ARGS((int argc,char *argv[],void *p));
static int dodnsquery __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 dodnsserver __ARGS((int argc,char *argv[],void *p));
static int doqtype __ARGS((int argc,char *argv[],void *p));
static int dostopdnsserver __ARGS((int argc,char *argv[],void *p));
static char * dtype __ARGS((int value));
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, char *filename));
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 char *checksuffix __ARGS((char *dname));
static struct rr *resolver __ARGS((struct rr *rrlp));
static void free_dhdr __ARGS((struct dhdr *));
static void proc_query __ARGS((int,void *,void *));
static void drx __ARGS((int,void *,void *));
/**
** Domain Resolver Commands
**/
static struct cmds Dcmds[] = {
{ "addserver", dodnsadd, 0, 2, "add <hostid>" },
{ "dropserver", dodnsdrop, 0, 2, "drop <hostid>" },
{ "list", dodnslist, 0, 0, NULLCHAR },
{ "maxwait", dodnsmaxw, 0, 0, NULLCHAR },
{ "query", dodnsquery, 512, 2, "query <hostid>" },
{ "qtype", doqtype, 0, 0, NULLCHAR },
{ "retry", dodnsretry, 0, 0, NULLCHAR },
{ "suffix", dosuffix, 0, 0, NULLCHAR },
#ifdef DSERVER
{ "startdns", dodnsserver,0, 0, NULLCHAR },
{ "stopdns", dostopdnsserver,0, 0, NULLCHAR },
#endif
{ "trace", dodnstrace, 0, 0, NULLCHAR },
{ "cache", docache, 0, 0, NULLCHAR },
{ NULLCHAR },
};
static struct cmds Dcachecmds[] = {
{ "clean", docacheclean, 0, 0, NULLCHAR },
{ "list", docachelist, 512, 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
dodnsmaxw(argc,argv,p)
int argc;
char *argv[];
void *p;
{
return setlong(&Dserver_maxwait,"Server response timeout limit (sec)",argc,argv);
}
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;
struct session *sp;
int row = MOREROWS;
if((sp = newsession(argv[1],DCLIST)) == NULLSESSION){
return -1;
}
(void)dcache_search(NULLRR); /* update ttl */
/* Put tty into raw mode so single-char responses will work */
sp->ttystate.echo = sp->ttystate.edit = 0;
for(rrp=Dcache;rrp!=NULLRR;rrp=rrp->next)
{
put_rr(stdout,rrp);
if(--row == 0){
row = keywait("--More--",0);
switch(row){
case -1:
case 'q':
case 'Q':
rrp = NULLRR;
break;
case '\n':
case '\r':
row = 1;
break;
case ' ':
default:
row = MOREROWS;
};
}
}
fflush(stdout);
keywait(NULLCHAR,1);
freesession(sp);
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;
{
int32 address;
if((address = resolve(argv[1])) == 0L){
tprintf("Resolver %s unknown\n",argv[1]);
return 1;
}
return add_nameserver(address);
}
int
add_nameserver(address)
int32 address;
{
struct dserver *dp;
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;
{
#ifdef DSERVER
struct DOM_FILE *t_dom_file;
#endif
register struct dserver *dp;
tprintf("Server address srtt mdev timeout queries responses timeouts\n");
for(dp = Dservers;dp != NULLDOM;dp = dp->next){
tprintf("%-20s%8lu%8lu%10lu%10lu%10lu%10lu\n",
inet_ntoa(dp->address),
dp->srtt,dp->mdev,dp->timeout,
dp->queries,dp->responses,dp->timeouts);
}
#ifdef DSERVER
if (Drx == NULLPROC)
tprintf("Domain Name Server not running.\n");
else tprintf("Domain Name Server running.\n");
t_dom_file = dom_file;
while(Drx != NULLPROC && t_dom_file)
{
if (t_dom_file->type == '1')
tprintf("We are primary for %s\n", t_dom_file->domain);
else if (t_dom_file->type == '2')
tprintf("We are secondary for %s (from %s)\n",
t_dom_file->domain, inet_ntoa(t_dom_file->source));
t_dom_file = t_dom_file->next;
}
#endif
return 0;
}
static int
doqtype(argc,argv,p)
int argc;
char *argv[];
void *p;
{
return setint(&qtype,"Query type",argc,argv);
}
static int
dodnsquery(argc,argv,p)
int argc;
char *argv[];
void *p;
{
struct rr *rrp;
struct rr *result_rrlp;
char *sname;
struct session *sp;
int row = MOREROWS;
if((sp = newsession(argv[1],DQUERY)) == NULLSESSION){
return -1;
}
if ( isaddr( argv[1] ) ) {
result_rrlp = inverse_a( aton( argv[1] ) );
} else {
sname = checksuffix( argv[1] );
rrp = make_rr(RR_QUERY,sname,CLASS_IN,qtype,0,0,NULL);
free(sname);
dns_query(rrp);
result_rrlp = dcache_search(rrp);
free_rr(rrp);
}
/* Put tty into raw mode so single-char responses will work */
sp->ttystate.echo = sp->ttystate.edit = 0;
for( rrp=result_rrlp; rrp!=NULLRR; rrp=rrp->next)
{
put_rr(stdout,rrp);
if(--row == 0){
row = keywait("--More--",0);
switch(row){
case -1:
case 'q':
case 'Q':
rrp = NULLRR;
break;
case '\n':
case '\r':
row = 1;
break;
case ' ':
default:
row = MOREROWS;
};
}
}
fflush(stdout);
free_rr(result_rrlp);
keywait(NULLCHAR,1);
freesession(sp);
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
**/
static char *
dtype(value)
int value;
{
static char buf[10];
if (value < Ndtypes)
return Dtypes[value];
sprintf( buf, "{%d}", value);
return buf;
}
/* 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 != TYPE_ANY
&& 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;
*/
i = strlen(target_rrp->name);
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)
tprintf("%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:
case TYPE_AXFR: /*##*/
{
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){
tprintf("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(500);
if(fgets(line,500,fp) == NULL){
free(line);
return NULLRR;
}
if (strchr(line, '('))
{
int done;
char *big_line, *p;
p = line;
while (*p && *p != '(')
p++;
*p = '\0';
big_line = mallocw(500);
if (big_line == NULL)
{
free(line);
return NULLRR;
}
strcpy(big_line, line);
done = FALSE;
while (fgets(line, 500, fp) && !done)
{
if (strchr(line, ')'))
done = TRUE;
p = line;
while (*p && *p != ')' && *p != ';')
p++;
*p = '\0';
p = line;
while (*p && (*p == ' ' || *p == '\t'))
p++;
strcat(big_line, " ");
strcat(big_line, p);
}
strcpy(line, big_line);
free(big_line);
}
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 || (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;
}
static void sofprintf(FILE *fp, char * fmt, ...)
{
/*
This routine behaves like the standard fprintf() except
for when the destination is stdout. In this case write
to the current process' output socket.
*/
char * buf;
va_list args;
buf = mallocw(1024); /* Big enough for most strings... (cf sockuser.c) */
va_start(args, fmt);
vsprintf(buf, fmt, args);
va_end(args);
if (fp == stdout)
usputs(Curproc->output, buf);
else fputs(buf, fp);
free(buf);
}
/* Print a resource record */
static void
put_rr(fp,rrp)
FILE *fp;
struct rr *rrp;
{
char * stuff;
if(fp == NULLFILE || rrp == NULLRR)
return;
if(rrp->name == NULLCHAR && rrp->comment != NULLCHAR){
sofprintf(fp,"%s",rrp->comment);
return;
}
sofprintf(fp,"%s",rrp->name);
if(rrp->ttl != TTL_MISSING)
sofprintf(fp,"\t%ld",rrp->ttl);
if(rrp->class == CLASS_IN)
sofprintf(fp,"\tIN");
else
sofprintf(fp,"\t<%u>",rrp->class);
stuff = dtype(rrp->type);
sofprintf(fp,"\t%s",stuff);
if(rrp->rdlength == 0){
/* Null data portion, indicates nonexistent record */
/* or unsupported type. Hopefully, these will filter */
/* as time goes by. */
sofprintf(fp,"\n");
return;
}
switch(rrp->type){
case TYPE_A:
sofprintf(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 */
sofprintf(fp,"\t%s\n",rrp->rdata.data);
break;
case TYPE_HINFO:
sofprintf(fp,"\t%s\t%s\n",
rrp->rdata.hinfo.cpu,
rrp->rdata.hinfo.os);
break;
case TYPE_MX:
sofprintf(fp,"\t%u\t%s\n",
rrp->rdata.mx.pref,
rrp->rdata.mx.exch);
break;
case TYPE_SOA:
sofprintf(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:
sofprintf(fp,"\n");
break;
}
}
/* Search local database for resource records.
* Returns RR list, or NULLRR if no record found.
*/
static struct rr *
dfile_search(rrlp, filename)
struct rr *rrlp;
char *filename;
{
int counter;
register struct rr *frrp;
struct rr **rrpp, *result_rrlp, *oldrrp;
int32 elapsed;
FILE *dbase;
struct stat dstat;
char *this_dfile;
#ifdef DEBUG
if(Dtrace){
tprintf("dfile_search: searching for %s\n",rrlp->name);
}
#endif
#ifdef DSERVER
if (filename)
{
if(Dtrace) {
tprintf("dfile_search: searching local zone file %s\n", filename);
}
this_dfile = mallocw(strlen(Dzones) + 15);
sprintf(this_dfile, "%s/%s", Dzones, filename);
}
else
#endif
this_dfile = Dfile;
while(Dfile_writing > 0)
pwait(&Dfile_reading);
Dfile_reading++;
if((dbase = fopen(this_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;
counter = 0;
while((frrp = get_rr(dbase,oldrrp)) != NULLRR){
if (++counter == 100)
{
pwait(NULL);
counter = 0;
}
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.
Unless we're primary!
*/
if(rrlp->type != TYPE_ANY
&& rrlp->next == NULLRR
&& result_rrlp != NULLRR
#ifdef DSERVER
&& filename == NULL
#endif
)
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);
}
#ifdef DSERVER
if (filename)
free(this_dfile);
#endif
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);
usesock(Curproc->input = Cmdpp->input);
close_s(Curproc->output);
usesock(Curproc->output = Cmdpp->output);
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*1000L);
pwait(&Dfile_wait_absolute);
alarm(0L);
}
}
log(-1,"update Domain.txt");
/* create new file for copy */
if((new_fp = fopen(newname,WRITE_TEXT)) == NULLFILE){
tprintf("dfile_update: can't create %s!\n",newname);
break;
}
if(fstat(fileno(new_fp),&new_stat) != 0){
tprintf("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){
tprintf("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;
char * stuff;
tprintf("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);
tprintf("%u questions:\n",dhp->qdcount);
for(rrp = dhp->questions; rrp != NULLRR; rrp = rrp->next){
stuff = dtype(rrp->type);
tprintf("%s type %s class %u\n",rrp->name,
stuff,rrp->class);
}
tprintf("%u answers:\n",dhp->ancount);
for(rrp = dhp->answers; rrp != NULLRR; rrp = rrp->next){
put_rr(stdout,rrp);
}
tprintf("%u authority:\n",dhp->nscount);
for(rrp = dhp->authority; rrp != NULLRR; rrp = rrp->next){
put_rr(stdout,rrp);
}
tprintf("%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 (int) (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){
tprintf("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){
tprintf("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->timeouts++;
dp->timeout <<= 1;
/* There may be a limit to how long we will wait */
if(Dserver_maxwait && dp->timeout > (Dserver_maxwait * 1000L))
dp->timeout = Dserver_maxwait * 1000L;
if((dp = dp->next) == NULLDOM){
dp = Dservers;
if(Dserver_retries > 0 && ++tried > Dserver_retries)
{
if (Dserver_maxwait == 0)
/* Only if not specified the maximum wait - MT Oct 92 */
{
/* Reset timeouts - GT 07 Jun 92. */
while (dp != NULLDOM)
{
dp->timeout = 2 * dp->mdev + dp->srtt + 3;
dp = dp->next;
}
}
dp = Dservers;
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;
}
/* Return "normalized" domain name, with default suffix and trailing '.'
*/
static char *
checksuffix(dname)
char *dname;
{
char *sname, *tname;
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;
}
return sname;
}
char *
zone_filename(domain, expire)
char *domain;
int32 *expire;
{
struct DOM_FILE *t_dom_file;
char *name;
int len;
len = strlen(domain);
name = mallocw(len + 2);
strcpy(name, domain);
if (name[len - 1] != '.')
{
strcat(name, ".");
len++;
}
t_dom_file = dom_file;
while (Drx != NULLPROC && t_dom_file)
{
if (stricmp(t_dom_file->domain, name + len
- strlen(t_dom_file->domain)) == NULL)
{
if (expire)
*expire = t_dom_file->dom_soa.expire;
if (Dtrace)
tprintf("DNS: Using file %s\n", t_dom_file->filename);
free(name);
return t_dom_file->filename;
}
t_dom_file = t_dom_file->next;
}
free(name);
return NULL;
}
/* 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;
char *t;
#ifdef DSERVER
int32 expire;
struct rr *t_rr;
#endif
#ifdef DSERVER
/*
Are we primary for the domain? If so, we must answer from the
file cos we can't guarantee that a full set of records for a
particular query will be in the cache.
*/
if ((t = zone_filename(rrlp->name, &expire)) != NULL)
{
result_rrlp = dfile_search(rrlp, t);
if (result_rrlp != NULLRR)
{
t_rr = result_rrlp;
while (t_rr)
{
t_rr->ttl = expire;
t_rr = t_rr->next;
}
/*
If we never use the cache to answer things we're primary for then there's
no point in caching the results! MT - G7LEU
dcache_add(copy_rr_list(result_rrlp));
*/
}
return result_rrlp;
}
#endif
if((result_rrlp = dcache_search(rrlp)) == NULLRR){
result_rrlp = dfile_search(rrlp, NULL);
}
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;
int looping = MAXCNAME;
if(dname == NULLCHAR)
return NULLRR;
sname = checksuffix(dname);
qrrp = make_rr(RR_QUERY,sname,CLASS_IN,dtype,0,0,NULL);
free(sname);
while(looping > 0){
/* Change this condition to include TYPE_ANY. ILP 17/2/93 */
if((result_rrlp=resolver(qrrp)) == NULLRR
|| result_rrlp->type == dtype
|| dtype == TYPE_ANY)
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;
/* choose the best of a set of routes */
while(rrp != NULLRR) {
if(rrp->rdlength > 0
&& (rp = rt_lookup(rrp->rdata.addr)) != NULLROUTE
#if 0
&& rp->metric <= cost) {
#endif
&& 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;
int our_domain = 0;
are_we_an_mx = 0;
if (zone_filename(name, NULL))
our_domain = 1;
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 && (addr = resolve(rrp->rdata.mx.exch)) != 0L)
{
if (addr == Ip_addr)
are_we_an_mx = 1;
if (rrp->rdata.mx.pref <= pref)
{
pref = rrp->rdata.mx.pref;
ip_address = addr;
}
}
rrp = rrp->next;
}
free_rr(arrp);
if(ip_address != 0)
break;
/* If this is our domain we can say no at this point */
if (our_domain)
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);
if (Dtrace)
tprintf("resolve_mx %s returns %s\n", name, inet_ntoa(ip_address));
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, NULL);
}
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;
}
#ifdef DSERVER
/* Domain Name Server - based on the server in GRI-Nos 910828
* ported to current NOS code by Johan. K. Reinalda, WG7J/PA3DIS
* Merged into Demon Internet Services version - MT (g7leu) Sept 1992.
* Added primary nameserver - MT (g7leu) October 1992.
*/
/* Process a query received by the DNS server */
static void
proc_query(unused,d,b)
int unused;
void *d;
void *b;
{
struct dserver *dp;
struct dhdr *dhdr;
int i,len;
char *buf;
struct sockaddr_in server;
struct rr *rrp, *rrans, *rrtmp;
#ifdef notdef
struct rr *rrns, *rradd, *ap;
#endif
struct rr *qp;
int32 expire;
dp = (struct dserver *) d; /* The query address */
dhdr = (struct dhdr *) b; /* The query in host format */
rrans = NULLRR;
#ifdef notdef
rrns = rradd = NULLRR;
#endif
qp = dhdr->questions;
/* This loop does NOT support multiple questions yet */
#ifdef notdef
for(i=0;i<dhdr->qdcount;i++){
#endif
if(zone_filename(qp->name, &expire))
dhdr->aa = 1;
else dhdr->aa = 0;
switch(qp->type) {
case TYPE_A:
case TYPE_MX:
case TYPE_CNAME:
case TYPE_HINFO:
case TYPE_PTR:
case TYPE_NS:
case TYPE_SOA:
/* So we can do a dom q <dom.name> from another ka9q ILP 17/2/93 */
case TYPE_ANY:
/* Not all of the below types are implemented in resolve_rr() */
#ifdef notdef
case TYPE_TXT:
#endif
if((rrp = resolve_rr(qp->name,qp->type)) != NULLRR) {
/* we found an entry, go tell him */
dhdr->rcode = NO_ERROR;
dhdr->qr = RESPONSE;
rrp->ttl = expire;
} else {
/* we did not find an entry, go tell him */
rrp = (struct rr *)callocw(1,sizeof(struct rr));
rrp->name = strdup(qp->name);
rrp->type = qp->type;
rrp->class = qp->class;
rrp->ttl = 500L;
rrp->rdata.addr = 0L;
rrp->rdlength = 4; /* size of addr data */
dhdr->rcode = NAME_ERROR;
dhdr->qr = RESPONSE;
}
rrans = rrp;
break;
/* Search only the local cache and domain file for these next few */
/* Is this a good idea ??? */
case TYPE_MB:
case TYPE_MG:
case TYPE_MR:
rrp = make_rr(RR_QUERY,qp->name,CLASS_IN,qp->type,0,0,NULL);
if((rrans = dcache_search(rrp)) == NULLRR){
rrans = dfile_search(rrp, NULL);
}
free_rr(rrp);
break;
#ifdef notdef
/* Not implemented, yet (?) */
case TYPE_MD:
case TYPE_MF:
case TYPE_WKS:
case TYPE_NULL:
case TYPE_MINFO:
#endif
default:
dhdr->rcode = NOT_IMPL;
dhdr->qr = RESPONSE;
}
/* This loop does NOT support multiple questions yet */
#ifdef notdef
qp = qp->next; /* next question */
}
#endif
/* Find the number of answer records */
i = 0;
rrtmp = rrans;
while(rrtmp != NULLRR) {
i++;
rrtmp = rrtmp->next;
}
dhdr->ancount = i;
dhdr->answers = rrans;
/* Authority & Additional RR's not implemented yet. */
dhdr->authority = NULLRR;
dhdr->nscount = 0;
dhdr->arcount = 0;
dhdr->additional = NULLRR;
#ifdef notdef
/* Find the number of authority records */
i = 0;
rrtmp = rrns;
while(rrtmp != NULLRR) {
i++;
rrtmp = rrtmp->next;
}
dhdr->nscount = i;
dhdr->authority = rrns;
/* Find the number of additional records */
i = 0;
rrtmp = rradd;
while(rrtmp != NULLRR) {
i++;
rrtmp = rrtmp->next;
}
dhdr->arcount = i;
dhdr->additional = rradd;
#endif
if(Dtrace) {
tputs("DNS: replying");
dumpdomain(dhdr,0);
}
/* Maximum reply size is 512, see rfc1034/1035 */
buf = mallocw(512);
len = htondomain(dhdr,buf,512);
free_dhdr(dhdr);
server.sin_family = AF_INET;
server.sin_port = dp->port;
server.sin_addr.s_addr = dp->address;
sendto(Dsocket,buf,len,0,(char *)&server,sizeof(server));
free(buf);
free((char *)dp);
}
/* Process to receive all domain server related messages */
static void
drx(unused,u,p)
int unused;
void *u;
void *p;
{
struct sockaddr_in sock,from;
int fromlen;
struct mbuf *bp;
struct dhdr *dhdr;
struct dserver *dp;
int foo;
Dsocket = socket(AF_INET,SOCK_DGRAM,0);
sock.sin_family = AF_INET;
sock.sin_addr.s_addr = INADDR_ANY;
sock.sin_port = IPPORT_DOMAIN;
if(bind(Dsocket,(char *)&sock,sizeof(sock)) == -1) {
tputs("DNS: can't bind\n");
Drx = NULLPROC;
return;
}
/* Now loop forever, processing queries */
for(;;){
fromlen = sizeof(from);
if((foo = recv_mbuf(Dsocket,&bp,0,(char *)&from,&fromlen)) == -1)
break; /* Server closing */
if(foo == 0)
continue;
dhdr = mallocw(sizeof(struct dhdr));
ntohdomain(dhdr,&bp);
if(Dtrace) {
tprintf("DNS: %u bytes from %s\n",foo,
psocket((struct sockaddr *)&from));
dumpdomain(dhdr,0);
}
/* Process queries only */
if(dhdr->qr == QUERY) {
/* Queries from ourself will cause a loop ! */
if(ismyaddr(from.sin_addr.s_addr) != NULLIF) {
if(Dtrace)
tputs("DNS: question from myself ignored\n");
free_dhdr(dhdr);
continue;
} else {
dp=(struct dserver *)callocw(1,sizeof(struct dserver));
dp->address = from.sin_addr.s_addr;
dp->srtt = (Dserver_maxwait * 1000L) / MSPTICK;
dp->timeout = dp->srtt * 2;
dp->port = from.sin_port;
if(dhdr->opcode == ZONEINIT) {
/* ZONEINIT not implemented */
free_dhdr(dhdr);
free(dp);
} else
newproc("Domain server",1024,proc_query
,0,(void *)dp,(void *)dhdr,0);
}
}
}
}
static int
dodnsserver(argc,argv,p)
int argc;
char *argv[];
void *p;
{
struct DOM_FILE *t_dom_file;
FILE *fp, *bfp;
char *t, *t1, *t2,*t3, *t4;
int done;
int32 source;
struct rr *frrp, *oldrrp;
#if 0
struct soa *soap;
#endif
char type, *delim = " \t";
if(Drx == NULLPROC)
{
/* Get rid of any old data */
t_dom_file = dom_file;
while (t_dom_file)
{
free(t_dom_file->filename);
free(t_dom_file->domain);
free(t_dom_file);
t_dom_file = t_dom_file->next;
}
dom_file = NULL;
/*
Find out where we're primary for (if anywhere)
*/
t = mallocw(200);
t2 = mallocw(200);
t4 = mallocw(13);
bfp = fopen(Dboot, READ_TEXT);
if (bfp)
while (1)
{
if (feof(bfp) || !fgets(t, 199, bfp))
{
fclose(bfp);
break;
}
t1 = strtok(t, delim);
if (!t1)
continue;
source = 0;
type = '0';
if (stricmp(t1, "primary") == 0)
type = '1';
if (stricmp(t1, "secondary") == 0)
type = '2';
if (type == '0')
continue;
t1 = strtok(NULL, delim);
if (!t1)
continue;
strlwr(t1);
strcpy(t2, t1);
t1 = strtok(NULL, delim);
if (!t1)
continue;
if (type == '2')
{
source = resolve(t1);
t1 = strtok(NULL, delim);
if (!t1)
continue;
}
rip(t1);
strcpy(t4, t1);
sprintf(t, "%s/%s", Dzones, t1);
t3 = t - 1;
while (*++t3)
if (*t3 == '\\')
*t3 = '/';
if (Dtrace)
tprintf("DNS: Looking for SOA record in %s\n", t);
fp = fopen(t, READ_TEXT);
if (fp)
{
done = 0;
oldrrp = NULLRR;
while(done == 0
&& (frrp = get_rr(fp,oldrrp)) != NULLRR)
{
oldrrp = frrp;
if (frrp->type == TYPE_SOA)
{
done = 1;
if (dom_file == NULL)
{
dom_file = mallocw(sizeof(struct DOM_FILE));
dom_file->next = NULL;
}
else {
t_dom_file = mallocw(sizeof(struct DOM_FILE));
t_dom_file->next = dom_file;
dom_file = t_dom_file;
}
dom_file->filename = mallocw(13);
strcpy(dom_file->filename, t4);
dom_file->domain = mallocw(strlen(t2) + 2);
sprintf(dom_file->domain, "%s.", t2);
dom_file->dom_soa = frrp->rdata.soa;
dom_file->type = type;
dom_file->source = source;
if (Dtrace)
{
tprintf(" filename %s\n", dom_file->filename);
tprintf(" mname %s\n", dom_file->dom_soa.mname);
tprintf(" rname %s\n", dom_file->dom_soa.rname);
tprintf(" serial %lu\n", dom_file->dom_soa.serial);
tprintf(" refresh %lu\n", dom_file->dom_soa.refresh);
tprintf(" retry %lu\n", dom_file->dom_soa.retry);
tprintf(" expire %lu\n", dom_file->dom_soa.expire);
tprintf(" minimum %lu\n", dom_file->dom_soa.minimum);
}
}
}
fclose(fp);
}
else tprintf("failed to open\n");
}
/* Start domain server task */
free(t4);
free(t2);
free(t);
Drx = newproc("Domain listener",1024,drx,0,NULL,NULL,0);
}
else tputs("DNS already running\n");
return 0;
}
static int
dostopdnsserver(argc,argv,p)
int argc;
char *argv[];
void *p;
{
if(Drx == NULLPROC)
/* Start domain server task */
tprintf("DNS not running\n");
killproc(Drx);
Drx = NULLPROC;
return 0;
}
/* Free a domain message */
static void
free_dhdr(dp)
struct dhdr *dp;
{
if(dp->qdcount != 0)
free_rr(dp->questions);
if(dp->ancount != 0)
free_rr(dp->answers);
if(dp->nscount != 0)
free_rr(dp->authority);
if(dp->arcount != 0)
free_rr(dp->additional);
free((char *)dp);
}
#endif