* cygwin.din: Export gethostbyname2.

* net.cc: define _CYGWIN_IN_H and include resolv.h.
	(realloc_ent): New function.
	(dup_ent): Call realloc_ent.
	(memcpy4to6): New function.
	(dn_length1): New function.
	(gethostby_helper): New function.
	(gethostbyname2): New function.
	* posix.sgml: Add gethostbyname2.
	* include/cygwin/version.h: Bump API minor number.
	* libc/minires.c (get_options): Look for "inet6" and apply bounds
	to "retry" and "retrans".
	(res_ninit): Set the default options at the beginning.
	(dn_expand): Fix "off by one".
This commit is contained in:
Corinna Vinschen 2009-03-06 16:31:26 +00:00
parent 45e20e47ba
commit 6f57cb4a52
6 changed files with 384 additions and 24 deletions

View File

@ -1,3 +1,20 @@
2009-03-06 Pierre A. Humblet <pierre@phumblet.no-ip.org>
* cygwin.din: Export gethostbyname2.
* net.cc: define _CYGWIN_IN_H and include resolv.h.
(realloc_ent): New function.
(dup_ent): Call realloc_ent.
(memcpy4to6): New function.
(dn_length1): New function.
(gethostby_helper): New function.
(gethostbyname2): New function.
* posix.sgml: Add gethostbyname2.
* include/cygwin/version.h: Bump API minor number.
* libc/minires.c (get_options): Look for "inet6" and apply bounds
to "retry" and "retrans".
(res_ninit): Set the default options at the beginning.
(dn_expand): Fix "off by one".
2009-03-06 Corinna Vinschen <corinna@vinschen.de>
* cygwin.din: Export wprintf, fwprintf, swprintf, vwprintf, vfwprintf,

View File

@ -636,6 +636,7 @@ _getgroups = getgroups SIGFE
_getgroups32 = getgroups32 SIGFE
gethostbyaddr = cygwin_gethostbyaddr SIGFE
gethostbyname = cygwin_gethostbyname SIGFE
gethostbyname2 SIGFE
gethostid SIGFE
gethostname = cygwin_gethostname SIGFE
_gethostname = cygwin_gethostname SIGFE

View File

