freebsd-dev/lib/libc/net/res_update.c
Daniel Eischen d201fe46e3 Remove _THREAD_SAFE and make libc thread-safe by default by
adding (weak definitions to) stubs for some of the pthread
functions.  If the threads library is linked in, the real
pthread functions will pulled in.

Use the following convention for system calls wrapped by the
threads library:
	__sys_foo - actual system call
	_foo - weak definition to __sys_foo
	foo - weak definition to __sys_foo

Change all libc uses of system calls wrapped by the threads
library from foo to _foo.  In order to define the prototypes
for _foo(), we introduce namespace.h and un-namespace.h
(suggested by bde).  All files that need to reference these
system calls, should include namespace.h before any standard
includes, then include un-namespace.h after the standard
includes and before any local includes.  <db.h> is an exception
and shouldn't be included in between namespace.h and
un-namespace.h  namespace.h will define foo to _foo, and
un-namespace.h will undefine foo.

Try to eliminate some of the recursive calls to MT-safe
functions in libc/stdio in preparation for adding a mutex
to FILE.  We have recursive mutexes, but would like to avoid
using them if possible.

Remove uneeded includes of <errno.h> from a few files.

Add $FreeBSD$ to a few files in order to pass commitprep.

Approved by:	-arch
2001-01-24 13:01:12 +00:00

516 lines
14 KiB
C

