mirror of
https://github.com/zeek/zeek.git
synced 2025-10-02 06:38:20 +00:00
794 lines
18 KiB
C
794 lines
18 KiB
C
/*
|
|
* See the file "COPYING" in the main distribution directory for copyright.
|
|
*/
|
|
/*
|
|
* nb_dns - non-blocking dns routines
|
|
*
|
|
* This version works with BIND 9
|
|
*
|
|
* Note: The code here is way more complicated than it should be but
|
|
* although the interface to send requests is public, the routine to
|
|
* crack reply buffers is private.
|
|
*/
|
|
|
|
#include "zeek-config.h" /* must appear before first ifdef */
|
|
#include "zeek/nb_dns.h"
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/socket.h>
|
|
|
|
#include <netinet/in.h>
|
|
|
|
#include <arpa/inet.h>
|
|
#include <arpa/nameser.h>
|
|
#ifdef NEED_NAMESER_COMPAT_H
|
|
#include <arpa/nameser_compat.h>
|
|
#endif
|
|
|
|
#include <errno.h>
|
|
#ifdef HAVE_MEMORY_H
|
|
#include <memory.h>
|
|
#endif
|
|
#include <netdb.h>
|
|
#include <resolv.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
|
|
#ifdef notdef
|
|
#include "gnuc.h"
|
|
#ifdef HAVE_OS_PROTO_H
|
|
#include "os-proto.h"
|
|
#endif
|
|
#endif
|
|
|
|
#if PACKETSZ > 1024
|
|
#define MAXPACKET PACKETSZ
|
|
#else
|
|
#define MAXPACKET 1024
|
|
#endif
|
|
|
|
#ifdef DO_SOCK_DECL
|
|
extern int socket(int, int, int);
|
|
extern int connect(int, const struct sockaddr *, int);
|
|
extern int send(int, const void *, int, int);
|
|
extern int recvfrom(int, void *, int, int, struct sockaddr *, int *);
|
|
#endif
|
|
|
|
/* Private data */
|
|
struct nb_dns_entry {
|
|
struct nb_dns_entry *next;
|
|
char name[NS_MAXDNAME + 1];
|
|
u_short id;
|
|
int qtype; /* query type */
|
|
int atype; /* address family */
|
|
int asize; /* address size */
|
|
void *cookie;
|
|
};
|
|
|
|
#ifndef MAXALIASES
|
|
#define MAXALIASES 35
|
|
#endif
|
|
#ifndef MAXADDRS
|
|
#define MAXADDRS 35
|
|
#endif
|
|
|
|
struct nb_dns_hostent {
|
|
struct hostent hostent;
|
|
int numaliases;
|
|
int numaddrs;
|
|
char *host_aliases[MAXALIASES + 1];
|
|
char *h_addr_ptrs[MAXADDRS + 1];
|
|
char hostbuf[8 * 1024];
|
|
};
|
|
|
|
struct nb_dns_info {
|
|
int s; /* Resolver file descriptor */
|
|
struct sockaddr_storage server; /* server address to bind to */
|
|
struct nb_dns_entry *list; /* outstanding requests */
|
|
struct nb_dns_hostent dns_hostent;
|
|
};
|
|
|
|
/* Forwards */
|
|
static int _nb_dns_mkquery(struct nb_dns_info *, const char *, int, int,
|
|
void *, char *);
|
|
static int _nb_dns_cmpsockaddr(struct sockaddr *, struct sockaddr *, char *);
|
|
|
|
static char *
|
|
my_strerror(int errnum)
|
|
{
|
|
#ifdef HAVE_STRERROR
|
|
extern char *strerror(int);
|
|
return strerror(errnum);
|
|
#else
|
|
static char errnum_buf[32];
|
|
snprintf(errnum_buf, sizeof(errnum_buf), "errno %d", errnum);
|
|
return errnum_buf;
|
|
#endif
|
|
}
|
|
|
|
static const char* sa_ntop(struct sockaddr* sa, char* buf, int len)
|
|
{
|
|
if ( sa->sa_family == AF_INET )
|
|
return inet_ntop(sa->sa_family,
|
|
&(((struct sockaddr_in*)sa)->sin_addr),
|
|
buf, len);
|
|
else
|
|
return inet_ntop(sa->sa_family,
|
|
&(((struct sockaddr_in6*)sa)->sin6_addr),
|
|
buf, len);
|
|
}
|
|
|
|
struct nb_dns_info *
|
|
nb_dns_init(char *errstr)
|
|
{
|
|
register struct nb_dns_info *nd;
|
|
|
|
nd = (struct nb_dns_info *)malloc(sizeof(*nd));
|
|
if (nd == NULL) {
|
|
snprintf(errstr, NB_DNS_ERRSIZE, "nb_dns_init: malloc(): %s",
|
|
my_strerror(errno));
|
|
return (NULL);
|
|
}
|
|
memset(nd, 0, sizeof(*nd));
|
|
nd->s = -1;
|
|
|
|
/* XXX should be able to init static hostent struct some other way */
|
|
(void)gethostbyname("localhost");
|
|
|
|
if ((_res.options & RES_INIT) == 0 && res_init() == -1) {
|
|
snprintf(errstr, NB_DNS_ERRSIZE, "res_init() failed");
|
|
free(nd);
|
|
return (NULL);
|
|
}
|
|
|
|
if ( _res.nscount == 0 )
|
|
{
|
|
// Really? Let's try parsing resolv.conf ourselves to see what's
|
|
// there. (e.g. musl libc has res_init() that doesn't actually
|
|
// parse the config file).
|
|
const char* config_file_path = "/etc/resolv.conf";
|
|
|
|
#ifdef _PATH_RESCONF
|
|
config_file_path = _PATH_RESCONF;
|
|
#endif
|
|
|
|
FILE* config_file = fopen(config_file_path, "r");
|
|
|
|
if ( config_file )
|
|
{
|
|
char line[128];
|
|
char* ns;
|
|
|
|
while ( fgets(line, sizeof(line), config_file) )
|
|
{
|
|
ns = strtok(line, " \t\n");
|
|
|
|
if ( ! ns || strcmp(ns, "nameserver") )
|
|
continue;
|
|
|
|
ns = strtok(0, " \t\n");
|
|
|
|
if ( ! ns )
|
|
continue;
|
|
|
|
/* XXX support IPv6 */
|
|
struct sockaddr_in a;
|
|
memset(&a, 0, sizeof(a));
|
|
a.sin_family = AF_INET;
|
|
a.sin_port = htons(53);
|
|
|
|
if ( inet_pton(AF_INET, ns, &a.sin_addr) == 1 )
|
|
{
|
|
memcpy(&nd->server, &a, sizeof(a));
|
|
nd->s = socket(nd->server.ss_family, SOCK_DGRAM, 0);
|
|
|
|
if ( nd->s < 0 )
|
|
{
|
|
snprintf(errstr, NB_DNS_ERRSIZE, "socket(): %s",
|
|
my_strerror(errno));
|
|
fclose(config_file);
|
|
free(nd);
|
|
return (NULL);
|
|
}
|
|
|
|
if ( connect(nd->s, (struct sockaddr *)&nd->server,
|
|
nd->server.ss_family == AF_INET ?
|
|
sizeof(struct sockaddr_in) :
|
|
sizeof(struct sockaddr_in6)) < 0 )
|
|
{
|
|
char s[INET6_ADDRSTRLEN];
|
|
sa_ntop((struct sockaddr*)&nd->server, s, INET6_ADDRSTRLEN);
|
|
snprintf(errstr, NB_DNS_ERRSIZE, "connect(%s): %s", s,
|
|
my_strerror(errno));
|
|
fclose(config_file);
|
|
close(nd->s);
|
|
free(nd);
|
|
return (NULL);
|
|
}
|
|
|
|
fclose(config_file);
|
|
return (nd);
|
|
}
|
|
}
|
|
|
|
fclose(config_file);
|
|
snprintf(errstr, NB_DNS_ERRSIZE, "no valid nameserver found in %s",
|
|
config_file_path);
|
|
free(nd);
|
|
return (NULL);
|
|
}
|
|
|
|
snprintf(errstr, NB_DNS_ERRSIZE, "resolver config file not located");
|
|
free(nd);
|
|
return (NULL);
|
|
}
|
|
|
|
int i;
|
|
|
|
for ( i = 0; i < _res.nscount; ++i )
|
|
{
|
|
memcpy(&nd->server, &_res.nsaddr_list[i], sizeof(struct sockaddr_in));
|
|
/* XXX support IPv6 */
|
|
if ( nd->server.ss_family != AF_INET )
|
|
continue;
|
|
|
|
nd->s = socket(nd->server.ss_family, SOCK_DGRAM, 0);
|
|
|
|
if ( nd->s < 0 )
|
|
{
|
|
snprintf(errstr, NB_DNS_ERRSIZE, "socket(): %s",
|
|
my_strerror(errno));
|
|
free(nd);
|
|
return (NULL);
|
|
}
|
|
|
|
if ( connect(nd->s, (struct sockaddr *)&nd->server,
|
|
nd->server.ss_family == AF_INET ?
|
|
sizeof(struct sockaddr_in) :
|
|
sizeof(struct sockaddr_in6)) < 0 )
|
|
{
|
|
char s[INET6_ADDRSTRLEN];
|
|
sa_ntop((struct sockaddr*)&nd->server, s, INET6_ADDRSTRLEN);
|
|
snprintf(errstr, NB_DNS_ERRSIZE, "connect(%s): %s", s,
|
|
my_strerror(errno));
|
|
close(nd->s);
|
|
free(nd);
|
|
return (NULL);
|
|
}
|
|
|
|
return (nd);
|
|
}
|
|
|
|
snprintf(errstr, NB_DNS_ERRSIZE, "no valid nameservers in resolver config");
|
|
free(nd);
|
|
return (NULL);
|
|
}
|
|
|
|
struct nb_dns_info *
|
|
nb_dns_init2(char *errstr, struct sockaddr* sa)
|
|
{
|
|
register struct nb_dns_info *nd;
|
|
|
|
nd = (struct nb_dns_info *)malloc(sizeof(*nd));
|
|
if (nd == NULL) {
|
|
snprintf(errstr, NB_DNS_ERRSIZE, "nb_dns_init: malloc(): %s",
|
|
my_strerror(errno));
|
|
return (NULL);
|
|
}
|
|
memset(nd, 0, sizeof(*nd));
|
|
nd->s = -1;
|
|
|
|
if ( sa->sa_family == AF_INET )
|
|
{
|
|
memcpy(&nd->server, sa, sizeof(struct sockaddr_in));
|
|
((struct sockaddr_in*)&nd->server)->sin_port = htons(53);
|
|
}
|
|
else
|
|
{
|
|
memcpy(&nd->server, sa, sizeof(struct sockaddr_in6));
|
|
((struct sockaddr_in6*)&nd->server)->sin6_port = htons(53);
|
|
}
|
|
|
|
nd->s = socket(nd->server.ss_family, SOCK_DGRAM, 0);
|
|
|
|
if ( nd->s < 0 )
|
|
{
|
|
snprintf(errstr, NB_DNS_ERRSIZE, "socket(): %s",
|
|
my_strerror(errno));
|
|
free(nd);
|
|
return (NULL);
|
|
}
|
|
|
|
if ( connect(nd->s, (struct sockaddr *)&nd->server,
|
|
nd->server.ss_family == AF_INET ?
|
|
sizeof(struct sockaddr_in) :
|
|
sizeof(struct sockaddr_in6)) < 0 )
|
|
{
|
|
char s[INET6_ADDRSTRLEN];
|
|
sa_ntop((struct sockaddr*)&nd->server, s, INET6_ADDRSTRLEN);
|
|
snprintf(errstr, NB_DNS_ERRSIZE, "connect(%s): %s", s,
|
|
my_strerror(errno));
|
|
close(nd->s);
|
|
free(nd);
|
|
return (NULL);
|
|
}
|
|
|
|
return (nd);
|
|
}
|
|
|
|
void
|
|
nb_dns_finish(struct nb_dns_info *nd)
|
|
{
|
|
register struct nb_dns_entry *ne, *ne2;
|
|
|
|
ne = nd->list;
|
|
while (ne != NULL) {
|
|
ne2 = ne;
|
|
ne = ne->next;
|
|
free(ne2);
|
|
}
|
|
close(nd->s);
|
|
free(nd);
|
|
}
|
|
|
|
int
|
|
nb_dns_fd(struct nb_dns_info *nd)
|
|
{
|
|
|
|
return (nd->s);
|
|
}
|
|
|
|
static int
|
|
_nb_dns_cmpsockaddr(register struct sockaddr *sa1,
|
|
register struct sockaddr *sa2, register char *errstr)
|
|
{
|
|
register struct sockaddr_in *sin1, *sin2;
|
|
#ifdef AF_INET6
|
|
register struct sockaddr_in6 *sin6a, *sin6b;
|
|
#endif
|
|
static const char serr[] = "answer from wrong nameserver (%d)";
|
|
|
|
if (sa1->sa_family != sa2->sa_family) {
|
|
snprintf(errstr, NB_DNS_ERRSIZE, serr, 1);
|
|
return (-1);
|
|
}
|
|
switch (sa1->sa_family) {
|
|
|
|
case AF_INET:
|
|
sin1 = (struct sockaddr_in *)sa1;
|
|
sin2 = (struct sockaddr_in *)sa2;
|
|
if (sin1->sin_port != sin2->sin_port) {
|
|
snprintf(errstr, NB_DNS_ERRSIZE, serr, 2);
|
|
return (-1);
|
|
}
|
|
if (sin1->sin_addr.s_addr != sin2->sin_addr.s_addr) {
|
|
snprintf(errstr, NB_DNS_ERRSIZE, serr, 3);
|
|
return (-1);
|
|
}
|
|
break;
|
|
|
|
#ifdef AF_INET6
|
|
case AF_INET6:
|
|
sin6a = (struct sockaddr_in6 *)sa1;
|
|
sin6b = (struct sockaddr_in6 *)sa2;
|
|
if (sin6a->sin6_port != sin6b->sin6_port) {
|
|
snprintf(errstr, NB_DNS_ERRSIZE, serr, 62);
|
|
return (-1);
|
|
}
|
|
if (memcmp(&sin6a->sin6_addr, &sin6b->sin6_addr,
|
|
sizeof(sin6a->sin6_addr)) != 0) {
|
|
snprintf(errstr, NB_DNS_ERRSIZE, serr, 63);
|
|
return (-1);
|
|
}
|
|
break;
|
|
#endif
|
|
|
|
default:
|
|
snprintf(errstr, NB_DNS_ERRSIZE, serr, 4);
|
|
return (-1);
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
_nb_dns_mkquery(register struct nb_dns_info *nd, register const char *name,
|
|
register int atype, register int qtype, register void * cookie,
|
|
register char *errstr)
|
|
{
|
|
register struct nb_dns_entry *ne;
|
|
register HEADER *hp;
|
|
register int n;
|
|
u_long msg[MAXPACKET / sizeof(u_long)];
|
|
|
|
/* Allocate an entry */
|
|
ne = (struct nb_dns_entry *)malloc(sizeof(*ne));
|
|
if (ne == NULL) {
|
|
snprintf(errstr, NB_DNS_ERRSIZE, "malloc(): %s",
|
|
my_strerror(errno));
|
|
return (-1);
|
|
}
|
|
memset(ne, 0, sizeof(*ne));
|
|
strncpy(ne->name, name, sizeof(ne->name) - 1);
|
|
ne->name[sizeof(ne->name) - 1] = '\0';
|
|
ne->qtype = qtype;
|
|
ne->atype = atype;
|
|
switch (atype) {
|
|
|
|
case AF_INET:
|
|
ne->asize = NS_INADDRSZ;
|
|
break;
|
|
|
|
#ifdef AF_INET6
|
|
case AF_INET6:
|
|
ne->asize = NS_IN6ADDRSZ;
|
|
break;
|
|
#endif
|
|
|
|
default:
|
|
snprintf(errstr, NB_DNS_ERRSIZE,
|
|
"_nb_dns_mkquery: bad family %d", atype);
|
|
free(ne);
|
|
return (-1);
|
|
}
|
|
|
|
/* Build the request */
|
|
n = res_mkquery(
|
|
ns_o_query, /* op code (query) */
|
|
name, /* domain name */
|
|
ns_c_in, /* query class (internet) */
|
|
qtype, /* query type */
|
|
NULL, /* data */
|
|
0, /* length of data */
|
|
NULL, /* new rr */
|
|
(u_char *)msg, /* buffer */
|
|
sizeof(msg)); /* size of buffer */
|
|
if (n < 0) {
|
|
snprintf(errstr, NB_DNS_ERRSIZE, "res_mkquery() failed");
|
|
free(ne);
|
|
return (-1);
|
|
}
|
|
|
|
hp = (HEADER *)msg;
|
|
ne->id = htons(hp->id);
|
|
|
|
if (send(nd->s, (char *)msg, n, 0) != n) {
|
|
snprintf(errstr, NB_DNS_ERRSIZE, "send(): %s",
|
|
my_strerror(errno));
|
|
free(ne);
|
|
return (-1);
|
|
}
|
|
|
|
ne->next = nd->list;
|
|
ne->cookie = cookie;
|
|
nd->list = ne;
|
|
|
|
return(0);
|
|
}
|
|
|
|
int
|
|
nb_dns_host_request(register struct nb_dns_info *nd, register const char *name,
|
|
register void *cookie, register char *errstr)
|
|
{
|
|
|
|
return (nb_dns_host_request2(nd, name, AF_INET, 0, cookie, errstr));
|
|
}
|
|
|
|
int
|
|
nb_dns_host_request2(register struct nb_dns_info *nd, register const char *name,
|
|
register int af, register int qtype, register void *cookie, register char *errstr)
|
|
{
|
|
if (qtype != 16) {
|
|
|
|
switch (af) {
|
|
|
|
case AF_INET:
|
|
qtype = T_A;
|
|
break;
|
|
|
|
#ifdef AF_INET6
|
|
case AF_INET6:
|
|
qtype = T_AAAA;
|
|
break;
|
|
#endif
|
|
|
|
default:
|
|
snprintf(errstr, NB_DNS_ERRSIZE,
|
|
"nb_dns_host_request2(): unknown address family %d", af);
|
|
return (-1);
|
|
}
|
|
}
|
|
return (_nb_dns_mkquery(nd, name, af, qtype, cookie, errstr));
|
|
}
|
|
|
|
int
|
|
nb_dns_addr_request(register struct nb_dns_info *nd, nb_uint32_t addr,
|
|
register void *cookie, register char *errstr)
|
|
{
|
|
|
|
return (nb_dns_addr_request2(nd, (char *)&addr, AF_INET,
|
|
cookie, errstr));
|
|
}
|
|
|
|
int
|
|
nb_dns_addr_request2(register struct nb_dns_info *nd, char *addrp,
|
|
register int af, register void *cookie, register char *errstr)
|
|
{
|
|
#ifdef AF_INET6
|
|
register char *cp;
|
|
register int n, i;
|
|
register size_t size;
|
|
#endif
|
|
register u_char *uaddr;
|
|
char name[NS_MAXDNAME + 1];
|
|
|
|
switch (af) {
|
|
|
|
case AF_INET:
|
|
uaddr = (u_char *)addrp;
|
|
snprintf(name, sizeof(name), "%u.%u.%u.%u.in-addr.arpa",
|
|
(uaddr[3] & 0xff),
|
|
(uaddr[2] & 0xff),
|
|
(uaddr[1] & 0xff),
|
|
(uaddr[0] & 0xff));
|
|
break;
|
|
|
|
#ifdef AF_INET6
|
|
case AF_INET6:
|
|
uaddr = (u_char *)addrp;
|
|
cp = name;
|
|
size = sizeof(name);
|
|
for (n = NS_IN6ADDRSZ - 1; n >= 0; --n) {
|
|
snprintf(cp, size, "%x.%x.",
|
|
(uaddr[n] & 0xf),
|
|
(uaddr[n] >> 4) & 0xf);
|
|
i = strlen(cp);
|
|
size -= i;
|
|
cp += i;
|
|
}
|
|
snprintf(cp, size, "ip6.arpa");
|
|
break;
|
|
#endif
|
|
|
|
default:
|
|
snprintf(errstr, NB_DNS_ERRSIZE,
|
|
"nb_dns_addr_request2(): unknown address family %d", af);
|
|
return (-1);
|
|
}
|
|
|
|
return (_nb_dns_mkquery(nd, name, af, T_PTR, cookie, errstr));
|
|
}
|
|
|
|
int
|
|
nb_dns_abort_request(struct nb_dns_info *nd, void *cookie)
|
|
{
|
|
register struct nb_dns_entry *ne, *lastne;
|
|
|
|
/* Try to find this request on the outstanding request list */
|
|
lastne = NULL;
|
|
for (ne = nd->list; ne != NULL; ne = ne->next) {
|
|
if (ne->cookie == cookie)
|
|
break;
|
|
lastne = ne;
|
|
}
|
|
|
|
/* Not a currently pending request */
|
|
if (ne == NULL)
|
|
return (-1);
|
|
|
|
/* Unlink this entry */
|
|
if (lastne == NULL)
|
|
nd->list = ne->next;
|
|
else
|
|
lastne->next = ne->next;
|
|
ne->next = NULL;
|
|
|
|
return (0);
|
|
}
|
|
|
|
/* Returns 1 with an answer, 0 when reply was old, -1 on fatal errors */
|
|
int
|
|
nb_dns_activity(struct nb_dns_info *nd, struct nb_dns_result *nr, char *errstr)
|
|
{
|
|
register int msglen, qtype, atype, n, i;
|
|
register struct nb_dns_entry *ne, *lastne;
|
|
socklen_t fromlen;
|
|
struct sockaddr_storage from;
|
|
u_long msg[MAXPACKET / sizeof(u_long)];
|
|
register char *bp, *ep;
|
|
register char **ap, **hap;
|
|
register u_int16_t id;
|
|
register const u_char *rdata;
|
|
register u_int32_t rttl = 0; // make compiler happy.
|
|
register struct hostent *he;
|
|
register size_t rdlen;
|
|
ns_msg handle;
|
|
ns_rr rr;
|
|
|
|
/* This comes from the second half of do_query() */
|
|
fromlen = sizeof(from);
|
|
msglen = recvfrom(nd->s, (char *)msg, sizeof(msg), 0,
|
|
(struct sockaddr*)&from, &fromlen);
|
|
if (msglen <= 0) {
|
|
snprintf(errstr, NB_DNS_ERRSIZE, "recvfrom(): %s",
|
|
my_strerror(errno));
|
|
return (-1);
|
|
}
|
|
if (msglen < HFIXEDSZ) {
|
|
snprintf(errstr, NB_DNS_ERRSIZE, "recvfrom(): undersized: %d",
|
|
msglen);
|
|
return (-1);
|
|
}
|
|
if (ns_initparse((u_char *)msg, msglen, &handle) < 0) {
|
|
snprintf(errstr, NB_DNS_ERRSIZE, "ns_initparse(): %s",
|
|
my_strerror(errno));
|
|
nr->host_errno = NO_RECOVERY;
|
|
return (-1);
|
|
}
|
|
|
|
/* RES_INSECURE1 style check */
|
|
if (_nb_dns_cmpsockaddr((struct sockaddr*)&nd->server,
|
|
(struct sockaddr*)&from, errstr) < 0) {
|
|
nr->host_errno = NO_RECOVERY;
|
|
return (-1);
|
|
}
|
|
|
|
/* Search for this request */
|
|
lastne = NULL;
|
|
id = ns_msg_id(handle);
|
|
for (ne = nd->list; ne != NULL; ne = ne->next) {
|
|
if (ne->id == id)
|
|
break;
|
|
lastne = ne;
|
|
}
|
|
|
|
/* Not an answer to a question we care about anymore */
|
|
if (ne == NULL)
|
|
return (0);
|
|
|
|
/* Unlink this entry */
|
|
if (lastne == NULL)
|
|
nd->list = ne->next;
|
|
else
|
|
lastne->next = ne->next;
|
|
ne->next = NULL;
|
|
|
|
/* RES_INSECURE2 style check */
|
|
/* XXX not implemented */
|
|
|
|
/* Initialize result struct */
|
|
memset(nr, 0, sizeof(*nr));
|
|
nr->cookie = ne->cookie;
|
|
qtype = ne->qtype;
|
|
|
|
/* Deal with various errors */
|
|
switch (ns_msg_getflag(handle, ns_f_rcode)) {
|
|
|
|
case ns_r_nxdomain:
|
|
nr->host_errno = HOST_NOT_FOUND;
|
|
free(ne);
|
|
return (1);
|
|
|
|
case ns_r_servfail:
|
|
nr->host_errno = TRY_AGAIN;
|
|
free(ne);
|
|
return (1);
|
|
|
|
case ns_r_noerror:
|
|
break;
|
|
|
|
case ns_r_formerr:
|
|
case ns_r_notimpl:
|
|
case ns_r_refused:
|
|
default:
|
|
nr->host_errno = NO_RECOVERY;
|
|
free(ne);
|
|
return (1);
|
|
}
|
|
|
|
/* Loop through records in packet */
|
|
memset(&rr, 0, sizeof(rr));
|
|
memset(&nd->dns_hostent, 0, sizeof(nd->dns_hostent));
|
|
he = &nd->dns_hostent.hostent;
|
|
/* XXX no support for aliases */
|
|
he->h_aliases = nd->dns_hostent.host_aliases;
|
|
he->h_addr_list = nd->dns_hostent.h_addr_ptrs;
|
|
he->h_addrtype = ne->atype;
|
|
he->h_length = ne->asize;
|
|
free(ne);
|
|
|
|
bp = nd->dns_hostent.hostbuf;
|
|
ep = bp + sizeof(nd->dns_hostent.hostbuf);
|
|
hap = he->h_addr_list;
|
|
ap = he->h_aliases;
|
|
|
|
for (i = 0; i < ns_msg_count(handle, ns_s_an); i++) {
|
|
/* Parse next record */
|
|
if (ns_parserr(&handle, ns_s_an, i, &rr) < 0) {
|
|
if (errno != ENODEV) {
|
|
nr->host_errno = NO_RECOVERY;
|
|
return (1);
|
|
}
|
|
/* All done */
|
|
break;
|
|
}
|
|
|
|
/* Ignore records that don't answer our query (e.g. CNAMEs) */
|
|
atype = ns_rr_type(rr);
|
|
if (atype != qtype)
|
|
continue;
|
|
|
|
rdata = ns_rr_rdata(rr);
|
|
rdlen = ns_rr_rdlen(rr);
|
|
rttl = ns_rr_ttl(rr);
|
|
switch (atype) {
|
|
|
|
case T_A:
|
|
case T_AAAA:
|
|
if (rdlen != (unsigned int) he->h_length) {
|
|
snprintf(errstr, NB_DNS_ERRSIZE,
|
|
"nb_dns_activity(): bad rdlen %d",
|
|
(int) rdlen);
|
|
nr->host_errno = NO_RECOVERY;
|
|
return (-1);
|
|
}
|
|
|
|
if (bp + rdlen >= ep) {
|
|
snprintf(errstr, NB_DNS_ERRSIZE,
|
|
"nb_dns_activity(): overflow 1");
|
|
nr->host_errno = NO_RECOVERY;
|
|
return (-1);
|
|
}
|
|
if (nd->dns_hostent.numaddrs + 1 >= MAXADDRS) {
|
|
snprintf(errstr, NB_DNS_ERRSIZE,
|
|
"nb_dns_activity(): overflow 2");
|
|
nr->host_errno = NO_RECOVERY;
|
|
return (-1);
|
|
}
|
|
|
|
memcpy(bp, rdata, rdlen);
|
|
*hap++ = bp;
|
|
bp += rdlen;
|
|
++nd->dns_hostent.numaddrs;
|
|
|
|
/* Keep looking for more A records */
|
|
break;
|
|
|
|
case T_TXT:
|
|
if (bp + rdlen >= ep) {
|
|
snprintf(errstr, NB_DNS_ERRSIZE,
|
|
"nb_dns_activity(): overflow 1 for txt");
|
|
nr->host_errno = NO_RECOVERY;
|
|
return (-1);
|
|
}
|
|
|
|
memcpy(bp, rdata, rdlen);
|
|
he->h_name = bp+1; /* First char is a control character. */
|
|
nr->hostent = he;
|
|
nr->ttl = rttl;
|
|
return (1);
|
|
|
|
case T_PTR:
|
|
n = dn_expand((const u_char *)msg,
|
|
(const u_char *)msg + msglen, rdata, bp, ep - bp);
|
|
if (n < 0) {
|
|
/* XXX return -1 here ??? */
|
|
nr->host_errno = NO_RECOVERY;
|
|
return (1);
|
|
}
|
|
he->h_name = bp;
|
|
/* XXX check for overflow */
|
|
bp += n; /* returned len includes EOS */
|
|
|
|
/* "Find first satisfactory answer" */
|
|
nr->hostent = he;
|
|
nr->ttl = rttl;
|
|
return (1);
|
|
}
|
|
}
|
|
|
|
nr->hostent = he;
|
|
nr->ttl = rttl;
|
|
return (1);
|
|
}
|