1
0
Files
irix-657m-src/eoe/lib/ns/dns/init.c
2022-09-29 17:59:04 +03:00

801 lines
17 KiB
C

/*
*
* Copyright 1997-1999 Silicon Graphics, Inc.
* All Rights Reserved.
*
* This is UNPUBLISHED PROPRIETARY SOURCE CODE of Silicon Graphics, Inc.;
* the contents of this file may not be disclosed to third parties, copied or
* duplicated in any form, in whole or in part, without the prior written
* permission of Silicon Graphics, Inc.
*
* RESTRICTED RIGHTS LEGEND:
* Use, duplication or disclosure by the Government is subject to restrictions
* as set forth in subdivision (c)(1)(ii) of the Rights in Technical Data
* and Computer Software clause at DFARS 252.227-7013, and/or in similar or
* successor clauses in the FAR, DOD or NASA FAR Supplement. Unpublished -
* rights reserved under the Copyright Laws of the United States.
*/
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <arpa/nameser.h>
#include <resolv.h>
#include <errno.h>
#include <malloc.h>
#include <ctype.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <netdb.h>
#include <ns_api.h>
#include <ns_daemon.h>
#include "ns_dns.h"
static server_t *servers = (server_t *)0;
static domain_t *domains = (domain_t *)0;
sort_t *sortlist = (sort_t *)0;
int _dns_sock = -1;
int _dns_ndots = 1;
time_t _dns_restime = 0;
extern int dns_query_callback(nsd_file_t **, int);
extern char **strtoargv(char *);
extern struct addrlist *alist, *nosort;
typedef struct dns_xid {
uint16_t xid;
nsd_file_t *rq;
struct dns_xid *next;
} dns_xid_t;
static dns_xid_t *dns_xids = (dns_xid_t *)0;
/*
** This routine will add a new xid record to our list.
*/
int
dns_xid_new(uint16_t xid, nsd_file_t *rq)
{
dns_xid_t *new;
new = (dns_xid_t *)nsd_calloc(1, sizeof(dns_xid_t));
if (! new) {
nsd_logprintf(NSD_LOG_RESOURCE, "dns_xid_new: failed malloc\n");
return NSD_ERROR;
}
new->xid = xid;
new->rq = rq;
new->next = dns_xids;
dns_xids = new;
return NSD_OK;
}
/*
** This routine will look down the xid list and return the request
** associated with an xid. We remove the record if it is found.
*/
nsd_file_t *
dns_xid_lookup(uint16_t xid)
{
dns_xid_t **last, *dx;
nsd_file_t *result=0;
for (last = &dns_xids, dx = dns_xids; dx && (dx->xid != xid);
last = &dx->next, dx = dx->next);
if (dx) {
*last = dx->next;
result = dx->rq;
free(dx);
}
return result;
}
/*
** This routine will copy the current list of servers into the request.
** We copy the list so that if the resolv.conf file changes in the middle
** of a request that we will not get confused.
*/
server_t *
dns_servers(void)
{
server_t *sp, *new, *result, **end;
result = (server_t *)0;
end = (server_t **)0;
for (sp = servers; sp; sp = sp->next) {
new = (server_t *)nsd_malloc(sizeof(server_t));
if (! new) {
nsd_logprintf(NSD_LOG_RESOURCE,
"dns_servers: failed malloc\n");
return (server_t *)0;
}
memcpy(new, sp, sizeof(server_t));
new->next = (server_t *)0;
if (end) {
*end = new;
} else {
result = new;
}
end = &new->next;
}
return result;
}
/*
** Push a given dns server to the bottom of the default server list
** Dont complain if we dont find it, requests can be made to any
** dns server, not just those in the default list.
*/
void
dns_pushdown_server(server_t *tsp)
{
server_t *sp, *csp, **end;
struct in_addr a;
if (! tsp)
return;
a.s_addr = tsp->addr;
csp = (server_t *)0;
for (end = &servers, sp = servers;
sp ;
end = &sp->next, sp = sp->next) {
if (csp == (server_t *)0 &&
sp->addr == tsp->addr &&
sp->next != (server_t *)0) {
csp = sp;
*end = sp->next;
}
}
if (csp) {
*end = csp;
csp->next = (server_t *)0;
nsd_logprintf(NSD_LOG_OPER,
"dns_server %s pushed to bottom of server list\n",
inet_ntoa(a));
}
}
/*
** This routine will return a copy of the domain list. We copy the
** domain list for each request so that the resolv.conf file can change
** while we are in the middle of a request and we will not become
** confused.
*/
domain_t *
dns_domains(void)
{
domain_t *dp, *new, *result, **end;
result = (domain_t *)0;
end = (domain_t **)0;
for (dp = domains; dp; dp = dp->next) {
new = (domain_t *)nsd_malloc(sizeof(domain_t) + dp->len);
if (! new) {
nsd_logprintf(NSD_LOG_RESOURCE,
"dns_domains: failed malloc\n");
return (domain_t *)0;
}
memcpy(new, dp, sizeof(domain_t) + dp->len);
new->next = (domain_t *)0;
if (end) {
*end = new;
} else {
result = new;
}
end = &new->next;
}
return result;
}
/*
** This routine just walks the server list freeing each node.
*/
static void
free_servers(server_t **list)
{
server_t *sp, **hsp;
for (hsp = list; *list; *list = sp) {
sp = (*list)->next;
free(*list);
}
*hsp = (server_t *)0;
}
/*
** This routine walks over the domain list freeing each node.
*/
static void
free_domains(domain_t **list)
{
domain_t *dp,**hdp;
for (hdp=list; *list; *list = dp) {
dp = (*list)->next;
free(*list);
}
*hdp=(domain_t *)0;
}
/*
** This routine walks over the sort list freeing each node.
*/
static void
free_sortlist(sort_t **list)
{
sort_t *sp, **hsp;
for (hsp=list; *list; *list = sp) {
sp = (*list)->next;
free(*list);
}
*hsp=(sort_t *)0;
}
/*
** This routine will free the memory associated with a DNS request
** structure.
*/
void
dns_free_request(dnsrequest_t **rqp)
{
dnsrequest_t *rq;
if (rqp && *rqp) {
rq = *rqp;
if (rq->servers) {
free_servers(&rq->servers);
}
if (rq->domains) {
free_domains(&rq->domains);
}
free(rq);
*rqp = (dnsrequest_t *)0;
}
}
/*
** Given a line like:
** domain foo.bar.com.
** this routine will create a list of domain structures like:
** foo.bar.com bar.com
*/
domain_t *
dns_domain_parse(char *domain)
{
domain_t *dp, *dpn, **end;
char *q, *last;
int len;
/*
** Skip keyword.
*/
if (strncasecmp("domain", domain, sizeof("domain") - 1) == 0) {
domain += sizeof("domain");
}
for (; *domain && isspace(*domain); domain++);
if (! *domain) {
return (domain_t *)0;
}
while (*domain == '.') {
domain++;
}
len = strcspn(domain, " \t");
/*
** Allocate memory for a new domain node.
*/
dp = (domain_t *)nsd_calloc(1, sizeof(domain_t) + len + 1);
if (! dp) {
nsd_logprintf(NSD_LOG_RESOURCE,
"dns_domain_parse: failed malloc\n");
return (domain_t *)0;
}
*dp->name = '.';
memcpy(dp->name+1, domain, len);
dp->len = len + 1;
dp->name[dp->len] = 0;
end = &dp->next;
/*
** Each sub-element in the name is also added as
** a domainname. Do not add any TLD to this list.
*/
for (last = domain + len, domain = strchr(domain, '.');
domain && (domain < last); domain = q) {
q = strchr(domain + 1, '.');
if (!q) {
break;
}
len = last - domain;
dpn = (domain_t *)nsd_calloc(1, sizeof(domain_t) + len + 1);
if (! dpn) {
nsd_logprintf(NSD_LOG_RESOURCE,
"dns_domain_parse: failed malloc\n");
free_domains(&dp);
return (domain_t *)0;
}
memcpy(dpn->name, domain, len);
dpn->len = len;
dpn->name[len] = 0;
*end = dpn;
end = &dpn->next;
}
return dp;
}
/*
** This will simply split a search path creating a list of domain structures.
*/
domain_t *
dns_search_parse(char *search)
{
char *p;
domain_t *list, *dp, **end;
int len;
if (strncasecmp(search, "search", sizeof("search" - 1)) == 0) {
search += sizeof("search");
}
for (list = 0, end = &list; *search; search = p) {
for (; *search && isspace(*search); search++);
for (p = search; *p && ! isspace(*p); p++);
if (*search) {
len = p - search;
dp = (domain_t *)nsd_calloc(1,
sizeof(domain_t) + len + 1);
if (! dp) {
nsd_logprintf(NSD_LOG_RESOURCE,
"dns_search_parse: failed malloc\n");
free_domains(&list);
return (domain_t *)0;
}
*dp->name = '.';
memcpy(dp->name+1, search, len);
dp->len = len + 1;
dp->name[dp->len] = 0;
*end = dp;
end = &dp->next;
}
}
return list;
}
/*
** This will parse a line like:
** nameserver 1.2.3.4 machine foo.sgi.com
** and create a server structure for each element.
*/
server_t *
dns_servers_parse(char *server)
{
server_t *sp, *list, **end;
struct hostent *he, h;
char *p, *q, buf[1024];
int n;
if (strncasecmp(server, "nameserver", sizeof("nameserver") - 1) == 0) {
server += sizeof("nameserver");
}
for (list = 0, end = &list; *server; server = p) {
for (; *server && isspace(*server); server++);
if (! *server) {
break;
}
for (p = server; *p && ! isspace(*p); p++);
sp = (server_t *)nsd_calloc(1, sizeof(server_t));
if (! sp) {
nsd_logprintf(NSD_LOG_RESOURCE,
"dns_servers_parse: failed malloc\n");
free_servers(&list);
return (server_t *)0;
}
n = p - server;
memcpy(buf, server, n);
buf[n] = 0;
n += (8 - (n % 8));
q = buf + n;
he = gethostbyname_r(buf, &h, q, sizeof(buf) - n, &n);
if (! he) {
nsd_logprintf(NSD_LOG_CRIT,
"dns_servers_parse: illegal nameserver "
"address: %s\n", buf);
free_servers(&list);
free(sp);
return (server_t *)0;
}
memcpy(&sp->addr, he->h_addr_list[0], sizeof(sp->addr));
if (sp->addr == 0) {
sp->addr = INADDR_LOOPBACK;
}
*end = sp;
end = &sp->next;
}
return list;
}
/*
** This routine will pasrse a sortlist line
*/
sort_t *
dns_sortlist_parse(char *p)
{
sort_t *tse, **se, *ne;
char *q, buf[64];
int len;
if (! strncasecmp(p, "sortlist", sizeof("sortlist") - 1)) {
p += sizeof("sortlist");
}
/* loop through entries */
for (tse = 0, se = &tse; *p; p = q) {
for (; *p && isspace(*p); p++);
if (! *p) {
break;
}
for (q = p; *q && ! isspace(*q) && *q != '/' && *q != '&'; q++);
len = q - p;
memcpy(buf, p, len);
buf[len] = 0;
ne = (sort_t *)nsd_calloc(1, sizeof(sort_t));
if (! ne) {
nsd_logprintf(NSD_LOG_RESOURCE,
"failed malloc");
return (sort_t*)0;
}
/* parse address */
if (! inet_aton(buf, &ne->addr)) {
nsd_logprintf(NSD_LOG_CRIT,
"illegal address in sortlist: %s\n", buf);
for (; *q && ! isspace(*q); q++);
free(ne);
continue;
}
/* parse netmask, or use default */
if (*q == '/' || *q == '&') {
p = q + 1;
for (; *q && ! isspace(*q); q++);
len = q - p;
memcpy(buf, p, len);
buf[len] = 0;
if (! inet_aton(buf, &ne->netmask)) {
nsd_logprintf(NSD_LOG_CRIT,
"illegal netmask in sortlist: %s\n", buf);
free(ne);
continue;
}
} else {
if (IN_CLASSA(ne->addr.s_addr)) {
ne->netmask.s_addr =
IN_CLASSA_NET;
} else if (IN_CLASSB(ne->addr.s_addr)) {
ne->netmask.s_addr =
IN_CLASSB_NET;
} else if (IN_CLASSC(ne->addr.s_addr)) {
ne->netmask.s_addr =
IN_CLASSC_NET;
} else if (IN_CLASSD(ne->addr.s_addr)) {
ne->netmask.s_addr =
IN_CLASSD_NET;
} else {
nsd_logprintf(NSD_LOG_CRIT,
"bad address in sortlist: %s\n", buf);
free(ne);
continue;
}
}
/* append to end of list */
*se = ne;
se = &ne->next;
}
return (tse);
}
/*
** Check if the dns server is going to be run.
*/
static int
chkconfig_named(void) {
char buf[16];
int fd;
fd = nsd_open("/etc/config/named", O_RDONLY, 0);
if (fd < 0) {
return FALSE;
}
if (read(fd, buf, sizeof(buf)) < 0) {
close(fd);
return FALSE;
}
close(fd);
if (strncasecmp(buf, "on", 2) == 0) {
return TRUE;
} else {
return FALSE;
}
}
/*
** This routine just parses the resolv.conf file into some global variables.
*/
int
dns_config(void)
{
FILE *fp;
char buf[1024], *p, *q;
server_t **ends;
struct stat sbuf;
struct hostent *hp, h;
char hbuf[1024];
int herr;
/*
** Remove any old servers, domains, sortlist, options.
*/
free_servers(&servers);
free_domains(&domains);
free_sortlist(&sortlist);
_dns_ndots = 1;
ends = &servers;
fp = fopen(_PATH_RESCONF, "r");
if (! fp) {
/*
** Defaults.
*/
if (chkconfig_named() == FALSE) {
nsd_logprintf(NSD_LOG_CRIT,
"No /etc/resolv.conf or nameserver found. No default DNS server set.\n");
servers = (server_t *)0;
} else {
p = nsd_attr_fetch_string(0, "hostname", 0);
if (p && (hp = gethostbyname_r(p, &h, hbuf,
sizeof(hbuf), &herr)) &&
(q = strchr(hp->h_name, '.'))) {
domains = dns_domain_parse(q + 1);
nsd_logprintf(NSD_LOG_OPER,
"dns_config: using default domain: %s server: localhost\n", q + 1);
} else if (((getdomainname(buf,sizeof(buf)) == 0) &&
*buf) &&
(q = strchr(hp->h_name, '.'))) {
domains = dns_domain_parse(buf);
nsd_logprintf(NSD_LOG_OPER,
"dns_config: using NIS domain (%s) as default dns domain, server: localhost\n",
buf);
}
servers = dns_servers_parse("0.0.0.0");
}
_dns_restime = 0;
return NSD_OK;
}
fstat(fileno(fp), &sbuf);
_dns_restime = sbuf.st_mtime;
while (fgets(buf, sizeof(buf), fp) != NULL) {
/* forward past leading whitespace */
for (p = buf; *p && isspace(*p); p++);
/* skip comments */
if ((*p == ';') || (*p == '#')) {
continue;
}
/* terminate line on comment */
q = strpbrk(p, ";#\n");
if (q) {
*q = (char)0;
}
if (!strncasecmp(p, "domain", sizeof("domain") - 1)) {
/* read default domain name */
if (domains) {
nsd_logprintf(NSD_LOG_CRIT,
"init (dns): multiple domains or search paths not allowed in resolv.conf\n");
continue;
}
domains = dns_domain_parse(p);
} else if (!strncasecmp(p, "search", sizeof("search") - 1)) {
/* set search list */
if (domains) {
nsd_logprintf(NSD_LOG_CRIT,
"init (dns): multiple domains or search paths not allowed in resolv.conf\n");
continue;
}
domains = dns_search_parse(p);
} else if (!strncasecmp(p, "nameserver",
sizeof("nameserver") - 1)) {
/* append nameserver to list */
*ends = dns_servers_parse(p);
if (*ends) {
ends = &(*ends)->next;
}
} else if (!strncasecmp(p, "sortlist",
sizeof("sortlist") - 1)) {
/* free old list if one exists */
if (sortlist) {
free_sortlist(&sortlist);
}
sortlist = dns_sortlist_parse(p);
} else if (!strncasecmp(p, "options", sizeof("options") - 1)) {
p += sizeof("options");
for (;;) {
for (; *p && isspace(*p); p++);
if (! *p) {
break;
}
if (! strncasecmp(p, "ndots:",
sizeof("ndots:") - 1)) {
p += sizeof("ndots:") - 1;
_dns_ndots = atoi(p);
if (_dns_ndots < 1) {
_dns_ndots = 1;
}
}
for (; *p && ! isspace(*p); p++);
if (! *p) {
break;
}
}
} else if (!strncasecmp(p, "hostresorder",
sizeof("hostresorder") - 1)) {
nsd_logprintf(NSD_LOG_OPER,
"Ignoring hostresorder keyword found in resolv.conf. Resolve order is now controlled by nsswitch.conf.\n");
}
}
fclose(fp);
if (! domains) {
char hostname[MAXHOSTNAMELEN + 1];
hostname[MAXHOSTNAMELEN]='\0';
if ((gethostname(hostname, MAXHOSTNAMELEN) != -1) &&
((p = strchr(hostname, '.')) != NULL)) {
domains = dns_domain_parse(++p);
nsd_logprintf(NSD_LOG_OPER,
"using DNS domain %s determined from hostname\n", p);
}
}
if (! domains) {
nsd_logprintf(NSD_LOG_OPER,
"init (dns): No domainname could be determined.\n");
nsd_logprintf(NSD_LOG_OPER,
"Only fully qualified lookups will be resolved\n");
}
if (! servers) {
/*
** Add a "default" server address.
**
** We want the address to be 127.0.0.1 and next to be NULL.
*/
servers = (server_t *)nsd_calloc(1, sizeof(server_t));
if (! servers) {
nsd_logprintf(NSD_LOG_RESOURCE,
"init (dns): failed malloc\n");
return NSD_ERROR;
}
servers->addr = INADDR_LOOPBACK;
}
return NSD_OK;
}
/*
** This routine will read the /etc/resolv.conf file and setup a list
** of "domains", and a list of "servers" for doing lookups.
*/
int
init(char *map)
{
/*
** Open up a socket for use in making dns lookups for all requests.
*/
if (! map && _dns_sock < 0) {
if ((_dns_sock = socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP)) < 0) {
nsd_logprintf(NSD_LOG_RESOURCE,
"init (dns): failed to create socket: %s\n",
strerror(errno));
return NSD_ERROR;
}
nsd_callback_new(_dns_sock, dns_query_callback, NSD_READ);
}
/*
** Parse resolv.conf.
*/
return dns_config();
}
/*
** The dump function just prints out our current state. Essentially, this
** is just what we parsed out of resolv.conf.
*/
int
dump(FILE *fp)
{
domain_t *dp;
server_t *sp;
sort_t *ap;
struct in_addr addr;
if (_dns_restime) {
fprintf(fp, "resolv.conf timestamp: %s", ctime(&_dns_restime));
} else {
fprintf(fp, "missing or unparsable resolv.conf\n");
}
/*
** Print out flags.
*/
fprintf(fp, "options:\n");
fprintf(fp, "\tndots: %d\n", _dns_ndots);
/*
** Print out server list.
*/
fprintf(fp, "servers:\n");
for (sp = servers; sp; sp = sp->next) {
addr.s_addr = sp->addr;
fprintf(fp, "\taddress: %s\n", inet_ntoa(addr));
}
/*
** Print out search path.
*/
fprintf(fp, "domain search patch:\n");
for (dp = domains; dp; dp = dp->next) {
fprintf(fp, "\tdomain: %s\n", dp->name);
}
/*
** Print out sort list.
*/
if (sortlist) {
fprintf(fp, "sort list:\n");
for (ap = sortlist; ap; ap = ap->next) {
fprintf(fp, "\t%s/", inet_ntoa(ap->addr));
fprintf(fp, "%s\n", inet_ntoa(ap->netmask));
}
}
return NSD_OK;
}