BSD 4_3 release
[unix-history] / usr / src / etc / named / ns_req.c
#ifndef lint
static char sccsid[] = "@(#)ns_req.c 4.3 (Berkeley) 5/30/86";
#endif
/*
* Copyright (c) 1986 Regents of the University of California
* All Rights Reserved
*/
#include <stdio.h>
#include <sys/types.h>
#include <sys/uio.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <syslog.h>
#include <sys/file.h>
#include <arpa/nameser.h>
#include "ns.h"
#include "db.h"
#define NADDRECS 20
struct addinfo {
char *a_dname; /* domain name */
u_short a_class; /* class for address */
};
struct addinfo addinfo[NADDRECS]; /* additional info records */
int addcount; /* number of names in addinfo */
char *dnptrs[20]; /* ptrs to dnames in message for dn_comp */
extern char *malloc();
extern int errno;
/*
* Process request using database; assemble and send response.
*/
ns_req(msg, msglen, buflen, qsp, from)
char *msg;
int msglen, buflen;
struct qstream *qsp;
struct sockaddr_in *from;
{
register HEADER *hp;
register char *cp;
register struct namebuf *np;
register struct databuf *dp;
struct databuf *tmp;
struct databuf *pdp;
struct hashbuf *htp;
struct zoneinfo *zp;
char *fname;
char dnbuf[MAXDNAME], *dname;
char **dpp;
char *dnp;
char *newmsg;
int n, class, type, count, foundname, founddata, cname = 0;
int newmsglen;
u_short id;
struct databuf *nsp[MAXNS], **nspp;
struct qinfo *qp;
time_t curtime;
extern struct qinfo *qhead;
extern int nsid;
#ifdef DEBUG
if (debug > 3) {
fprintf(ddt,"ns_req()\n");
fp_query(msg, ddt);
}
#endif
hp = (HEADER *) msg;
if (hp->qr) {
ns_resp(msg, msglen);
return;
}
cp = msg + sizeof(HEADER);
hp->rcode = NOERROR;
dpp = dnptrs;
*dpp++ = msg;
addcount = 0;
switch (hp->opcode) {
case QUERY:
if (ntohs(hp->qdcount) != 1 ||
hp->ancount || hp->nscount || hp->arcount) {
#ifdef DEBUG
if (debug)
fprintf(ddt,"FORMERR Query header counts wrong\n");
#endif
hp->qdcount = 0;
hp->ancount = 0;
hp->nscount = 0;
hp->arcount = 0;
hp->rcode = FORMERR;
break;
}
/*
* Get domain name, class, and type.
*/
if ((*cp & INDIR_MASK) == 0)
*dpp++ = cp; /* remember name for compression */
*dpp = NULL;
if ((n = dn_expand(msg, msg + msglen, cp, dnbuf,
sizeof(dnbuf))) < 0) {
#ifdef DEBUG
if (debug)
fprintf(ddt,"FORMERR Query expand name failed\n");
#endif
hp->rcode = FORMERR;
break;
}
cp += n;
type = getshort(cp);
cp += sizeof(u_short);
class = getshort(cp);
cp += sizeof(u_short);
if (cp < msg + msglen) {
#ifdef DEBUG
if (debug)
fprintf(ddt,"FORMERR Query message length off\n");
#endif
hp->rcode = FORMERR;
break;
}
#ifdef DEBUG
if (cp > msg + msglen)
if (debug > 5)
fprintf(ddt,"message length > recived message\n");
#endif
#ifdef notdef
if (!check_class(quest.qtype->class)) {
if (debug)
fprintf(ddt,"FORMERR Query class is wrong\n");
hp->rcode = FORMERR;
break;
}
if (!check_type(quest.qtype->type)) {
if (debug)
fprintf(ddt,"proc_query FORMERR Query type wrong\n");
hp->rcode = FORMERR;
break;
}
#endif
/*
* Process query.
*/
if (type == T_AXFR) {
/* refuse request if not a TCP connection */
if (qsp == QSTREAM_NULL) {
hp->rcode = REFUSED;
break;
}
/* don't compress names */
dnptrs[0] = NULL;
}
buflen -= msglen;
count = 0;
foundname = 0;
founddata = 0;
dname = dnbuf;
again:
#ifdef DEBUG
if (debug)
fprintf(ddt,"nlookup(%s)\n", dname);
#endif
htp = hashtab; /* lookup relative to root */
np = nlookup(dname, &htp, &fname, 0);
/*
* if nlookup failed to find address then
* see if there are any '.''s in the name
* if not then add local domain name to the
* name and try again.
*/
if (np == NULL) {
fname = "";
dnp = &dname[strlen(dname)];
if ( index(dname, '.') == NULL)
for (zp = zones; zp < &zones[nzones]; zp++) {
if ( zp->z_type == Z_DOMAIN){
#ifdef DEBUG
if (debug >= 5)
fprintf(ddt,"domain = %s\n",
zp->z_origin);
#endif
(void) strcat(dname,".");
(void) strcat(dname,zp->z_origin);
#ifdef DEBUG
if (debug)
fprintf(ddt,"nlookup(%s)\n", dname);
#endif
np = nlookup(dname, &htp, &fname, 0);
if (np != NULL)
break;
fname = "";
*dnp = '\0';
}
}
}
if (fname != dname) {
#ifdef DEBUG
if (debug && cname) {
if (founddata)
fprintf(ddt,"CNAME with data\n");
else
fprintf(ddt,"CNAME without data %s\n", dname);
}
#endif
goto findns;
}
#ifdef DEBUG
if (debug)
fprintf(ddt,"found '%s'\n", dname);
#endif
foundname++;
pdp = NULL;
dp = np->n_data;
/* look for the data */
while (dp != NULL) {
if (!wanted(dp, class, type)) {
pdp = dp;
dp = dp->d_next;
continue;
}
if ((n = make_rr(dname, dp, cp, buflen, 1)) < 0) {
if (n == -1) {
hp->tc = 1;
break;
}
/* delete old cache entry */
#ifdef DEBUG
if (debug)
fprintf(ddt,"deleting cache entry\n");
#endif
rminv(dp);
tmp = dp->d_next;
(void) free((char *)dp);
dp = tmp;
if (pdp == NULL)
np->n_data = dp;
else
pdp->d_next = dp;
continue;
}
cp += n;
buflen -= n;
count++;
if (dp->d_zone)
hp->aa = 1;
if (dp->d_type == T_CNAME) {
if (type == T_ANY)
break;
cname++;
dname = dp->d_data;
goto again;
}
founddata++;
pdp = dp;
dp = dp->d_next;
}
#ifdef DEBUG
if (debug >= 5) {
fprintf(ddt,"foundname = %d count = %d ", foundname, count);
fprintf(ddt,"founddata = %d cname = %d\n",founddata, cname);
}
#endif
if (count) {
hp->ancount = htons((u_short)count);
if (type == T_AXFR && founddata) {
#ifdef DEBUG
if (debug >= 5)
fprintf(ddt,"doing axfr()\n");
#endif
/*
* child does the work while
* the parent continues
*/
if (fork() == 0) {
register FILE *rfp;
int fdstat;
rfp = fdopen(qsp->s_rfd, "w");
setproctitle("zone XFR to", qsp->s_rfd);
fdstat = fcntl(qsp->s_rfd, F_GETFL, 0);
if (fdstat != -1)
(void) fcntl(qsp->s_rfd, F_SETFL,
fdstat & ~FNDELAY);
fwritemsg(rfp, msg, cp - msg);
doaxfr(np, rfp, 1);
fwritemsg(rfp, msg, cp - msg);
(void) fflush(rfp);
exit(0);
}
qsp->s_time = tt.tv_sec;
qsp->s_refcnt--;
return;
}
if (hp->aa)
break;
}
findns:
/*
* Look for name servers to refer to and
* fill in the authority section or record the address
* for forwarding the query (recursion desired).
*/
for (count = 0; ; np = np->n_parent) {
#ifdef DEBUG
if (debug >= 5)
fprintf(ddt,"fname = '%s'\n", fname);
#endif
if (*fname == '\0') {
for (np = hashtab->h_tab[0]; np != NULL;
np = np->n_next)
if ( np->n_dname[0] == '\0')
goto foundns;
#ifdef DEBUG
if (debug)
fprintf(ddt,"No root nameserver?\n");
#endif
syslog(LOG_ERR,"No root Nameserver\n");
hp->rcode = SERVFAIL;
break;
}
foundns:
nspp = nsp; /* record ns records if forwarding */
pdp = NULL;
dp = np->n_data;
curtime =(u_long) tt.tv_sec;
while (dp != NULL) {
if (dp->d_zone && match(dp, class, T_SOA)) {
if (!foundname)
hp->rcode = NXDOMAIN;
hp->aa = 1;
goto finish;
}
if (!match(dp, class, T_NS)) {
pdp = dp;
dp = dp->d_next;
continue;
}
if ((dp->d_zone == 0) &&
(dp->d_ttl < curtime)) {
/* delete old cache entry */
#ifdef DEBUG
if (debug)
fprintf(ddt,"deleting cache entry\n");
#endif
rminv(dp);
tmp = dp->d_next;
(void) free((char *)dp);
dp = tmp;
if (pdp == NULL)
np->n_data = dp;
else
pdp->d_next = dp;
continue;
}
if (hp->rd && !founddata) {
if (nspp < &nsp[MAXNS-1])
*nspp++ = dp;
pdp = dp;
dp = dp->d_next;
continue;
}
if ((n = make_rr(fname, dp, cp, buflen, 1)) < 0) {
if (n == -1) {
hp->tc = 1;
break;
}
/* delete old cache entry */
#ifdef DEBUG
if (debug)
fprintf(ddt,"deleting cache entry\n");
#endif
rminv(dp);
tmp = dp->d_next;
(void) free((char *)dp);
dp = tmp;
if (pdp == NULL)
np->n_data = dp;
else
pdp->d_next = dp;
continue;
}
cp += n;
buflen -= n;
count++;
pdp = dp;
dp = dp->d_next;
}
if (count && founddata) {
hp->nscount = htons((u_short)count);
break;
}
if (nspp != nsp) {
*nspp = NULL;
if (cname) {
id = hp->id;
for (qp = qhead; qp!=QINFO_NULL;
qp = qp->q_link) {
if (qp->q_id == id &&
qp->q_cmsglen == msglen &&
bcmp((char *)qp->q_cmsg+2,
msg+2, msglen-2) == 0)
return;
}
/* build new qinfo struct */
qp = qnew();
qp->q_naddr = 0;
if (nslookup(nsp, qp) == 0) {
#ifdef DEBUG
if (debug >= 5)
fprintf(ddt,"none found in nsp\n");
#endif
qfree(qp);
hp->rcode = SERVFAIL;
break;
}
qp->q_cname++;
qp->q_stream = qsp;
qp->q_curaddr = 0;
qp->q_id = id;
qp->q_from = *from;
if ((qp->q_cmsg = malloc((unsigned)msglen))
== NULL) {
#if DEBUG
if (debug)
fprintf(ddt,"ns_req: malloc fail\n");
#endif
syslog(LOG_ERR, "ns_req: Out Of Memory");
hp->rcode = SERVFAIL;
break;
}
bcopy(msg, qp->q_cmsg, qp->q_cmsglen = msglen);
if ((newmsg = malloc(BUFSIZ)) == NULL) {
#if DEBUG
if (debug)
fprintf(ddt,"ns_req: malloc error\n");
#endif
syslog(LOG_ERR, "ns_req: Out Of Memory");
hp->rcode = SERVFAIL;
goto finish;
}
buflen = BUFSIZ;
dnptrs[0] = newmsg;
dnptrs[1] = NULL;
newmsglen = res_mkquery(QUERY, dname, class,
type, (char *)NULL, 0, NULL,
newmsg, buflen);
qp->q_msg = newmsg;
qp->q_msglen = newmsglen;
hp = (HEADER *) newmsg;
hp->id = qp->q_nsid = htons((u_short)++nsid);
hp->ancount = 0;
hp->nscount = 0;
hp->arcount = 0;
schedretry(qp, (time_t)RETRYTIME);
#ifdef DEBUG
if (debug)
fprintf(ddt,"forw -> %s (%d)\n",
inet_ntoa(qp->q_addr[0].sin_addr),
ntohs(qp->q_addr[0].sin_port));
if ( debug >= 10)
fp_query(newmsg, ddt);
#endif
if (sendto(ds, newmsg, newmsglen, 0,
&qp->q_addr[0], sizeof(qp->q_addr[0])) < 0){
#ifdef DEBUG
if (debug)
fprintf(ddt,"sendto error \n");
#endif
}
return;
}
if ((n = ns_forw(nsp, msg, msglen, from, qsp)) == 0)
return;
if (n == -2) {
hp->rcode = SERVFAIL;
break;
}
}
if (*fname == '\0')
break;
if ((fname = index(fname, '.')) == NULL)
fname = "";
else
fname++;
}
break;
case IQUERY: {
register struct invbuf *ip;
register int i;
int dlen, alen;
char anbuf[PACKETSZ], *data;
if (ntohs(hp->ancount) != 1 ||
hp->qdcount || hp->nscount || hp->arcount) {
#ifdef DEBUG
if (debug)
fprintf(ddt,"FORMERR IQuery header counts wrong\n");
#endif
hp->qdcount = 0;
hp->ancount = 0;
hp->nscount = 0;
hp->arcount = 0;
hp->rcode = FORMERR;
break;
}
/*
* Skip domain name, get class, and type.
*/
if ((n = dn_skip(cp)) < 0) {
#ifdef DEBUG
if (debug)
fprintf(ddt,"FORMERR IQuery packet name problem\n");
#endif
hp->rcode = FORMERR;
break;
}
cp += n;
type = getshort(cp);
cp += sizeof(u_short);
class = getshort(cp);
cp += sizeof(u_short) + sizeof(u_long);
dlen = getshort(cp);
cp += sizeof(u_short) + dlen;
if (cp != msg + msglen) {
#ifdef DEBUG
if (debug)
fprintf(ddt,"FORMERR IQuery message length off\n");
#endif
hp->rcode = FORMERR;
break;
}
/* not all inverse queries are handled. */
switch (type) {
case T_A:
case T_UID:
case T_GID:
break;
default:
hp->rcode = REFUSED;
goto finish;
}
fname = msg + sizeof(HEADER);
bcopy(fname, anbuf, alen = cp - fname);
data = anbuf + alen - dlen;
cp = fname;
buflen -= sizeof(HEADER);
count = 0;
for (ip = invtab[dhash(data, dlen)]; ip != NULL;
ip = ip->i_next) {
for (i = 0; i < INVBLKSZ; i++) {
if ((np = ip->i_dname[i]) == NULL)
break;
#ifdef DEBUG
if (debug >= 5)
fprintf(ddt,"dname = %d\n", np->n_dname);
#endif
for (dp = np->n_data; dp != NULL; dp = dp->d_next) {
if (!match(dp, class, type))
continue;
if (dp->d_size != dlen ||
bcmp(dp->d_data, data, dlen))
continue;
getname(np, dnbuf, sizeof(dnbuf));
buflen -= QFIXEDSZ;
if ((n =
dn_comp(dnbuf, cp, buflen, (char **)NULL,
(char **)NULL)) < 0)
{
hp->tc = 1;
goto finish;
}
cp += n;
putshort((u_short)dp->d_type, cp);
cp += sizeof(u_short);
putshort((u_short)dp->d_class, cp);
cp += sizeof(u_short);
buflen -= n;
count++;
}
}
}
hp->qdcount = htons((u_short)count);
if (alen > buflen) {
hp->tc = 1;
break;
}
bcopy(anbuf, cp, alen);
cp += alen;
break;
}
#ifdef notdef
case UPDATEA:
if ((n =
dn_expand(msg, msg + msglen, cp, dnbuf, sizeof(dnbuf))) < 0)
{
#ifdef DEBUG
if (debug)
fprintf(ddt,"FORMERR UpdateA expand name failed\n");
#endif
hp->rcode = FORMERR;
break;
}
cp += n;
type = getshort(cp);
cp += sizeof(u_short);
class = getshort(cp);
cp += sizeof(u_short);
ttl = getlong(cp);
cp += sizeof(u_long);
n = getshort(cp);
cp += sizeof(u_short);
if (cp + n != msg + msglen) {
#ifdef DEBUG
if (debug)
fprintf(ddt,"FORMERR UpdateA expand name failed\n");
#endif
hp->rcode = FORMERR;
break;
}
dp = savedata(class, type, ttl, cp, n);
dp->d_zone = findzone(dnbuf, class);
if ((n = updatedb(dnbuf, NULL, dp, DB_NODATA)) != NOERROR) {
#ifdef DEBUG
if (debug)
fprintf(ddt,"update failed\n");
#endif
hp->rcode = n;
}
hp->rcode = NOERROR;
break;
case UPDATED:
if ((n =
dn_expand(msg, msg + msglen, cp, dnbuf, sizeof(dnbuf))) < 0)
{
#ifdef DEBUG
if (debug)
fprintf(ddt,"FORMERR UpdateD expand name failed\n");
#endif
hp->rcode = FORMERR;
break;
}
cp += n;
rrec.r_type = getshort(cp);
cp += sizeof(u_short);
rrec.r_class = getshort(cp);
cp += sizeof(u_short) + sizeof(u_long);
rrec.r_size = getshort(cp);
rrec.r_data = cp += sizeof(u_short);
if (cp + rrec.r_size != msg + msglen) {
#ifdef DEBUG
if (debug)
fprintf(ddt,"FORMERR UpdateD message length off\n");
#endif
hp->rcode = FORMERR;
break;
}
if (updatedb(dnbuf, &rrec, NULL, DB_DELETE) < 0) {
#ifdef DEBUG
if (debug)
fprintf(ddt,"update failed\n");
#endif
}
hp->rcode = NOERROR;
break;
case UPDATEM:
if ((n = dn_expand(msg, msg + msglen, cp, addbuf,
sizeof(addbuf))) < 0) {
#ifdef DEBUG
if (debug)
fprintf(ddt,"FORMERR UpdateM expand name 1 failed\n");
#endif
hp->rcode = FORMERR;
break;
}
cp += n;
rrec.r_type = getshort(cp);
cp += sizeof(u_short);
rrec.r_class = getshort(cp);
cp += sizeof(u_short) + sizeof(u_long);
rrec.r_size = getshort(cp);
rrec.r_data = cp += sizeof(u_short);
cp += rrec.r_size;
if ((n =
dn_expand(msg, msg + msglen, cp, dnbuf, sizeof(dnbuf))) < 0)
{
#ifdef DEBUG
if (debug)
fprintf(ddt,"FORMERR UpdateM expand name 2 failed\n");
#endif
hp->rcode = FORMERR;
break;
}
if (cistrcmp(dnbuf, addbuf) != 0) {
#ifdef DEBUG
if (debug)
fprintf(ddt,"FORMERR UpdateM string compair failed\n");
#endif
hp->rcode = FORMERR;
break;
}
cp += n;
type = getshort(cp);
cp += sizeof(u_short);
class = getshort(cp);
cp += sizeof(u_short);
ttl = getlong(cp);
cp += sizeof(u_long);
n = getshort(cp);
cp += sizeof(u_short);
if (cp + n != msg + msglen) {
#ifdef DEBUG
if (debug)
fprintf(ddt,"FORMERR UpdateM message length off\n");
#endif
hp->rcode = FORMERR;
break;
}
dp = savedata(class, type, ttl, cp, n);
dp->d_zone = findzone(dnbuf, class);
if ((n =
updatedb(dnbuf, &rrec, dp, DB_MEXIST|DB_DELETE)) != NOERROR)
{
#ifdef DEBUG
if (debug)
fprintf(ddt,"update failed\n");
#endif
hp->rcode = n;
}
hp->rcode = NOERROR;
break;
#endif
case ZONEREF:
#ifdef DEBUG
if (debug)
fprintf(ddt,"Refresh Zone\n");
#endif
default:
hp->rcode = NOTIMP;
hp->qdcount = 0;
hp->ancount = 0;
hp->nscount = 0;
hp->arcount = 0;
}
finish:
hp->qr = 1; /* set Response flag */
hp->ra = 1; /* Recursion is Available */
if (addcount)
cp += doaddinfo(hp, cp, buflen - (cp - msg));
#ifdef DEBUG
if (debug >= 10)
fp_query(msg, ddt);
#endif
if (qsp == QSTREAM_NULL) {
if (sendto(ds, msg, cp-msg, 0, from, sizeof(*from)) < 0) {
#ifdef DEBUG
if (debug)
fprintf(ddt,"error returning msg\n");
#endif
}
} else {
(void) writemsg(qsp->s_rfd, msg, cp - msg);
qsp->s_time = tt.tv_sec;
qsp->s_refcnt--;
}
}
fwritemsg(rfp, msg, msglen)
FILE *rfp;
char *msg;
int msglen;
{
u_short len = htons((u_short)msglen);
if (fwrite((char *)&len, sizeof(len), 1, rfp) != 1 ||
fwrite(msg, msglen, 1, rfp) != 1) {
#ifdef DEBUG
if (debug)
fprintf(ddt,"fwrite failed %d\n", errno);
#endif
/*
syslog(LOG_ERR, "fwritemsg: write failed: %m");
*/
}
return;
}
writemsg(rfd, msg, msglen)
int rfd;
char *msg;
int msglen;
{
struct iovec iov[2];
u_short len = htons((u_short)msglen);
iov[0].iov_base = (caddr_t)&len;
iov[0].iov_len = sizeof(len);
iov[1].iov_base = msg;
iov[1].iov_len = msglen;
if (writev(rfd, iov, 2) != sizeof(len) + msglen) {
#ifdef DEBUG
if (debug)
fprintf(ddt,"write failed %d\n", errno);
#endif
/*
syslog(LOG_ERR, "writemsg: write failed: %m");
*/
return (-1);
}
return (0);
}
/*
* Copy databuf into a resource record for replies.
* Return size of RR if OK, -1 if buffer is full and
* -2 if its an outdated cache entry.
*/
make_rr(name, dp, buf, buflen, doadd)
char *name;
register struct databuf *dp;
char *buf;
int buflen, doadd;
{
register char *cp;
register struct addinfo *ap;
char *cp1, *sp;
register long n;
u_long ttl;
char **edp = dnptrs + sizeof(dnptrs)/sizeof(dnptrs[0]);
#ifdef DEBUG
if (debug >= 5)
fprintf(ddt,"make_rr(%s, %x, %x, %d, %d) %d\n", name, dp, buf,
buflen, doadd, dp->d_size);
#endif
/* check for outdated RR before updating dnptrs by dn_comp() */
if (dp->d_zone == 0) {
ttl = dp->d_ttl - (u_long) tt.tv_sec;
if (dp->d_ttl < (u_long)tt.tv_sec)
return (-2);
} else {
ttl = zones[dp->d_zone].z_minimum;
if (dp->d_ttl > ttl)
ttl = dp->d_ttl;
}
buflen -= RRFIXEDSZ;
if ((n = dn_comp(name, buf, buflen, (char **)dnptrs, (char **)edp)) < 0)
return (-1);
cp = buf + n;
buflen -= n;
putshort((u_short)dp->d_type, cp);
cp += sizeof(u_short);
putshort((u_short)dp->d_class, cp);
cp += sizeof(u_short);
putlong(ttl, cp);
cp += sizeof(u_long);
sp = cp;
cp += sizeof(u_short);
switch (dp->d_type) {
case T_CNAME:
case T_MG:
case T_MR:
case T_PTR:
if ((n = dn_comp(dp->d_data, cp, buflen, (char **)dnptrs,
(char **)edp)) < 0)
return (-1);
putshort((u_short)n, sp);
cp += n;
break;
case T_MB:
case T_NS:
/* Store domain name in answer */
if ((n = dn_comp(dp->d_data, cp, buflen, (char **)dnptrs,
(char **)edp)) < 0)
return (-1);
putshort((u_short)n, sp);
cp += n;
if (!doadd)
break;
for (ap = addinfo, n = addcount; --n >= 0; ap++)
if (cistrcmp(ap->a_dname, dp->d_data) == 0)
goto found;
/* add domain name to additional section */
if (addcount >= NADDRECS)
break;
addcount++;
ap->a_dname = dp->d_data;
ap->a_class = dp->d_class;
found:
break;
case T_SOA:
case T_MINFO:
cp1 = dp->d_data;
if ((n = dn_comp(cp1, cp, buflen, (char **)dnptrs,
(char **)edp)) < 0)
return (-1);
cp += n;
buflen -= dp->d_type == T_MINFO ? n : n + 5 * sizeof(u_long);
cp1 += strlen(cp1) + 1;
if ((n = dn_comp(cp1, cp, buflen, (char **)dnptrs,
(char **)edp)) < 0)
return (-1);
cp += n;
if (dp->d_type == T_SOA) {
cp1 += strlen(cp1) + 1;
bcopy(cp1, cp,
(int)(n = dp->d_size - (cp1 - dp->d_data)));
cp += n;
}
putshort((u_short)(cp - sp) - sizeof(u_short), sp);
break;
case T_MX:
/* cp1 == our data/ cp == data of RR */
cp1 = dp->d_data;
/* copy preference */
bcopy(cp1,cp,sizeof(u_short));
cp += sizeof(u_short);
cp1 += sizeof(u_short);
buflen -= sizeof(u_short);
if ((n = dn_comp(cp1, cp, buflen, (char **)dnptrs,
(char **)edp)) < 0)
return(-1);
cp += n;
/* save data length */
putshort((u_short)(cp-sp)-sizeof(u_short),sp);
break;
default:
if (dp->d_size > buflen)
return (-1);
bcopy(dp->d_data, cp, dp->d_size);
putshort((u_short)dp->d_size, sp);
cp += dp->d_size;
}
return (cp - buf);
}
/*
* Lookup addresses for names in addinfo and put into the message's
* additional section.
*/
doaddinfo(hp, msg, msglen)
HEADER *hp;
char *msg;
int msglen;
{
register char *cp;
register struct namebuf *np;
register struct databuf *dp;
register struct databuf *tmp;
struct hashbuf *htp;
struct databuf *pdp;
struct addinfo *ap;
char *fname;
int n, count;
#ifdef DEBUG
if (debug >= 3)
fprintf(ddt,"doaddinfo() addcount = %d\n", addcount);
#endif
count = 0;
cp = msg;
for (ap = addinfo; --addcount >= 0; ap++) {
#ifdef DEBUG
if (debug >= 5)
fprintf(ddt,"do additional '%s'\n", ap->a_dname);
#endif
htp = hashtab; /* lookup relative to root */
np = nlookup(ap->a_dname, &htp, &fname, 0);
if (np == NULL || fname != ap->a_dname)
continue;
#ifdef DEBUG
if (debug >= 5)
fprintf(ddt,"found it\n");
#endif
pdp = NULL;
dp = np->n_data;
/* look for the data */
while (dp != NULL) {
if (!match(dp, (int)ap->a_class, T_A)) {
pdp = dp;
dp = dp->d_next;
continue;
}
if ((n = make_rr(ap->a_dname, dp, cp, msglen, 0)) < 0)
{
if (n == -1)
break;
/* delete old cache entry */
#ifdef DEBUG
if (debug)
fprintf(ddt,"deleting cache entry\n");
#endif
rminv(dp);
tmp = dp->d_next;
(void) free((char *)dp);
dp = tmp;
if (pdp == NULL)
np->n_data = dp;
else
pdp->d_next = dp;
continue;
}
#ifdef DEBUG
if (debug >= 5)
fprintf(ddt,"n = %d\n", n);
#endif
cp += n;
msglen -= n;
count++;
pdp = dp;
dp = dp->d_next;
}
}
hp->arcount = htons((u_short)count);
return (cp - msg);
}
/*
* Do we want this data record based on the class and type?
*/
wanted(dp, class, type)
struct databuf *dp;
int class, type;
{
#ifdef DEBUG
if (debug > 3)
fprintf(ddt,"wanted(%x, %d, %d) %d, %d\n", dp, class, type,
dp->d_class, dp->d_type);
#endif
if (dp->d_class != class && class != C_ANY)
return (0);
if (type == dp->d_type)
return (1);
switch (dp->d_type) {
case T_ANY:
case T_CNAME:
return (1);
}
switch (type) {
case T_ANY:
return (1);
case T_MAILB:
switch (dp->d_type) {
case T_MR:
case T_MB:
case T_MG:
case T_MINFO:
return (1);
}
break;
case T_AXFR:
if (dp->d_type == T_SOA)
return (1);
}
return (0);
}
/*
* Get the domain name of 'np' and put in 'buf'.
*/
getname(np, buf, buflen)
struct namebuf *np;
char *buf;
int buflen;
{
register char *cp;
cp = buf;
while (np != NULL) {
if (cp != buf)
*cp++ = '.';
(void) strcpy(cp, np->n_dname);
cp += strlen(cp);
np = np->n_parent;
}
*cp = '\0';
}
/*
* Do a zone transfer. SOA record already sent.
*/
doaxfr(np, rfp, isroot)
register struct namebuf *np;
FILE *rfp;
int isroot;
{
register struct databuf *dp;
register int n;
struct namebuf **npp, **nppend;
char msg[PACKETSZ];
HEADER *hp = (HEADER *) msg;
char *cp;
char dname[MAXDNAME];
int fndns;
#ifdef DEBUG
if (debug && isroot)
fprintf(ddt,"doaxfr()\n");
#endif
fndns = 0;
hp->id = 0;
hp->opcode = QUERY;
hp->qr = hp->aa = hp->tc = hp->ra = hp->pr = hp->rd = 0;
hp->rcode = NOERROR;
hp->qdcount = 0;
hp->ancount = htons(1);
hp->nscount = 0;
hp->arcount = 0;
cp = msg + sizeof(HEADER);
getname(np, dname, sizeof(dname));
/* first do data records */
for (dp = np->n_data; dp != NULL; dp = dp->d_next) {
/* skip the root SOA record (marks end of data) */
if (isroot) {
if (dp->d_type == T_SOA)
continue;
} else if (dp->d_type == T_NS)
fndns = 1;
if (dp->d_zone == 0)
continue;
if ((n = make_rr(dname, dp, cp,
sizeof(msg)-sizeof(HEADER), 0)) < 0)
continue;
fwritemsg(rfp, msg, n + sizeof(HEADER));
}
/* next do subdomains */
if (fndns || np->n_hash == NULL)
return;
npp = np->n_hash->h_tab;
nppend = npp + np->n_hash->h_size;
while (npp < nppend) {
for (np = *npp++; np != NULL; np = np->n_next) {
doaxfr(np, rfp, 0);
}
}
#ifdef DEBUG
if (debug && isroot)
fprintf(ddt,"exit doaxfr()\n");
#endif
}