home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
ftp.ee.lbl.gov
/
2014.05.ftp.ee.lbl.gov.tar
/
ftp.ee.lbl.gov
/
hf-1.2.tar.gz
/
hf-1.2.tar
/
hf-1.2
/
nb_dns.c
< prev
next >
Wrap
C/C++ Source or Header
|
2010-02-10
|
14KB
|
636 lines
/*
* Copyright (c) 2000, 2001, 2002, 2009, 2010
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that: (1) source code distributions
* retain the above copyright notice and this paragraph in its entirety, (2)
* distributions including binary code include the above copyright notice and
* this paragraph in its entirety in the documentation or other materials
* provided with the distribution, and (3) all advertising materials mentioning
* features or use of this software display the following acknowledgement:
* ``This product includes software developed by the University of California,
* Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
* the University nor the names of its contributors may be used to endorse
* or promote products derived from this software without specific prior
* written permission.
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*/
#ifndef lint
static const char rcsid[] =
"@(#) $Id: nb_dns.c 181 2010-02-10 09:46:47Z leres $ (LBL)";
#endif
/*
* 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.
*/
#ifdef notdef
#include "config.h" /* must appear before first ifdef */
#endif
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <arpa/nameser.h>
#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>
#include "gnuc.h"
#ifdef HAVE_OS_PROTO_H
#include "os-proto.h"
#endif
#include "nb_dns.h"
#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];
int qtype; /* query type */
int atype; /* address family */
int asize; /* address size */
u_short id;
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_in 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)
{
#if 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
}
struct nb_dns_info *
nb_dns_init(char *errstr)
{
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);
}
nd->s = socket(PF_INET, SOCK_DGRAM, 0);
if (nd->s < 0) {
snprintf(errstr, NB_DNS_ERRSIZE, "socket(): %s",
my_strerror(errno));
free(nd);
return (NULL);
}
/* XXX should use resolver config */
nd->server = _res.nsaddr_list[0];
if (connect(nd->s, (struct sockaddr *)&nd->server,
sizeof(struct sockaddr)) < 0) {
snprintf(errstr, NB_DNS_ERRSIZE, "connect(%s): %s",
inet_ntoa(nd->server.sin_addr), my_strerror(errno));
close(nd->s);
free(nd);
return (NULL);
}
return (nd);
}
void
nb_dns_finish(struct nb_dns_info *nd)
{
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(struct sockaddr *sa1, struct sockaddr *sa2, char *errstr)
{
struct sockaddr_in *sin1, *sin2;
#ifdef AF_INET6
struct sockaddr_in6 *sin6a, *sin6b;
#endif
const static char serr[] = "answer from wrong nameserver (%d)";
if (sa1->sa_family != sa1->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, 2);
return (-1);
}
if (memcmp(&sin6a->sin6_addr, &sin6b->sin6_addr,
sizeof(sin6a->sin6_addr)) != 0) {
snprintf(errstr, NB_DNS_ERRSIZE, serr, 3);
return (-1);
}
break;
#endif
default:
snprintf(errstr, NB_DNS_ERRSIZE, serr, 4);
return (-1);
}
return (0);
}
static int
_nb_dns_mkquery(struct nb_dns_info *nd, const char *name, int atype,
int qtype, void * cookie, char *errstr)
{
HEADER *hp;
int n;
u_char *msg;
struct nb_dns_entry *ne;
/* Allocate an entry */
ne = (struct nb_dns_entry *)calloc(1, sizeof(*ne));
if (ne == NULL) {
snprintf(errstr, NB_DNS_ERRSIZE, "calloc(): %s",
my_strerror(errno));
return (-1);
}
strncpy(ne->name, name, sizeof(ne->name));
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);
return (-1);
}
/* Allocate msg buffer */
msg = calloc(1, MAXPACKET);
if (msg == NULL) {
snprintf(errstr, NB_DNS_ERRSIZE, "calloc() msg: %s",
my_strerror(errno));
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 */
MAXPACKET); /* size of buffer */
if (n < 0) {
snprintf(errstr, NB_DNS_ERRSIZE, "res_mkquery() failed (%d)",
MAXPACKET);
free(ne);
free(msg);
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);
free(msg);
return (-1);
}
ne->next = nd->list;
ne->cookie = cookie;
nd->list = ne;
free(msg);
return(0);
}
int
nb_dns_host_request(struct nb_dns_info *nd, const char *name,
void *cookie, char *errstr)
{
return (nb_dns_host_request2(nd, name, AF_INET, cookie, errstr));
}
int
nb_dns_host_request2(struct nb_dns_info *nd, const char *name,
int af, void *cookie, char *errstr)
{
int qtype;
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(): uknown address family %d", af);
return (-1);
}
return (_nb_dns_mkquery(nd, name, af, qtype, cookie, errstr));
}
int
nb_dns_addr_request(struct nb_dns_info *nd, nb_uint32_t addr,
void *cookie, char *errstr)
{
return (nb_dns_addr_request2(nd, (char *)&addr, AF_INET,
cookie, errstr));
}
int
nb_dns_addr_request2(struct nb_dns_info *nd, char *addrp,
int af, void *cookie, char *errstr)
{
#ifdef AF_INET6
char *cp;
int n, i;
size_t size;
#endif
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.int");
break;
#endif
default:
snprintf(errstr, NB_DNS_ERRSIZE,
"nb_dns_addr_request2(): uknown 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)
{
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)
{
int msglen, qtype, atype, n, i;
struct nb_dns_entry *ne, *lastne;
socklen_t fromlen;
struct sockaddr from;
u_long msg[MAXPACKET / sizeof(u_long)];
char *bp, *ep;
char **ap, **hap;
u_int16_t id;
const u_char *rdata;
struct hostent *he;
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, &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, &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);
switch (atype) {
case T_A:
case T_AAAA:
if (rdlen != he->h_length) {
snprintf(errstr, NB_DNS_ERRSIZE,
"nb_dns_activity(): bad rdlen %d", 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_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;
return (1);
}
}
nr->hostent = he;
return (1);
}