newlib/winsup/cygwin/libc/minires.c

911 lines
26 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* minires.c. Stub synchronous resolver for Cygwin.
Copyright 2006 Red Hat, Inc.
Written by Pierre A. Humblet <Pierre.Humblet@ieee.org>
This file is part of Cygwin.
This software is a copyrighted work licensed under the terms of the
Cygwin license. Please consult the file "CYGWIN_LICENSE" for
details. */
#include "minires.h"
/***********************************************************************
Utilities
***********************************************************************/
/***********************************************************************
dprintf
***********************************************************************/
void minires_dprintf(const char * format, ...)
{
va_list args;
va_start(args, format);
fprintf(stderr, "Minires: ");
vfprintf(stderr, format, args);
va_end(args);
}
/***********************************************************************
scanline
Put pointers in list[] to the beginning of each space or comma delimited
word in "in", and put the lengths in sizes[] (counting the final 0).
Return the number of words found
***********************************************************************/
static int scanline(char * in, char **list, int * sizes, int maxnum)
{
int i;
char * startp;
for (i = 0; i < maxnum; i++) {
while((*in) && (isspace(*in) || *in == ',')) in++;
if (*in == 0)
break;
startp = in++;
while((*in) && !isspace(*in) && *in != ',') in++;
list[i] = startp;
sizes[i] = in - startp + 1;
if (*in)
*in++ = 0;
}
return i;
}
/***********************************************************************
Read the search string.
***********************************************************************/
void minires_get_search(char * string, res_state statp)
{
char * words[MAXDNSRCH+1], * ptr;
int sizes[MAXDNSRCH+1];
int i, j, debug = statp->options & RES_DEBUG;
i = scanline(string, words, sizes, MAXDNSRCH+1);
ptr = statp->defdname;
for (j = 0; j < i; j++) {
if (j < MAXDNSRCH
&& ptr + sizes[j] < &statp->defdname[DIM(statp->defdname)]) {
statp->dnsrch[j] = strcpy(ptr, words[j]);
statp->dnsrch[j+1] = NULL;
ptr += sizes[j];
DPRINTF(debug, "search \"%s\"\n", words[j]);
}
else
DPRINTF(debug, "no space for \"%s\"\n", words[j]);
}
}
/***********************************************************************
Read options
***********************************************************************/
static void get_options(res_state statp, int i, char **words)
{
char *ptr;
int value;
while (i-- > 0) {
if (!strcasecmp("debug", words[i])) {
statp->options |= RES_DEBUG;
DPRINTF(statp->options & RES_DEBUG, "%s: 1\n", words[i]);
continue;
}
if (!strcasecmp("osquery", words[i])) {
statp->use_os = 1;
DPRINTF(statp->options & RES_DEBUG, "%s: 1\n", words[i]);
continue;
}
if ((ptr = strchr(words[i], ':'))) {
*ptr++ = 0;
value = atoi(ptr);
/* Not supported
if (!strcasecmp("ndots", words[i])) {
statp->ndots = value;
continue;
}
*/
if (!strcasecmp("retry", words[i])) {
if (value < 1)
value = 1;
statp->retry = value;
DPRINTF(statp->options & RES_DEBUG, "%s: %d\n", words[i], value);
continue;
}
if (!strcasecmp("retrans", words[i])) {
if (value < 1)
value = 1;
statp->retrans = value;
DPRINTF(statp->options & RES_DEBUG, "%s: %d\n", words[i], value);
continue;
}
}
DPRINTF(statp->options & RES_DEBUG, "unknown option: \"%s\"\n", words[i]);
}
}
/***********************************************************************
Read the resolv.conf file.
We only look for nameserver, domain, search and options
***********************************************************************/
#if MAXNS > MAXDNSRCH + 1
#define MAXSIZE MAXNS
#else
#define MAXSIZE MAXDNSRCH + 1 /* Make unused one visible */
#endif
static void get_resolv(res_state statp)
{
FILE * fd;
char *words[MAXSIZE + 1], line[4096], *ptr;
int sizes[DIM(words)];
int i, j, ns = 0, have_search, have_address, debug = statp->options & RES_DEBUG;
fd = fopen(_PATH_RESCONF, "r");
DPRINTF(debug, _PATH_RESCONF ": %s\n", fd?"OK":strerror(errno));
if (fd == NULL)
return;
statp->use_os = 0; /* Do not use os_query, except if allowed by "options" */
have_search = (statp->dnsrch[0] != NULL);
have_address = (statp->nscount != 0);
while ( fgets(line, sizeof(line), fd) != 0) {
DPRINTF(debug, "resolv.conf %s", line);
if ((i = scanline(line, words, sizes, DIM(words))) > 0) {
if (!have_address
&& !strncasecmp("nameserver", words[0], sizes[0])) {
for ( j = 1; j < i ; j++) {
unsigned int address;
address = cygwin_inet_addr(words[j]);
if (address == -1) {
DPRINTF(debug, "invalid server \"%s\"\n", words[j]);
}
else if (ns >= MAXNS) {
DPRINTF(debug, "no space for server \"%s\"\n", words[j]);
}
else {
statp->nsaddr_list[ns++].sin_addr.s_addr = address;
statp->nscount++;
DPRINTF(debug, "server \"%s\"\n", words[j]);
}
}
}
else if (!have_search
&& (!strncasecmp("search", words[0], sizes[0])
|| !strncasecmp("domain", words[0], sizes[0]))) {
ptr = statp->defdname;
for (j = 0; j + 1 < i; j++) {
if (j < MAXDNSRCH
&& ptr + sizes[j + 1] < &statp->defdname[DIM(statp->defdname)]) {
statp->dnsrch[j] = strcpy(ptr, words[j+1]);
statp->dnsrch[j+1] = 0;
ptr += sizes[j+1];
DPRINTF(debug, "domain|search \"%s\"\n", statp->dnsrch[j]);
}
else {
DPRINTF(debug, "no space for \"%s\"\n", words[j+1]);
}
}
}
/* Options line */
else if (!strncasecmp("options", words[0], sizes[0]))
get_options(statp, i - 1, &words[1]);
}
}
fclose(fd);
return;
}
/****************************************************************************/
/*
open_sock()
Create a datagram socket and call bind.
****************************************************************************/
static int open_sock(struct sockaddr_in *CliAddr, int debug)
{
int fd;
DPRINTF(debug, "opening UDP socket\n");
/* Create a datagram socket */
if ((fd = cygwin_socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
DPRINTF(debug, "socket(UDP): %s\n", strerror(errno));
return -1;
}
CliAddr->sin_family = AF_INET;
CliAddr->sin_addr.s_addr = htonl(INADDR_ANY);
CliAddr->sin_port = htons(0);
bzero(CliAddr->sin_zero, sizeof(CliAddr->sin_zero));
/* Get a port */
if (cygwin_bind(fd, (struct sockaddr *) CliAddr, sizeof(*CliAddr)) < 0) {
DPRINTF(debug, "bind: %s\n", strerror(errno));
return -1;
}
return fd;
}
/*****************************************************************
*
__res_state()
Undocumented but public. Accessed through _res
*****************************************************************/
static struct __res_state res;
struct __res_state *__res_state(void)
{
return & res;
}
/*****************************************************************
*
res_init()
*****************************************************************/
int res_ninit(res_state statp)
{
int i;
statp->res_h_errno = NETDB_SUCCESS;
statp->nscount = 0;
statp->os_query = NULL;
statp->retrans = RES_TIMEOUT; /* timeout in seconds */
statp->retry = RES_MAXRETRY; /* max number of retries */
statp->use_os = 1; /* use os_query if available and allowed by get_resolv */
statp->mypid = -1;
statp->sockfd = -1;
for (i = 0; i < DIM(statp->dnsrch); i++) statp->dnsrch[i] = 0;
/* resolv.conf (dns servers & search list)*/
get_resolv(statp);
/* Get dns servers and search list from an os-specific routine, set os_query */
get_dns_info(statp);
if (statp->nscount == 0 && !statp->os_query) {
errno = ENONET;
statp->res_h_errno = NETDB_INTERNAL;
DPRINTF(statp->options & RES_DEBUG, "no dns server found\n");
return -1;
}
for (i = 0; i < statp->nscount; i++) {
statp->nsaddr_list[i].sin_family = AF_INET;
statp->nsaddr_list[i].sin_port = htons(NAMESERVER_PORT);
bzero(statp->nsaddr_list[i].sin_zero, sizeof(statp->nsaddr_list[i].sin_zero));
}
/* Only debug may be set before calling init */
statp->options &= RES_DEBUG;
statp->options |= RES_INIT | RES_DEFAULT;
return 0;
}
int res_init()
{
int r = res_ninit(& res);
h_errno = res.res_h_errno;
return r;
}
/*****************************************************************
*
res_close()
*****************************************************************/
void res_nclose(res_state statp)
{
int res;
if (statp->sockfd != -1) {
res = close(statp->sockfd);
DPRINTF(statp->options & RES_DEBUG, "close sockfd %d: %s\n",
statp->sockfd, (res == 0)?"OK":strerror(errno));
statp->sockfd = -1;
}
}
void res_close()
{
res_nclose(& res);
}
/*****************************************************************
*
get_tcp_buf()
*****************************************************************/
static int get_tcp_buf(int fd, unsigned char *buf, int size, int debug)
{
int res;
while (size > 0) {
if ((res = read(fd, buf, size)) < 0) {
DPRINTF(debug, "read: %s\n", strerror(errno));
return -1;
}
DPRINTF(debug, "read %d out of %d\n", res, size);
size -= res;
buf += res;
}
return 0;
}
/*****************************************************************
*
get_tcp()
*****************************************************************/
static int get_tcp(struct sockaddr_in *CliAddr,
const unsigned char * MsgPtr, int MsgLength,
unsigned char * AnsPtr, int AnsLength, int debug)
{
int fd, res = -1;
unsigned short ans_length;
union {short len; u_char buf[sizeof(short)];} len_buf;
DPRINTF(debug, "retrying with TCP\n");
/* Create a tcp socket */
if ((fd = cygwin_socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
DPRINTF(debug, "socket(TCP): %s\n", strerror(errno));
return -1;
}
if (cygwin_connect(fd, (struct sockaddr *) CliAddr, sizeof(* CliAddr)) < 0) {
DPRINTF(debug, "connect: %s\n", strerror(errno));
goto done;
}
/* Send the length then the message */
len_buf.len = htons(MsgLength);
if (write(fd, len_buf.buf, sizeof(len_buf)) != sizeof(len_buf)
|| write(fd, MsgPtr, MsgLength) != MsgLength) {
DPRINTF(debug, "write: %s\n", strerror(errno));
goto done;
}
/* Read the answer length */
if (get_tcp_buf(fd, len_buf.buf, sizeof(len_buf), debug))
goto done;
ans_length = ntohs(len_buf.len);
/* Read the answer */
if (get_tcp_buf(fd, AnsPtr, MIN(ans_length, AnsLength), debug))
goto done;
res = ans_length;
done:
close (fd);
return res;
}
/*****************************************************************
**
res_send
Assumes that the message is a query starting with a short id.
Handles retransmissions until that id is received.
*****************************************************************/
int res_nsend( res_state statp, const unsigned char * MsgPtr,
int MsgLength, unsigned char * AnsPtr, int AnsLength)
{
/* Current server, shared by all tasks */
volatile static unsigned int SServ = 0XFFFFFFFF;
int tcp;
const int debug = statp->options & RES_DEBUG;
fd_set fdset_read;
int rslt, addrLen, transNum, wServ;
struct sockaddr_in mySockAddr, dnsSockAddr;
struct timeval timeOut;
statp->res_h_errno = NETDB_SUCCESS;
if (((statp->options & RES_INIT) == 0) && (res_ninit(statp) != 0))
return -1;
/* Close the socket if it had been opened before a fork.
Reuse of pid's cannot hurt */
if ((statp->sockfd != -1) && (statp->mypid != getpid())) {
res_nclose(statp);
}
/* Open a socket for this process */
if (statp->sockfd == -1) {
/* Create a socket and bind it (to any port) */
statp->sockfd = open_sock(& mySockAddr, debug);
if (statp->sockfd < 0 ) {
statp->res_h_errno = NETDB_INTERNAL;
return -1;
}
/* Set close on exec flag */
if (fcntl(statp->sockfd, F_SETFD, 1) == -1) {
DPRINTF(debug, "fcntl: %s\n",
strerror(errno));
statp->res_h_errno = NETDB_INTERNAL;
return -1;
}
statp->mypid = getpid();
if (SServ == 0XFFFFFFFF) /* Pseudo random */
SServ = statp->mypid % statp->nscount;
}
transNum = 0;
while ( transNum++ < statp->retry) {
if ((wServ = SServ + 1) >= statp->nscount)
wServ = 0;
SServ = wServ;
/* Send the message */
rslt = cygwin_sendto(statp->sockfd, MsgPtr, MsgLength, 0,
(struct sockaddr *) &statp->nsaddr_list[wServ],
sizeof(struct sockaddr_in));
DPRINTF(debug, "sendto: server %08x sockfd %d %s\n",
statp->nsaddr_list[wServ].sin_addr.s_addr,
statp->sockfd, (rslt == MsgLength)?"OK":strerror(errno));
if (rslt != MsgLength) {
statp->res_h_errno = NETDB_INTERNAL;
return -1;
};
/*
Wait for a reply with select()
*/
FD_ZERO(&fdset_read);
FD_SET (statp->sockfd, &fdset_read );
timeOut.tv_sec = statp->retrans;
timeOut.tv_usec = 0;
rslt = cygwin_select(statp->sockfd + 1, &fdset_read, NULL, NULL, &timeOut);
if ( rslt == 0 ) { /* Timeout */
DPRINTF(statp->options & RES_DEBUG, "timeout for server %08x\n",
statp->nsaddr_list[wServ].sin_addr.s_addr);
continue;
}
else if ((rslt != 1) || (FD_ISSET(statp->sockfd, &fdset_read) == 0)) {
DPRINTF(debug, "select: %s\n", strerror(errno));
statp->res_h_errno = NETDB_INTERNAL;
return -1;
}
addrLen = sizeof(dnsSockAddr);
rslt = cygwin_recvfrom(statp->sockfd, AnsPtr, AnsLength, 0,
(struct sockaddr *) & dnsSockAddr, & addrLen);
if (rslt <= 0) {
DPRINTF(debug, "recvfrom: %s\n", strerror(errno));
statp->res_h_errno = NETDB_INTERNAL;
return -1;
}
/*
Prepare to retry with tcp
*/
for (tcp = 0; tcp < 2; tcp++) {
/* Check if this is the message we expected */
if ((*MsgPtr == *AnsPtr) /* Ids match */
&& (*(MsgPtr + 1) == *(AnsPtr + 1))
/* We have stopped checking this because the question may not be present on error,
in particular when the name in the question is not a valid name.
Simply check that the header is present. */
&& (rslt >= HFIXEDSZ)
/* && (rslt >= MsgLength )
&& (memcmp(MsgPtr + HFIXEDSZ, AnsPtr + HFIXEDSZ, MsgLength - HFIXEDSZ) == 0) */
&& ((AnsPtr[2] & QR) != 0)) {
DPRINTF(debug, "answer %u from %08x. Error %d. Count %d.\n",
rslt, dnsSockAddr.sin_addr.s_addr,
AnsPtr[3] & ERR_MASK, AnsPtr[6]*256 + AnsPtr[7]);
#if 0
NETDB_INTERNAL -1 /* see errno */
NETDB_SUCCESS 0 /* no problem */
HOST_NOT_FOUND 1 /* Authoritative Answer Host not found */
TRY_AGAIN 2 /* Non-Authoritive Host not found, or SERVERFAIL */
Also seen returned by some servers when the name is too long
NO_RECOVERY 3 /* Non recoverable errors, FORMERR, REFUSED, NOTIMP */
NO_DATA 4 /* Valid name, no data record of requested type */
#endif
if ((AnsPtr[3] & ERR_MASK) == NOERROR) {
if ((AnsPtr[2] & TC) && !(statp->options & RES_IGNTC)) { /* Truncated. Try TCP */
rslt = get_tcp(&statp->nsaddr_list[wServ], MsgPtr, MsgLength,
AnsPtr, AnsLength, statp->options & RES_DEBUG);
continue;
}
else if ((AnsPtr[6] | AnsPtr[7])!= 0)
return rslt;
else
statp->res_h_errno = NO_DATA;
}
else {
/* return HOST_NOT_FOUND even for non-authoritative answers */
if ((AnsPtr[3] & ERR_MASK) == NXDOMAIN)
statp->res_h_errno = HOST_NOT_FOUND;
else if ((AnsPtr[3] & ERR_MASK) == SERVFAIL)
statp->res_h_errno = TRY_AGAIN;
else
statp->res_h_errno = NO_RECOVERY;
}
return -1;
}
else {
DPRINTF(debug, "unexpected answer %u from %x to query to %x\n",
rslt, dnsSockAddr.sin_addr.s_addr,
statp->nsaddr_list[wServ].sin_addr.s_addr);
break;
}
} /* TCP */
}
DPRINTF(debug, "too many retries\n");
statp->res_h_errno = TRY_AGAIN;
return -1;
}
int res_send( const unsigned char * MsgPtr, int MsgLength,
unsigned char * AnsPtr, int AnsLength)
{
int r = res_nsend(& res, MsgPtr, MsgLength, AnsPtr, AnsLength);
h_errno = res.res_h_errno;
return r;
}
/*****************************************************************
*
res_mkquery
Return: packet size
-1 name format is incorrect
*****************************************************************/
int res_nmkquery (res_state statp,
int op, const char * dnameptr, int qclass, int qtype,
const unsigned char * dataptr, int datalen,
const unsigned char * newrr, unsigned char * buf, int buflen)
{
int i, len;
short id;
if (op == QUERY) {
/* Write the name and verify buffer length */
len = dn_comp(dnameptr, buf + HFIXEDSZ, buflen - HFIXEDSZ - QFIXEDSZ, NULL, NULL);
if (len < 0) {
DPRINTF(statp->options & RES_DEBUG,
"\"%s\" invalid or buffer too short\n", dnameptr);
statp->res_h_errno = NETDB_INTERNAL;
return -1;
}
/* Fill the header */
id = statp->id;
PUTSHORT(id, buf);
PUTSHORT(RD, buf);
PUTSHORT(1, buf); /* Number of questions */
for (i = 0; i < 3; i++)
PUTSHORT(0, buf); /* Number of answers */
/* Write qtype and qclass */
buf += len;
PUTSHORT(qtype, buf);
PUTSHORT(qclass, buf);
return len + 16; /* packet size */
}
else { /* Not implemented */
errno = ENOSYS;
statp->res_h_errno = NETDB_INTERNAL;
return -1;
}
}
int res_mkquery (int op, const char * dnameptr, int qclass, int qtype,
const unsigned char * dataptr, int datalen,
const unsigned char * newrr, unsigned char * buf, int buflen)
{
int r = res_nmkquery (& res, op, dnameptr, qclass, qtype,
dataptr, datalen, newrr, buf, buflen);
h_errno = res.res_h_errno;
return r;
}
/*****************************************************************
*
res_query()
*****************************************************************/
int res_nquery( res_state statp, const char * DomName, int Class, int Type,
unsigned char * AnsPtr, int AnsLength)
{
u_char packet[PACKETSZ];
int len;
DPRINTF(statp->options & RES_DEBUG, "query \"%s\" type %d\n", DomName, Type);
statp->res_h_errno = NETDB_SUCCESS;
/* If a hook exists to a native implementation, use it */
if (statp->os_query)
return ((os_query_t *) statp->os_query)(statp, DomName, Class, Type, AnsPtr, AnsLength);
if ((len = res_nmkquery (statp, QUERY, DomName, Class, Type,
0, 0, 0, packet, PACKETSZ)) < 0)
return -1;
return res_nsend( statp, packet, len, AnsPtr, AnsLength);
}
int res_query( const char * DomName, int Class, int Type, unsigned char * AnsPtr, int AnsLength)
{
int r = res_nquery(& res, DomName, Class, Type, AnsPtr, AnsLength);
h_errno = res.res_h_errno;
return r;
}
/*****************************************************************
*
res_querydomain()
*****************************************************************/
int res_nquerydomain( res_state statp, const char * Name, const char * DomName,
int Class, int Type, unsigned char * AnsPtr, int AnsLength)
{
char fqdn[MAXDNAME], *ptr;
int nlen;
if (!DomName)
ptr = (char *) Name;
else if ((nlen = strlen(Name)) >= sizeof(fqdn) - 1)
goto error;
else {
strcpy(fqdn, Name);
ptr = &fqdn[nlen];
if (nlen && *(ptr - 1) != '.')
*(ptr++ - 1) = '.';
fqdn[sizeof(fqdn) - 1] = 0;
strncpy(ptr, DomName, sizeof(fqdn) - (ptr - fqdn));
if (fqdn[sizeof(fqdn) - 1])
goto error;
ptr = fqdn;
}
return res_nquery(statp, ptr, Class, Type, AnsPtr, AnsLength);
error:
DPRINTF(statp->options & RES_DEBUG, "querydomain: name too long\n");
errno = EINVAL;
statp->res_h_errno = NETDB_INTERNAL;;
return -1;
}
int res_querydomain( const char * Name, const char * DomName, int Class,
int Type, unsigned char * AnsPtr, int AnsLength)
{
int r = res_nquerydomain(& res, Name, DomName, Class, Type, AnsPtr,
AnsLength);
h_errno = res.res_h_errno;
return r;
}
/*****************************************************************
*
res_search()
*****************************************************************/
int res_nsearch( res_state statp, const char * DomName, int Class, int Type,
unsigned char * AnsPtr, int AnsLength)
{
int len, stat, i;
char fullDomName[MAXDNAME], *ptr, *sptr;
DPRINTF(statp->options & RES_DEBUG, "search \"%s\" type %d\n", DomName, Type);
if (((statp->options & RES_INIT) == 0) && (res_ninit(statp) != 0))
return -1;
stat = res_nquery( statp, DomName, Class, Type, AnsPtr, AnsLength);
/* Check if will skip search */
if (statp->res_h_errno != HOST_NOT_FOUND /* Success or hard failure */
|| ((ptr = strrchr(DomName, '.')) && (!*(ptr+1))) /* Final dot */
|| (((statp->options & RES_DNSRCH) == 0) /* Or no search */
&& ((ptr != NULL) /* And some dot */
|| ((statp->options & RES_DEFNAMES) == 0)))/* or no def domain */
|| (!(sptr = statp->dnsrch[0])))
return stat;
len = strlen(DomName);
if (len >= MAXDNAME - 1) /* Space for next dot */
goto error;
strcpy(fullDomName, DomName);
fullDomName[len++] = '.';
fullDomName[MAXDNAME - 1] = 0; /* Overflow indicator */
i = 0;
do {
strncpy(fullDomName + len, sptr, MAXDNAME - len);
if (fullDomName[MAXDNAME - 1])
goto error;
stat = res_nquery(statp, fullDomName, Class, Type, AnsPtr, AnsLength);
} while ((sptr = statp->dnsrch[++i]) != NULL
&& statp->res_h_errno == HOST_NOT_FOUND
&& (statp->options & RES_DNSRCH) != 0);
/* Return last stat */
return stat;
error:
DPRINTF(statp->options & RES_DEBUG, "name too long during search\n");
errno = EINVAL;
statp->res_h_errno = NETDB_INTERNAL;
return -1;
}
int res_search( const char * DomName, int Class, int Type,
unsigned char * AnsPtr, int AnsLength)
{
int r = res_nsearch(& res, DomName, Class, Type, AnsPtr, AnsLength);
h_errno = res.res_h_errno;
return r;
}
/*****************************************************************
*
dn_expand
*****************************************************************/
int dn_expand(const unsigned char *msg, const unsigned char *eomorig,
const unsigned char *comp_dn, char *exp_dn, int length)
{
unsigned int len, complen = 0;
const unsigned char *comp_dn_orig = comp_dn;
/* char * exp_start = exp_dn; */
errno = EINVAL;
if (comp_dn >= eomorig)
goto expand_fail;
if ((len = *comp_dn++) == 0) /* Weird case */
exp_dn++;
else do {
if (len <= MAXLABEL) {
if ((length -= (len + 1)) > 0 /* Need space for final . */
&& comp_dn + len <= eomorig) {
do { *exp_dn++ = *comp_dn++; } while (--len != 0);
*exp_dn++ = '.';
}
else
goto expand_fail;
}
else if (len >= (128+64)) {
if (!complen) /* Still in the original field? */
complen = (comp_dn - comp_dn_orig) + 1;
comp_dn = msg + (((len & ~(128+64)) << 8) + *comp_dn);
if (comp_dn >= eomorig)
goto expand_fail;
}
else
goto expand_fail;
} while ((len = *comp_dn++) != 0);
/* Replace last . with a 0 */
*(--exp_dn) = 0;
if (!complen)
complen = comp_dn - comp_dn_orig;
/* fprintf(stderr, "dn_expand %s\n", exp_start); */
return complen;
expand_fail:
/* fprintf(stderr, "dn_expand fails\n"); */
return -1;
}
/*****************************************************************
*
dn_comp
Return -1 in case of overflow, but still fill buffer correctly.
We do not check the alphabet of the host names
nor the length of the compressed name and we
preserve the letter cases.
*****************************************************************/
int dn_comp(const char * exp_dn, u_char * comp_dn, int length,
u_char ** dnptrs, u_char ** lastdnptr)
{
u_char *cptr = comp_dn, *dptr, *lptr, *rptr;
unsigned int i, len;
u_char * const eptr = comp_dn + length - 1; /* Last valid */
errno = EINVAL;
if (*exp_dn == '.' && !*(exp_dn + 1))
exp_dn++;
while (1) {
if (*exp_dn == '.' || cptr > eptr)
return -1;
if (*exp_dn == 0) {
*cptr++ = 0;
break;
}
/* Try to compress */
if (dnptrs) {
for (i = 1; dnptrs[i]; i++) {
dptr = dnptrs[i];
if (dptr >= comp_dn) /* Handle name.name */
continue;
rptr = (u_char *) exp_dn;
len = *dptr++;
while (1) {
do {
if (*dptr++ != *rptr++)
goto next_dn;
} while (--len);
len = *dptr++;
if (len == 0) { /* last label */
if (!*rptr || (*rptr == '.' && !*(rptr + 1))) { /* Full match */
len = (dnptrs[i] - dnptrs[0]) | 0xC000;
/* Write pointer */
*cptr++ = len >> 8;
if (cptr > eptr)
return -1;
*cptr++ = len;
goto done;
}
goto next_dn;
}
if (*rptr++ != '.')
goto next_dn;
if (len >= 128 + 64) {
dptr = dnptrs[0] + ((len - 128 - 64) << 8) + *dptr;
len = *dptr++;
}
}
next_dn: ;
}
/* Record label if asked and if space is available and if not too far off */
if (lastdnptr && (lastdnptr != &dnptrs[i]) && (cptr - dnptrs[0]) < 0xC000) {
dnptrs[i] = cptr;
dnptrs[i+1] = NULL;
}
}
/* Write label */
lptr = cptr++; /* Length byte */
rptr = (u_char *) exp_dn;
do {
if (cptr <= eptr)
*cptr++ = *rptr;
} while ((*++rptr != '.') && (*rptr != 0));
len = rptr - (u_char *) exp_dn;
if (len > MAXLABEL)
return -1;
*lptr = len;
exp_dn = (char *) rptr;
if (*exp_dn != 0)
exp_dn++; /* Skip over . */
}
done:
return cptr - comp_dn;
}
/*****************************************************************
*
dn_skipname
Measures the compressed domain name length and returns it.
*****************************************************************/
int dn_skipname(const unsigned char *comp_dn, const unsigned char *eom)
{
int len;
const unsigned char *comp_dn_orig = comp_dn;
do {
len = *comp_dn++;
if (len >= (128 + 64)) {
comp_dn++;
break;
}
if (len > MAXLABEL ||
(comp_dn += len) > eom)
return -1;
} while (len != 0);
return comp_dn - comp_dn_orig;
}