1
0
mirror of https://sourceware.org/git/glibc.git synced 2025-07-30 22:43:12 +03:00

* include/resolv.h: Adjust __libc_res_nquery and __libc_res_nsend

prototypes.
	* include/arpa/nameser_compat.h: Define T_UNSPEC.
	* nis/Versions (libnss_nis): Export _nss_nis_gethostbyname4_r.
	(libnss_nisplus): Export _nss_nisplus_gethostbyname4_r.
	* nis/nss_nis/nis-hosts.c (LINE_PARSER): Change to also handle
	af==AF_UNSPEC.
	(_nss_nis_gethostbyname4_r): New function.
	* nis/nss_nisplus/nisplus-hosts.c (_nss_nisplus_parse_hostent):
	Change to also handle af==AF_UNSPEC.
	(get_tablename): New function.  Use it to avoid duplication.
	(_nss_nisplus_gethostbyname4_r): New function.
	* nscd/aicache.c (addhstaiX): Use gethostbyname4_r function is
	available.
	* nss/Versions (libnss_files): Export _nss_files_gethostbyname4_r.
	* nss/nss.h: Define struct gaih_addrtuple.
	* nss/nss_files/files-hosts.c (LINE_PARSER): Change to also handle
	af==AF_UNSPEC.
	(_nss_files_gethostbyname4_r): New function.
	* resolv/Versions (libnss_dns): Export _nss_dns_gethostbyname4_r.
	* resolv/gethnmaddr.c: Adjust __libc_res_nsearch and __libc_res_nquery
	calls.
	* resolv/res_query.c (__libc_res_nquery): Take two additional
	parameters for second answer buffer.  Handle type=T_UNSPEC to mean
	look up IPv4 and IPv6.
	Change all callers.
	* resolv/res_send.c (__libc_res_nsend): Take five aditional parameters
	for an additional query and answer buffer.  Pass to send_vc and
	send_dg.
	(send_vc): Send possibly two requests and receive two answers.
	(send_dg): Likewise.
	* resolv/nss_dns/dns-host.c: Adjust calls to __libc_res_nsearch and
	__libc_res_nquery.
	(_nss_dns_gethostbyname4_r): New function.
	(gaih_getanswer_slice): Likewise.
	(gaih_getanswer): Likewise.
	* resolv/nss_dns/dns-canon.c (_nss_dns_getcanonname_r): Adjust
	__libc_res_nquery call.
	* resolv/nss_dns/dns-network.c (_nss_dns_getnetbyaddr_r): Likewise.
	(_nss_dns_getnetbyname_r): Adjust __libc_res_nsearch call.
	* sysdeps/posix/getaddrinfo.c: Use gethostbyname4_r function is
	available.
This commit is contained in:
Ulrich Drepper
2008-05-10 23:27:39 +00:00
parent a82e8bb8d0
commit 1eb946b935
17 changed files with 1565 additions and 527 deletions

View File