@ -350,12 +350,13 @@ details. */
199: Export open_wmemstream.
200: Export mbsnrtowcs, wcsnrtombs.
201: Export wprintf, fwprintf, swprintf, vwprintf, vfwprintf, vswprintf.
202: Export gethostbyname2.
*/
/* Note that we forgot to bump the api for ualarm, strtoll, strtoull */
#define CYGWIN_VERSION_API_MAJOR 0
#define CYGWIN_VERSION_API_MINOR 201
#define CYGWIN_VERSION_API_MINOR 202
/* There is also a compatibity version number associated with the
shared memory regions. It is incremented when incompatible

View File

@ -1,6 +1,6 @@
/* minires.c. Stub synchronous resolver for Cygwin.
Copyright 2006, 2008 Red Hat, Inc.
Copyright 2006, 2008, 2009 Red Hat, Inc.
Written by Pierre A. Humblet <Pierre.Humblet@ieee.org>
@ -99,6 +99,11 @@ static void get_options(res_state statp, int i, char **words)
DPRINTF(statp->options & RES_DEBUG, "%s: 1\n", words[i]);
continue;
}
if (!strcasecmp("inet6", words[i])) {
statp->options |= RES_USE_INET6;
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]);
@ -114,16 +119,22 @@ static void get_options(res_state statp, int i, char **words)
continue;
}
*/
if (!strcasecmp("retry", words[i])) {
if (!strcasecmp("retry", words[i])
|| !strcasecmp("attempts", words[i])) {
if (value < 1)
value = 1;
else if (value > RES_MAXRETRY)
value = RES_MAXRETRY;
statp->retry = value;
DPRINTF(statp->options & RES_DEBUG, "%s: %d\n", words[i], value);
continue;
}
if (!strcasecmp("retrans", words[i])) {
if (!strcasecmp("retrans", words[i])
|| !strcasecmp("timeout", words[i])) {
if (value < 1)
value = 1;
else if (value > RES_MAXRETRANS)
value = RES_MAXRETRANS;
statp->retrans = value;
DPRINTF(statp->options & RES_DEBUG, "%s: %d\n", words[i], value);
continue;
@ -270,6 +281,9 @@ int res_ninit(res_state statp)
int i;
statp->res_h_errno = NETDB_SUCCESS;
/* Only debug may be set before calling init */
statp->options &= RES_DEBUG;
statp->options |= RES_INIT | RES_DEFAULT;
statp->nscount = 0;
statp->os_query = NULL;
statp->retrans = RES_TIMEOUT; /* timeout in seconds */
@ -299,9 +313,6 @@ int res_ninit(res_state statp)
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;
}
@ -806,7 +817,7 @@ int dn_expand(const unsigned char *msg, const unsigned char *eomorig,
exp_dn++;
else do {
if (len <= MAXLABEL) {
if ((length -= (len + 1)) > 0 /* Need space for final . */
if ((length -= (len + 1)) >= 0 /* Need space for final . */
&& comp_dn + len <= eomorig) {
do { *exp_dn++ = *comp_dn++; } while (--len != 0);
*exp_dn++ = '.';
@ -836,7 +847,6 @@ expand_fail:
return -1;
}
/*****************************************************************
*
dn_comp
@ -926,8 +936,7 @@ int dn_comp(const char * exp_dn, u_char * comp_dn, int length,
}
/*****************************************************************
*
dn_skipname
* dn_skipname
Measures the compressed domain name length and returns it.
*****************************************************************/
@ -949,3 +958,38 @@ int dn_skipname(const unsigned char *comp_dn, const unsigned char *eom)
return comp_dn - comp_dn_orig;
}
/*****************************************************************
* dn_length1 For internal use
Return length of uncompressesed name incl final 0.
*****************************************************************/
int dn_length1(const unsigned char *msg, const unsigned char *eomorig,
const unsigned char *comp_dn)
{
unsigned int len, length = 0;
errno = EINVAL;
if (comp_dn >= eomorig)
goto expand_fail;
else while ((len = *comp_dn++) != 0) {
if (len <= MAXLABEL) {
if ((comp_dn += len) <= eomorig)
length += len + 1;
else
goto expand_fail;
}
else if (len >= (128+64)) {
comp_dn = msg + (((len & ~(128+64)) << 8) + *comp_dn);
if (comp_dn >= eomorig)
goto expand_fail;
}
else
goto expand_fail;
}
return length;
expand_fail:
return -1;
}

View File

@ -1,7 +1,7 @@
/* net.cc: network-related routines.
Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
2005, 2006, 2007 Red Hat, Inc.
2005, 2006, 2007, 2008, 2009 Red Hat, Inc.
This file is part of Cygwin.
@ -43,6 +43,8 @@ details. */
#include "cygwin/in6.h"
#include "ifaddrs.h"
#include "tls_pbuf.h"
#define _CYGWIN_IN_H
#include <resolv.h>
extern "C"
{
@ -53,6 +55,9 @@ extern "C"
int sscanf (const char *, const char *, ...);
int cygwin_inet_aton(const char *, struct in_addr *);
const char *cygwin_inet_ntop (int, const void *, char *, socklen_t);
int dn_length1(const unsigned char *, const unsigned char *,
const unsigned char *);
} /* End of "C" section */
const struct in6_addr in6addr_any = {{IN6ADDR_ANY_INIT}};
@ -264,6 +269,25 @@ struct pservent
static const char *entnames[] = {"host", "proto", "serv"};
static unionent *
realloc_ent (unionent *&dst, int sz)
{
/* Allocate the storage needed. Allocate a rounded size to attempt to force
reuse of this buffer so that a poorly-written caller will not be using
a freed buffer. */
unsigned rsz = 256 * ((sz + 255) / 256);
unionent * ptr;
if ((ptr = (unionent *) realloc (dst, rsz)))
dst = ptr;
return ptr;
}
static inline hostent *
realloc_ent (int sz, hostent *)
{
return (hostent *) realloc_ent (_my_tls.locals.hostent_buf, sz);
}
/* Generic "dup a {host,proto,serv}ent structure" function.
This is complicated because we need to be able to free the
structure at any point and we can't rely on the pointer contents
@ -310,7 +334,7 @@ dup_ent (unionent *&dst, unionent *src, unionent::struct_type type)
break;
}
/* Every *ent begins with a name. Calculate it's length. */
/* Every *ent begins with a name. Calculate its length. */
int namelen = strlen_round (src->name);
sz = struct_sz + namelen;
@ -355,13 +379,8 @@ dup_ent (unionent *&dst, unionent *src, unionent::struct_type type)
}
}
/* Allocate the storage needed. Allocate a rounded size to attempt to force
reuse of this buffer so that a poorly-written caller will not be using
a freed buffer. */
unsigned rsz = 256 * ((sz + 255) / 256);
dst = (unionent *) realloc (dst, rsz);
if (dst)
/* Allocate the storage needed. */
if (realloc_ent (dst, sz))
{
memset (dst, 0, sz);
/* This field is common to all *ent structures but named differently
@ -857,6 +876,287 @@ cygwin_gethostbyaddr (const char *addr, int len, int type)
return res;
}
static void
memcpy4to6 (char *dst, const u_char *src)
{
const unsigned int h[] = {0, 0, htonl (0xFFFF)};
memcpy (dst, h, 12);
memcpy (dst + 12, src, NS_INADDRSZ);
}
static hostent *
gethostby_helper (const char *name, const int af, const int type,
const int addrsize_in, const int addrsize_out)
{
/* Get the data from the name server */
const int maxcount = 3;
int old_errno, ancount = 0, anlen = 1024, msgsize = 0;
u_char *ptr, *msg = NULL;
int sz;
hostent *ret;
char *string_ptr;
while ((anlen > msgsize) && (ancount++ < maxcount))
{
msgsize = anlen;
ptr = (u_char *) realloc (msg, msgsize);
if (ptr == NULL )
{
old_errno = errno;
free (msg);
set_errno (old_errno);
h_errno = NETDB_INTERNAL;
return NULL;
}
msg = ptr;
anlen = res_search (name, ns_c_in, type, msg, msgsize);
}
if (ancount >= maxcount)
{
free (msg);
h_errno = NO_RECOVERY;
return NULL;
}
if (anlen < 0) /* errno and h_errno are set */
{
old_errno = errno;
free (msg);
set_errno (old_errno);
return NULL;
}
u_char *eomsg = msg + anlen - 1;
/* We scan the answer records to determine the required memory size.
They can be corrupted and we don't fully trust that the message
follows the standard exactly. glibc applies some checks that
we emulate.
The answers are copied in the hostent structure in a second scan.
To simplify the second scan we store information as follows:
- "class" is replaced by the compressed name size
- the first 16 bits of the "ttl" store the expanded name size + 1
- the last 16 bits of the "ttl" store the offset to the next valid record.
Note that "type" is rewritten in host byte order. */
class record {
public:
unsigned type: 16; // type
unsigned complen: 16; // class or compressed length
unsigned namelen1: 16; // expanded length (with final 0)
unsigned next_o: 16; // offset to next valid
unsigned size: 16; // data size
u_char data[]; // data
record * next () { return (record *) (((char *) this) + next_o); }
void set_next ( record * nxt) { next_o = ((char *) nxt) - ((char *) this); }
u_char * name () { return (u_char *) (((char *) this) - complen); }
};
record * anptr = NULL, * prevptr = NULL, * curptr;
int i, alias_count = 0, string_size = 0, address_count = 0;
int complen, namelen1 = 0, address_len = 0, antype, anclass, ansize;
/* Get the count of answers */
ancount = ntohs (((HEADER *) msg)->ancount);
/* Skip the question, it was verified by res_send */
ptr = msg + sizeof (HEADER);
if ((complen = dn_skipname (ptr, eomsg)) < 0)
goto corrupted;
/* Point to the beginning of the answer section */
ptr += complen + NS_QFIXEDSZ;
/* Scan the answer records to determine the sizes */
for (i = 0; i < ancount; i++, ptr = curptr->data + ansize)
{
if ((complen = dn_skipname (ptr, eomsg)) < 0)
goto corrupted;
curptr = (record *) (ptr + complen);
antype = ntohs (curptr->type);
anclass = ntohs (curptr->complen);
ansize = ntohs (curptr->size);
/* Class must be internet */
if (anclass != ns_c_in)
continue;
curptr->complen = complen;
if ((namelen1 = dn_length1 (msg, eomsg, curptr-> name())) <= 0)
goto corrupted;
if (antype == ns_t_cname)
{
alias_count++;
string_size += namelen1;
}
else if (antype == type)
{
ansize = ntohs (curptr->size);
if (ansize != addrsize_in)
continue;
if (address_count == 0)
{
address_len = namelen1;
string_size += namelen1;
}
else if (address_len != namelen1)
continue;
address_count++;
}
/* Update the records */
curptr->type = antype; /* Host byte order */
curptr->namelen1 = namelen1;
if (! anptr)
anptr = prevptr = curptr;
else
{
prevptr->set_next (curptr);
prevptr = curptr;
}
}
/* If there is no address, quit */
if (address_count == 0)
{
free (msg);
h_errno = NO_DATA;
return NULL;
}
/* Determine the total size */
sz = DWORD_round (sizeof(hostent))
+ sizeof (char *) * (alias_count + address_count + 2)
+ string_size
+ address_count * addrsize_out;
ret = realloc_ent (sz, (hostent *) NULL);
if (! ret)
{
old_errno = errno;
free (msg);
set_errno (old_errno);
h_errno = NETDB_INTERNAL;
return NULL;
}
ret->h_addrtype = af;
ret->h_length = addrsize_out;
ret->h_aliases = (char **) (((char *) ret) + DWORD_round (sizeof(hostent)));
ret->h_addr_list = ret->h_aliases + alias_count + 1;
string_ptr = (char *) (ret->h_addr_list + address_count + 1);
/* Rescan the answers */
ancount = alias_count + address_count; /* Valid records */
alias_count = address_count = 0;
for (i = 0, curptr = anptr; i < ancount; i++, curptr = curptr->next ())
{
antype = curptr->type;
if (antype == ns_t_cname)
{
complen = dn_expand (msg, eomsg, curptr->name (), string_ptr, string_size);
#ifdef DEBUGGING
if (complen != curptr->complen)
go to debugging;
#endif
ret->h_aliases[alias_count++] = string_ptr;
namelen1 = curptr->namelen1;
string_ptr += namelen1;
string_size -= namelen1;
continue;
}
if (antype == type)
{
if (address_count == 0)
{
complen = dn_expand (msg, eomsg, curptr->name(), string_ptr, string_size);
#ifdef DEBUGGING
if (complen != curptr->complen)
go to debugging;
#endif
ret->h_name = string_ptr;
namelen1 = curptr->namelen1;
string_ptr += namelen1;
string_size -= namelen1;
}
ret->h_addr_list[address_count++] = string_ptr;
if (addrsize_in != addrsize_out)
memcpy4to6 (string_ptr, curptr->data);
else
memcpy (string_ptr, curptr->data, addrsize_in);
string_ptr += addrsize_out;
string_size -= addrsize_out;
continue;
}
#ifdef DEBUGGING
/* Should not get here */
go to debugging;
#endif
}
#ifdef DEBUGGING
if (string_size < 0)
go to debugging;
#endif
free (msg);
ret->h_aliases[alias_count] = NULL;
ret->h_addr_list[address_count] = NULL;
return ret;
corrupted:
free (msg);
/* Hopefully message corruption errors are temporary.
Should it be NO_RECOVERY ? */
h_errno = TRY_AGAIN;
return NULL;
#ifdef DEBUGGING
debugging:
system_printf ("Please debug.");
free (msg);
free (ret);
h_errno = NO_RECOVERY;
return NULL;
#endif
}
/* gethostbyname2: standards? */
extern "C" struct hostent *
gethostbyname2 (const char *name, int af)
{
sig_dispatch_pending ();
myfault efault;
if (efault.faulted (EFAULT))
return NULL;
if (!(_res.options & RES_INIT))
res_init();
bool v4to6 = _res.options & RES_USE_INET6;
int type, addrsize_in, addrsize_out;
switch (af)
{
case AF_INET:
addrsize_in = NS_INADDRSZ;
addrsize_out = (v4to6) ? NS_IN6ADDRSZ : NS_INADDRSZ;
type = ns_t_a;
break;
case AF_INET6:
addrsize_in = addrsize_out = NS_IN6ADDRSZ;
type = ns_t_aaaa;
break;
default:
set_errno (EAFNOSUPPORT);
h_errno = NETDB_INTERNAL;
return NULL;
}
return gethostby_helper (name, af, type, addrsize_in, addrsize_out);
}
/* exported as accept: standards? */
extern "C" int
cygwin_accept (int fd, struct sockaddr *peer, socklen_t *len)
@ -2486,10 +2786,6 @@ cygwin_sendmsg (int fd, const struct msghdr *msg, int flags)
* SOFTWARE.
*/
#define IN6ADDRSZ 16
#define INADDRSZ 4
#define INT16SZ 2
/* int
* inet_pton4(src, dst)
* like inet_aton() but without all the hexadecimal and shorthand.

View File

@ -1067,6 +1067,7 @@ also IEEE Std 1003.1-2008 (POSIX.1-2008).</para>
gcvt (SUSv3)
gethostbyaddr (SUSv3)
gethostbyname (SUSv3)
gethostbyname2 (first defined in BIND 4.9.4)
getpass (SUSv2)
getutent (XPG2)
getutid (XPG2)