#if !defined(lint) && !defined(SABER)
static char rcsid[] = "$FreeBSD$";
#endif /* not lint */
/*
* Copyright (c) 1996 by Internet Software Consortium.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
* ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
* CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
* PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
* ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
* SOFTWARE.
*/
/*
* Based on the Dynamic DNS reference implementation by Viraj Bais
* <viraj_bais@ccm.fm.intel.com>
*/
#include <sys/param.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <arpa/nameser.h>
#include <limits.h>
#include <netdb.h>
#include <resolv.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/*
* Separate a linked list of records into groups so that all records
* in a group will belong to a single zone on the nameserver.
* Create a dynamic update packet for each zone and send it to the
* nameservers for that zone, and await answer.
* Abort if error occurs in updating any zone.
* Return the number of zones updated on success, < 0 on error.
*
* On error, caller must deal with the unsynchronized zones
* eg. an A record might have been successfully added to the forward
* zone but the corresponding PTR record would be missing if error
* was encountered while updating the reverse zone.
*/
#define NSMAX 16
struct ns1 {
char nsname[MAXDNAME];
struct in_addr nsaddr1;
};
struct zonegrp {
char z_origin[MAXDNAME];
int16_t z_class;
char z_soardata[MAXDNAME + 5 * INT32SZ];
struct ns1 z_ns[NSMAX];
int z_nscount;
ns_updrec * z_rr;
struct zonegrp *z_next;
};
int
res_update(ns_updrec *rrecp_in) {
ns_updrec *rrecp, *tmprrecp;
u_char buf[PACKETSZ], answer[PACKETSZ], packet[2*PACKETSZ];
char name[MAXDNAME], zname[MAXDNAME], primary[MAXDNAME],
mailaddr[MAXDNAME];
u_char soardata[2*MAXCDNAME+5*INT32SZ];
char *dname, *svdname, *cp1, *target;
u_char *cp, *eom;
HEADER *hp = (HEADER *) answer;
struct zonegrp *zptr = NULL, *tmpzptr, *prevzptr, *zgrp_start = NULL;
int i, j, k = 0, n, ancount, nscount, arcount, rcode, rdatasize,
newgroup, done, myzone, seen_before, numzones = 0;
u_int16_t dlen, class, qclass, type, qtype;
u_int32_t ttl;
if ((_res.options & RES_INIT) == 0 && res_init() == -1) {
h_errno = NETDB_INTERNAL;
return (-1);
}
for (rrecp = rrecp_in; rrecp; rrecp = rrecp->r_next) {
dname = rrecp->r_dname;
n = strlen(dname);
if (dname[n-1] == '.')
dname[n-1] = '\0';
qtype = T_SOA;
qclass = rrecp->r_class;
done = 0;
seen_before = 0;
while (!done && dname) {
if (qtype == T_SOA) {
for (tmpzptr = zgrp_start;
tmpzptr && !seen_before;
tmpzptr = tmpzptr->z_next) {
if (strcasecmp(dname,
tmpzptr->z_origin) == 0 &&
tmpzptr->z_class == qclass)
seen_before++;
for (tmprrecp = tmpzptr->z_rr;
tmprrecp && !seen_before;
tmprrecp = tmprrecp->r_grpnext)
if (strcasecmp(dname, tmprrecp->r_dname) == 0
&& tmprrecp->r_class == qclass) {
seen_before++;
break;
}
if (seen_before) {
/*
* Append to the end of
* current group.
*/
for (tmprrecp = tmpzptr->z_rr;
tmprrecp->r_grpnext;
tmprrecp = tmprrecp->r_grpnext)
(void)NULL;
tmprrecp->r_grpnext = rrecp;
rrecp->r_grpnext = NULL;
done = 1;
break;
}
}
} else if (qtype == T_A) {
for (tmpzptr = zgrp_start;
tmpzptr && !done;
tmpzptr = tmpzptr->z_next)
for (i = 0; i < tmpzptr->z_nscount; i++)
if (tmpzptr->z_class == qclass &&
strcasecmp(tmpzptr->z_ns[i].nsname,
dname) == 0 &&
tmpzptr->z_ns[i].nsaddr1.s_addr != 0) {
zptr->z_ns[k].nsaddr1.s_addr =
tmpzptr->z_ns[i].nsaddr1.s_addr;
done = 1;
break;
}
}
if (done)
break;
n = res_mkquery(QUERY, dname, qclass, qtype, NULL,
0, NULL, buf, sizeof buf);
if (n <= 0) {
fprintf(stderr, "res_update: mkquery failed\n");
return (n);
}
n = res_send(buf, n, answer, sizeof answer);
if (n < 0) {
fprintf(stderr, "res_update: send error for %s\n",
rrecp->r_dname);
return (n);
}
if (n < HFIXEDSZ)
return (-1);
ancount = ntohs(hp->ancount);
nscount = ntohs(hp->nscount);
arcount = ntohs(hp->arcount);
rcode = hp->rcode;
cp = answer + HFIXEDSZ;
eom = answer + n;
/* skip the question section */
n = dn_skipname(cp, eom);
if (n < 0 || cp + n + 2 * INT16SZ > eom)
return (-1);
cp += n + 2 * INT16SZ;
if (qtype == T_SOA) {
if (ancount == 0 && nscount == 0 && arcount == 0) {
/*
* if (rcode == NOERROR) then the dname exists but
* has no soa record associated with it.
* if (rcode == NXDOMAIN) then the dname does not
* exist and the server is replying out of NCACHE.
* in either case, proceed with the next try
*/
dname = strchr(dname, '.');
if (dname != NULL)
dname++;
continue;
} else if ((rcode == NOERROR || rcode == NXDOMAIN) &&
ancount == 0 &&
nscount == 1 && arcount == 0) {
/*
* name/data does not exist, soa record supplied in the
* authority section
*/
/* authority section must contain the soa record */
if ((n = dn_expand(answer, eom, cp, zname,
sizeof zname)) < 0)
return (n);
cp += n;
if (cp + 2 * INT16SZ > eom)
return (-1);
GETSHORT(type, cp);
GETSHORT(class, cp);
if (type != T_SOA || class != qclass) {
fprintf(stderr, "unknown answer\n");
return (-1);
}
myzone = 0;
svdname = dname;
while (dname)
if (strcasecmp(dname, zname) == 0) {
myzone = 1;
break;
} else if ((dname = strchr(dname, '.')) != NULL)
dname++;
if (!myzone) {
dname = strchr(svdname, '.');
if (dname != NULL)
dname++;
continue;
}
nscount = 0;
/* fallthrough */
} else if (rcode == NOERROR && ancount == 1) {
/*
* found the zone name
* new servers will supply NS records for the zone
* in authority section and A records for those
* nameservers in the additional section
* older servers have to be explicitly queried for
* NS records for the zone
*/
/* answer section must contain the soa record */
if ((n = dn_expand(answer, eom, cp, zname,
sizeof zname)) < 0)
return (n);
else
cp += n;
if (cp + 2 * INT16SZ > eom)
return (-1);
GETSHORT(type, cp);
GETSHORT(class, cp);
if (type == T_CNAME) {
dname = strchr(dname, '.');
if (dname != NULL)
dname++;
continue;
}
if (strcasecmp(dname, zname) != 0 ||
type != T_SOA ||
class != rrecp->r_class) {
fprintf(stderr, "unknown answer\n");
return (-1);
}
/* FALLTHROUGH */
} else {
fprintf(stderr,
"unknown response: ans=%d, auth=%d, add=%d, rcode=%d\n",
ancount, nscount, arcount, hp->rcode);
return (-1);
}
if (cp + INT32SZ + INT16SZ > eom)
return (-1);
/* continue processing the soa record */
GETLONG(ttl, cp);
GETSHORT(dlen, cp);
if (cp + dlen > eom)
return (-1);
newgroup = 1;
zptr = zgrp_start;
prevzptr = NULL;
while (zptr) {
if (strcasecmp(zname, zptr->z_origin) == 0 &&
type == T_SOA && class == qclass) {
newgroup = 0;
break;
}
prevzptr = zptr;
zptr = zptr->z_next;
}
if (!newgroup) {
for (tmprrecp = zptr->z_rr;
tmprrecp->r_grpnext;
tmprrecp = tmprrecp->r_grpnext)
;
tmprrecp->r_grpnext = rrecp;
rrecp->r_grpnext = NULL;
done = 1;
cp += dlen;
break;
} else {
if ((n = dn_expand(answer, eom, cp, primary,
sizeof primary)) < 0)
return (n);
cp += n;
/*
* We don't have to bounds check here because the
* next use of 'cp' is in dn_expand().
*/
cp1 = (char *)soardata;
strcpy(cp1, primary);
cp1 += strlen(cp1) + 1;
if ((n = dn_expand(answer, eom, cp, mailaddr,
sizeof mailaddr)) < 0)
return (n);
cp += n;
strcpy(cp1, mailaddr);
cp1 += strlen(cp1) + 1;
if (cp + 5*INT32SZ > eom)
return (-1);
memcpy(cp1, cp, 5*INT32SZ);
cp += 5*INT32SZ;
cp1 += 5*INT32SZ;
rdatasize = (u_char *)cp1 - soardata;
zptr = calloc(1, sizeof(struct zonegrp));
if (zptr == NULL)
return (-1);
if (zgrp_start == NULL)
zgrp_start = zptr;
else
prevzptr->z_next = zptr;
zptr->z_rr = rrecp;
rrecp->r_grpnext = NULL;
strcpy(zptr->z_origin, zname);
zptr->z_class = class;
memcpy(zptr->z_soardata, soardata, rdatasize);
/* fallthrough to process NS and A records */
}
} else if (qtype == T_NS) {
if (rcode == NOERROR && ancount > 0) {
strcpy(zname, dname);
for (zptr = zgrp_start; zptr; zptr = zptr->z_next) {
if (strcasecmp(zname, zptr->z_origin) == 0)
break;
}
if (zptr == NULL)
/* should not happen */
return (-1);
if (nscount > 0) {
/*
* answer and authority sections contain
* the same information, skip answer section
*/
for (j = 0; j < ancount; j++) {
n = dn_skipname(cp, eom);
if (n < 0)
return (-1);
n += 2*INT16SZ + INT32SZ;
if (cp + n + INT16SZ > eom)
return (-1);
cp += n;
GETSHORT(dlen, cp);
cp += dlen;
}
} else
nscount = ancount;
/* fallthrough to process NS and A records */
} else {
fprintf(stderr, "cannot determine nameservers for %s:\
ans=%d, auth=%d, add=%d, rcode=%d\n",
dname, ancount, nscount, arcount, hp->rcode);
return (-1);
}
} else if (qtype == T_A) {
if (rcode == NOERROR && ancount > 0) {
arcount = ancount;
ancount = nscount = 0;
/* fallthrough to process A records */
} else {
fprintf(stderr, "cannot determine address for %s:\
ans=%d, auth=%d, add=%d, rcode=%d\n",
dname, ancount, nscount, arcount, hp->rcode);
return (-1);
}
}
/* process NS records for the zone */
j = 0;
for (i = 0; i < nscount; i++) {
if ((n = dn_expand(answer, eom, cp, name,
sizeof name)) < 0)
return (n);
cp += n;
if (cp + 3 * INT16SZ + INT32SZ > eom)
return (-1);
GETSHORT(type, cp);
GETSHORT(class, cp);
GETLONG(ttl, cp);
GETSHORT(dlen, cp);
if (cp + dlen > eom)
return (-1);
if (strcasecmp(name, zname) == 0 &&
type == T_NS && class == qclass) {
if ((n = dn_expand(answer, eom, cp,
name, sizeof name)) < 0)
return (n);
target = zptr->z_ns[j++].nsname;
strcpy(target, name);
}
cp += dlen;
}
if (zptr->z_nscount == 0)
zptr->z_nscount = j;
/* get addresses for the nameservers */
for (i = 0; i < arcount; i++) {
if ((n = dn_expand(answer, eom, cp, name,
sizeof name)) < 0)
return (n);
cp += n;
if (cp + 3 * INT16SZ + INT32SZ > eom)
return (-1);
GETSHORT(type, cp);
GETSHORT(class, cp);
GETLONG(ttl, cp);
GETSHORT(dlen, cp);
if (cp + dlen > eom)
return (-1);
if (type == T_A && dlen == INT32SZ && class == qclass) {
for (j = 0; j < zptr->z_nscount; j++)
if (strcasecmp(name, zptr->z_ns[j].nsname) == 0) {
memcpy(&zptr->z_ns[j].nsaddr1.s_addr, cp,
INT32SZ);
break;
}
}
cp += dlen;
}
if (zptr->z_nscount == 0) {
dname = zname;
qtype = T_NS;
continue;
}
done = 1;
for (k = 0; k < zptr->z_nscount; k++)
if (zptr->z_ns[k].nsaddr1.s_addr == 0) {
done = 0;
dname = zptr->z_ns[k].nsname;
qtype = T_A;
}
} /* while */
}
_res.options |= RES_DEBUG;
for (zptr = zgrp_start; zptr; zptr = zptr->z_next) {
/* append zone section */
rrecp = res_mkupdrec(ns_s_zn, zptr->z_origin,
zptr->z_class, ns_t_soa, 0);
if (rrecp == NULL) {
fprintf(stderr, "saverrec error\n");
fflush(stderr);
return (-1);
}
rrecp->r_grpnext = zptr->z_rr;
zptr->z_rr = rrecp;
n = res_mkupdate(zptr->z_rr, packet, sizeof packet);
if (n < 0) {
fprintf(stderr, "res_mkupdate error\n");
fflush(stderr);
return (-1);
} else
fprintf(stdout, "res_mkupdate: packet size = %d\n", n);
/*
* Override the list of NS records from res_init() with
* the authoritative nameservers for the zone being updated.
* Sort primary to be the first in the list of nameservers.
*/
for (i = 0; i < zptr->z_nscount; i++) {
if (strcasecmp(zptr->z_ns[i].nsname,
zptr->z_soardata) == 0) {
struct in_addr tmpaddr;
if (i != 0) {
strcpy(zptr->z_ns[i].nsname,
zptr->z_ns[0].nsname);
strcpy(zptr->z_ns[0].nsname,
zptr->z_soardata);
tmpaddr = zptr->z_ns[i].nsaddr1;
zptr->z_ns[i].nsaddr1 =
zptr->z_ns[0].nsaddr1;
zptr->z_ns[0].nsaddr1 = tmpaddr;
}
break;
}
}
for (i = 0; i < MAXNS; i++) {
_res.nsaddr_list[i].sin_addr = zptr->z_ns[i].nsaddr1;
_res.nsaddr_list[i].sin_family = AF_INET;
_res.nsaddr_list[i].sin_port = htons(NAMESERVER_PORT);
}
_res.nscount = (zptr->z_nscount < MAXNS) ?
zptr->z_nscount : MAXNS;
n = res_send(packet, n, answer, sizeof(answer));
if (n < 0) {
fprintf(stderr, "res_send: send error, n=%d\n", n);
break;
} else
numzones++;
}
/* free malloc'ed memory */
while(zgrp_start) {
zptr = zgrp_start;
zgrp_start = zgrp_start->z_next;
res_freeupdrec(zptr->z_rr); /* Zone section we allocated. */
free((char *)zptr);
}
return (numzones);
}