@ -176,10 +176,14 @@ evNowTime(struct timespec *res) {
/* Forward. */
static int send_vc(res_state, const u_char *, int,
u_char **, int *, int *, int, u_char **);
const u_char *, int,
u_char **, int *, int *, int, u_char **,
u_char **, int *, int *);
static int send_dg(res_state, const u_char *, int,
const u_char *, int,
u_char **, int *, int *, int,
int *, int *, u_char **);
int *, int *, u_char **,
u_char **, int *, int *);
#ifdef DEBUG
static void Aerror(const res_state, FILE *, const char *, int,
const struct sockaddr *);
@ -334,33 +338,41 @@ libresolv_hidden_def (res_queriesmatch)
int
__libc_res_nsend(res_state statp, const u_char *buf, int buflen,
u_char *ans, int anssiz, u_char **ansp)
const u_char *buf2, int buflen2,
u_char *ans, int anssiz, u_char **ansp, u_char **ansp2,
int *nansp2)
{
int gotsomewhere, terrno, try, v_circuit, resplen, ns, n;
int gotsomewhere, terrno, try, v_circuit, resplen, resplen2, ns, n;
if (statp->nscount == 0) {
__set_errno (ESRCH);
return (-1);
}
if (anssiz < HFIXEDSZ) {
if (anssiz < (buf2 == NULL ? 1 : 2) * HFIXEDSZ) {
__set_errno (EINVAL);
return (-1);
}
if ((statp->qhook || statp->rhook) && anssiz < MAXPACKET && ansp) {
u_char *buf = malloc (MAXPACKET);
if (buf == NULL)
return (-1);
memcpy (buf, ans, HFIXEDSZ);
*ansp = buf;
ans = buf;
anssiz = MAXPACKET;
#ifdef USE_HOOKS
if (__builtin_expect (statp->qhook || statp->rhook, 0)) {
if (anssiz < MAXPACKET && ansp) {
u_char *buf = malloc (MAXPACKET);
if (buf == NULL)
return (-1);
memcpy (buf, ans, HFIXEDSZ);
*ansp = buf;
ans = buf;
anssiz = MAXPACKET;
}
}
#endif
DprintQ((statp->options & RES_DEBUG) || (statp->pfcode & RES_PRF_QUERY),
(stdout, ";; res_send()\n"), buf, buflen);
v_circuit = (statp->options & RES_USEVC) || buflen > PACKETSZ;
v_circuit = ((statp->options & RES_USEVC)
|| buflen > PACKETSZ
|| buflen2 > PACKETSZ);
gotsomewhere = 0;
terrno = ETIMEDOUT;
@ -442,7 +454,7 @@ __libc_res_nsend(res_state statp, const u_char *buf, int buflen,
* Some resolvers want to even out the load on their nameservers.
* Note that RES_BLAST overrides RES_ROTATE.
*/
if ((statp->options & RES_ROTATE) != 0 &&
if (__builtin_expect ((statp->options & RES_ROTATE) != 0, 0) &&
(statp->options & RES_BLAST) == 0) {
struct sockaddr_in6 *ina;
unsigned int map;
@ -479,8 +491,9 @@ __libc_res_nsend(res_state statp, const u_char *buf, int buflen,
if (nsap == NULL)
goto next_ns;
same_ns:
if (statp->qhook) {
same_ns:
#ifdef USE_HOOKS
if (__builtin_expect (statp->qhook != NULL, 0)) {
int done = 0, loops = 0;
do {
@ -512,6 +525,7 @@ __libc_res_nsend(res_state statp, const u_char *buf, int buflen,
}
} while (!done);
}
#endif
#ifdef DEBUG
char tmpbuf[40];
@ -521,29 +535,34 @@ __libc_res_nsend(res_state statp, const u_char *buf, int buflen,
ns + 1, inet_ntop(AF_INET6, &nsap->sin6_addr,
tmpbuf, sizeof (tmpbuf))));
if (v_circuit) {
if (__builtin_expect (v_circuit, 0)) {
/* Use VC; at most one attempt per server. */
try = statp->retry;
n = send_vc(statp, buf, buflen, &ans, &anssiz, &terrno,
ns, ansp);
n = send_vc(statp, buf, buflen, buf2, buflen2,
&ans, &anssiz, &terrno,
ns, ansp, ansp2, nansp2, &resplen2);
if (n < 0)
return (-1);
if (n == 0)
goto next_ns;
resplen = n;
} else {
/* Use datagrams. */
n = send_dg(statp, buf, buflen, &ans, &anssiz, &terrno,
ns, &v_circuit, &gotsomewhere, ansp);
n = send_dg(statp, buf, buflen, buf2, buflen2,
&ans, &anssiz, &terrno,
ns, &v_circuit, &gotsomewhere, ansp,
ansp2, nansp2, &resplen2);
if (n < 0)
return (-1);
if (n == 0)
goto next_ns;
if (v_circuit)
// XXX Check whether both requests failed or
// XXX whether one have been answered successfully
goto same_ns;
resplen = n;
}
resplen = n;
Dprint((statp->options & RES_DEBUG) ||
((statp->pfcode & RES_PRF_REPLY) &&
(statp->pfcode & RES_PRF_HEAD1)),
@ -553,6 +572,11 @@ __libc_res_nsend(res_state statp, const u_char *buf, int buflen,
(statp->pfcode & RES_PRF_REPLY),
(stdout, "%s", ""),
ans, (resplen > anssiz) ? anssiz : resplen);
if (buf2 != NULL)
DprintQ((statp->options & RES_DEBUG) ||
(statp->pfcode & RES_PRF_REPLY),
(stdout, "%s", ""),
*ansp2, (resplen2 > *nansp2) ? *nansp2 : resplen2);
/*
* If we have temporarily opened a virtual circuit,
@ -563,7 +587,8 @@ __libc_res_nsend(res_state statp, const u_char *buf, int buflen,
(statp->options & RES_STAYOPEN) == 0) {
__res_iclose(statp, false);
}
if (statp->rhook) {
#ifdef USE_HOOKS
if (__builtin_expect (statp->rhook, 0)) {
int done = 0, loops = 0;
do {
@ -593,6 +618,7 @@ __libc_res_nsend(res_state statp, const u_char *buf, int buflen,
} while (!done);
}
#endif
return (resplen);
next_ns: ;
} /*foreach ns*/
@ -612,7 +638,8 @@ int
res_nsend(res_state statp,
const u_char *buf, int buflen, u_char *ans, int anssiz)
{
return __libc_res_nsend(statp, buf, buflen, ans, anssiz, NULL);
return __libc_res_nsend(statp, buf, buflen, NULL, 0, ans, anssiz,
NULL, NULL, NULL);
}
libresolv_hidden_def (res_nsend)
@ -620,17 +647,23 @@ libresolv_hidden_def (res_nsend)
static int
send_vc(res_state statp,
const u_char *buf, int buflen, u_char **ansp, int *anssizp,
int *terrno, int ns, u_char **anscp)
const u_char *buf, int buflen, const u_char *buf2, int buflen2,
u_char **ansp, int *anssizp,
int *terrno, int ns, u_char **anscp, u_char **ansp2, int *anssizp2,
int *resplen2)
{
const HEADER *hp = (HEADER *) buf;
const HEADER *hp2 = (HEADER *) buf2;
u_char *ans = *ansp;
int anssiz = *anssizp;
int orig_anssizp = *anssizp;
// XXX REMOVE
// int anssiz = *anssizp;
HEADER *anhp = (HEADER *) ans;
struct sockaddr_in6 *nsap = EXT(statp).nsaddrs[ns];
int truncating, connreset, resplen, n;
struct iovec iov[2];
struct iovec iov[4];
u_short len;
u_short len2;
u_char *cp;
connreset = 0;
@ -677,11 +710,19 @@ send_vc(res_state statp,
/*
* Send length & message
*/
ns_put16((u_short)buflen, (u_char*)&len);
len = htons ((u_short) buflen);
evConsIovec(&len, INT16SZ, &iov[0]);
evConsIovec((void*)buf, buflen, &iov[1]);
if (TEMP_FAILURE_RETRY (writev(statp->_vcsock, iov, 2))
!= (INT16SZ + buflen)) {
int niov = 2;
ssize_t explen = INT16SZ + buflen;
if (buf2 != NULL) {
len2 = htons ((u_short) buflen2);
evConsIovec(&len2, INT16SZ, &iov[2]);
evConsIovec((void*)buf2, buflen2, &iov[3]);
niov = 4;
explen += INT16SZ + buflen2;
}
if (TEMP_FAILURE_RETRY (writev(statp->_vcsock, iov, niov)) != explen) {
*terrno = errno;
Perror(statp, stderr, "write failed", errno);
__res_iclose(statp, false);
@ -690,6 +731,8 @@ send_vc(res_state statp,
/*
* Receive length & response
*/
int recvresp1 = 0;
int recvresp2 = buf2 == NULL;
read_len:
cp = ans;
len = INT16SZ;
@ -718,30 +761,66 @@ send_vc(res_state statp,
}
return (0);
}
#ifdef _STRING_ARCH_unaligned
resplen = ntohs (*(uint16_t *) ans);
#else
resplen = ns_get16(ans);
if (resplen > anssiz) {
#endif
int *thisanssizp;
u_char **thisansp;
int *thisresplenp;
if ((recvresp1 | recvresp2) == 0 || buf2 == NULL) {
thisanssizp = anssizp;
thisansp = anscp ?: ansp;
assert (anscp != NULL || ansp2 == NULL);
thisresplenp = &resplen;
} else {
if (*anssizp != MAXPACKET) {
/* No buffer allocated for the first
reply. We can try to use the rest
of the user-provided buffer. */
*anssizp2 = orig_anssizp - resplen;
*ansp2 = *ansp + resplen;
} else {
/* The first reply did not fit into the
user-provided buffer. Maybe the second
answer will. */
*anssizp2 = orig_anssizp;
*ansp2 = *ansp;
}
thisanssizp = anssizp2;
thisansp = ansp2;
thisresplenp = resplen2;
}
anhp = (HEADER *) *thisansp;
*thisresplenp = resplen;
if (resplen > *thisanssizp) {
/* Yes, we test ANSCP here. If we have two buffers
both will be allocatable. */
if (anscp) {
ans = malloc (MAXPACKET);
if (ans == NULL) {
u_char *newp = malloc (MAXPACKET);
if (newp == NULL) {
*terrno = ENOMEM;
__res_iclose(statp, false);
return (0);
}
anssiz = MAXPACKET;
*anssizp = MAXPACKET;
*ansp = ans;
*anscp = ans;
anhp = (HEADER *) ans;
*thisanssizp = MAXPACKET;
*thisansp = newp;
anhp = (HEADER *) newp;
len = resplen;
} else {
Dprint(statp->options & RES_DEBUG,
(stdout, ";; response truncated\n")
);
truncating = 1;
len = anssiz;
len = *thisanssizp;
}
} else
len = resplen;
if (len < HFIXEDSZ) {
/*
* Undersized message.
@ -752,7 +831,8 @@ send_vc(res_state statp,
__res_iclose(statp, false);
return (0);
}
cp = ans;
cp = *thisansp;
while (len != 0 && (n = read(statp->_vcsock, (char *)cp, (int)len)) > 0){
cp += n;
len -= n;
@ -768,7 +848,7 @@ send_vc(res_state statp,
* Flush rest of answer so connection stays in synch.
*/
anhp->tc = 1;
len = resplen - anssiz;
len = resplen - *thisanssizp;
while (len != 0) {
char junk[PACKETSZ];
@ -787,14 +867,25 @@ send_vc(res_state statp,
* itself confused, then drop the packet and
* wait for the correct one.
*/
if (hp->id != anhp->id) {
if ((recvresp1 || hp->id != anhp->id)
&& (recvresp2 || hp2->id != anhp->id)) {
DprintQ((statp->options & RES_DEBUG) ||
(statp->pfcode & RES_PRF_REPLY),
(stdout, ";; old answer (unexpected):\n"),
ans, (resplen > anssiz) ? anssiz: resplen);
*thisansp,
(resplen > *thisanssiz) ? *thisanssiz: resplen);
goto read_len;
}
/* Mark which reply we received. */
if (recvresp1 == 0 && hp->id == anhp->id)
recvresp1 = 1;
else
recvresp2 = 1;
/* Repeat waiting if we have a second answer to arrive. */
if ((recvresp1 & recvresp2) == 0)
goto read_len;
/*
* All is well, or the error is fatal. Signal that the
* next nameserver ought not be tried.
@ -804,19 +895,20 @@ send_vc(res_state statp,
static int
send_dg(res_state statp,
const u_char *buf, int buflen, u_char **ansp, int *anssizp,
int *terrno, int ns, int *v_circuit, int *gotsomewhere, u_char **anscp)
const u_char *buf, int buflen, const u_char *buf2, int buflen2,
u_char **ansp, int *anssizp,
int *terrno, int ns, int *v_circuit, int *gotsomewhere, u_char **anscp,
u_char **ansp2, int *anssizp2, int *resplen2)
{
const HEADER *hp = (HEADER *) buf;
const HEADER *hp2 = (HEADER *) buf2;
u_char *ans = *ansp;
int anssiz = *anssizp;
HEADER *anhp = (HEADER *) ans;
int orig_anssizp = *anssizp;
struct sockaddr_in6 *nsap = EXT(statp).nsaddrs[ns];
struct timespec now, timeout, finish;
struct pollfd pfd[1];
int ptimeout;
struct sockaddr_in6 from;
socklen_t fromlen;
int resplen, seconds, n;
if (EXT(statp).nssocks[ns] == -1) {
@ -879,6 +971,8 @@ send_dg(res_state statp,
evAddTime(&finish, &now, &timeout);
int need_recompute = 0;
int nwritten = 0;
int recvresp1 = 0;
int recvresp2 = buf2 == NULL;
pfd[0].fd = EXT(statp).nssocks[ns];
pfd[0].events = POLLOUT;
wait:
@ -918,35 +1012,73 @@ send_dg(res_state statp,
}
__set_errno (0);
if (pfd[0].revents & POLLOUT) {
if (send (pfd[0].fd, buf, buflen, MSG_NOSIGNAL) != buflen) {
ssize_t sr;
if (nwritten != 0)
sr = send (pfd[0].fd, buf2, buflen2, MSG_NOSIGNAL);
else
sr = send (pfd[0].fd, buf, buflen, MSG_NOSIGNAL);
if (sr != buflen) {
if (errno == EINTR || errno == EAGAIN)
goto recompute_resend;
Perror(statp, stderr, "send", errno);
goto err_out;
}
pfd[0].events = POLLIN;
if (nwritten != 0 || buf2 == NULL)
pfd[0].events = POLLIN;
else
pfd[0].events = POLLIN | POLLOUT;
++nwritten;
goto wait;
} else if (pfd[0].revents & POLLIN) {
fromlen = sizeof(struct sockaddr_in6);
if (anssiz < MAXPACKET
int *thisanssizp;
u_char **thisansp;
int *thisresplenp;
if ((recvresp1 | recvresp2) == 0 || buf2 == NULL) {
thisanssizp = anssizp;
thisansp = anscp ?: ansp;
assert (anscp != NULL || ansp2 == NULL);
thisresplenp = &resplen;
} else {
if (*anssizp != MAXPACKET) {
/* No buffer allocated for the first
reply. We can try to use the rest
of the user-provided buffer. */
*anssizp2 = orig_anssizp - resplen;
*ansp2 = *ansp + resplen;
} else {
/* The first reply did not fit into the
user-provided buffer. Maybe the second
answer will. */
*anssizp2 = orig_anssizp;
*ansp2 = *ansp;
}
thisanssizp = anssizp2;
thisansp = ansp2;
thisresplenp = resplen2;
}
if (*thisanssizp < MAXPACKET
/* Yes, we test ANSCP here. If we have two buffers
both will be allocatable. */
&& anscp
&& (ioctl (pfd[0].fd, FIONREAD, &resplen) < 0
|| anssiz < resplen)) {
ans = malloc (MAXPACKET);
if (ans == NULL)
ans = *ansp;
else {
anssiz = MAXPACKET;
&& (ioctl (pfd[0].fd, FIONREAD, thisresplenp) < 0
|| *thisanssizp < *thisresplenp)) {
u_char *newp = malloc (MAXPACKET);
if (newp != NULL) {
*anssizp = MAXPACKET;
*ansp = ans;
*anscp = ans;
anhp = (HEADER *) ans;
*thisansp = ans = newp;
}
}
resplen = recvfrom(pfd[0].fd, (char*)ans, anssiz,0,
(struct sockaddr *)&from, &fromlen);
if (resplen <= 0) {
HEADER *anhp = (HEADER *) *thisansp;
socklen_t fromlen = sizeof(struct sockaddr_in6);
assert (sizeof(from) <= fromlen);
*thisresplenp = recvfrom(pfd[0].fd, (char*)*thisansp,
*thisanssizp, 0,
(struct sockaddr *)&from, &fromlen);
if (*thisresplenp <= 0) {
if (errno == EINTR || errno == EAGAIN) {
need_recompute = 1;
goto wait;
@ -955,17 +1087,18 @@ send_dg(res_state statp,
goto err_out;
}
*gotsomewhere = 1;
if (resplen < HFIXEDSZ) {
if (*thisresplenp < HFIXEDSZ) {
/*
* Undersized message.
*/
Dprint(statp->options & RES_DEBUG,
(stdout, ";; undersized: %d\n",
resplen));
*thisresplen));
*terrno = EMSGSIZE;
goto err_out;
}
if (hp->id != anhp->id) {
if ((recvresp1 || hp->id != anhp->id)
&& (recvresp2 || hp2->id != anhp->id)) {
/*
* response from old query, ignore it.
* XXX - potential security hazard could
@ -974,7 +1107,9 @@ send_dg(res_state statp,
DprintQ((statp->options & RES_DEBUG) ||
(statp->pfcode & RES_PRF_REPLY),
(stdout, ";; old answer:\n"),
ans, (resplen > anssiz) ? anssiz : resplen);
thisansp,
(*thisresplen > *thisanssiz)
? *thisanssiz : *thisresplen);
goto wait;
}
if (!(statp->options & RES_INSECURE1) &&
@ -987,14 +1122,16 @@ send_dg(res_state statp,
DprintQ((statp->options & RES_DEBUG) ||
(statp->pfcode & RES_PRF_REPLY),
(stdout, ";; not our server:\n"),
ans, (resplen > anssiz) ? anssiz : resplen);
thisansp,
(*thisresplen > *thisanssiz)
? *thisanssiz : *thisresplen);
goto wait;
}
#ifdef RES_USE_EDNS0
if (anhp->rcode == FORMERR
&& (statp->options & RES_USE_EDNS0) != 0U) {
/*
* Do not retry if the server do not understand
* Do not retry if the server does not understand
* EDNS0. The case has to be captured here, as
* FORMERR packet do not carry query section, hence
* res_queriesmatch() returns 0.
@ -1002,15 +1139,23 @@ send_dg(res_state statp,
DprintQ(statp->options & RES_DEBUG,
(stdout,
"server rejected query with EDNS0:\n"),
ans, (resplen > anssiz) ? anssiz : resplen);
thisans,
(*thisresplen > *thisanssiz)
? *thisanssiz : *thisresplen);
/* record the error */
statp->_flags |= RES_F_EDNS0ERR;
goto err_out;
}
#endif
if (!(statp->options & RES_INSECURE2) &&
!res_queriesmatch(buf, buf + buflen,
ans, ans + anssiz)) {
if (!(statp->options & RES_INSECURE2)
&& (recvresp1 || !res_queriesmatch(buf, buf + buflen,
*thisansp,
*thisansp
+ *thisanssizp))
&& (recvresp2 || !res_queriesmatch(buf2, buf2 + buflen2,
*thisansp,
*thisansp
+ *thisanssizp))) {
/*
* response contains wrong query? ignore it.
* XXX - potential security hazard could
@ -1019,7 +1164,9 @@ send_dg(res_state statp,
DprintQ((statp->options & RES_DEBUG) ||
(statp->pfcode & RES_PRF_REPLY),
(stdout, ";; wrong query name:\n"),
ans, (resplen > anssiz) ? anssiz : resplen);
thisansp,
(*thisresplen > *thisanssiz)
? *thisanssiz : *thisresplen);
goto wait;
}
if (anhp->rcode == SERVFAIL ||
@ -1027,7 +1174,9 @@ send_dg(res_state statp,
anhp->rcode == REFUSED) {
DprintQ(statp->options & RES_DEBUG,
(stdout, "server rejected query:\n"),
ans, (resplen > anssiz) ? anssiz : resplen);
thisansp,
(*thisresplen > *thisanssiz)
? *thisanssiz : *thisresplen);
next_ns:
__res_iclose(statp, false);
/* don't retry if called from dig */
@ -1038,7 +1187,9 @@ send_dg(res_state statp,
&& anhp->aa == 0 && anhp->ra == 0 && anhp->arcount == 0) {
DprintQ(statp->options & RES_DEBUG,
(stdout, "referred query:\n"),
ans, (resplen > anssiz) ? anssiz : resplen);
thisansp,
(*thisresplen > *thisanssiz)
? *thisanssiz : *thisresplen);
goto next_ns;
}
if (!(statp->options & RES_IGNTC) && anhp->tc) {
@ -1050,8 +1201,18 @@ send_dg(res_state statp,
(stdout, ";; truncated answer\n"));
*v_circuit = 1;
__res_iclose(statp, false);
// XXX if we have received one reply we could
// XXX use it and not repeat it over TCP...
return (1);
}
/* Mark which reply we received. */
if (recvresp1 == 0 && hp->id == anhp->id)
recvresp1 = 1;
else
recvresp2 = 1;
/* Repeat waiting if we have a second answer to arrive. */
if ((recvresp1 & recvresp2) == 0)
goto wait;
/*
* All is well, or the error is fatal. Signal that the
* next nameserver ought not be tried.