aboutsummaryrefslogtreecommitdiff
path: root/resolv/res_send.c
diff options
context:
space:
mode:
Diffstat (limited to 'resolv/res_send.c')
-rw-r--r--resolv/res_send.c1069
1 files changed, 531 insertions, 538 deletions
diff --git a/resolv/res_send.c b/resolv/res_send.c
index d3dc2ba..bf500b1 100644
--- a/resolv/res_send.c
+++ b/resolv/res_send.c
@@ -1,7 +1,7 @@
/*
* Copyright (c) 1985, 1989, 1993
* 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 the following conditions
* are met:
@@ -13,7 +13,7 @@
* 4. 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 BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
@@ -29,14 +29,14 @@
/*
* Portions Copyright (c) 1993 by Digital Equipment Corporation.
- *
+ *
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies, and that
* the name of Digital Equipment Corporation not be used in advertising or
* publicity pertaining to distribution of the document or software without
* specific, written prior permission.
- *
+ *
* THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL
* WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT
@@ -66,7 +66,7 @@
#if defined(LIBC_SCCS) && !defined(lint)
static const char sccsid[] = "@(#)res_send.c 8.1 (Berkeley) 6/4/93";
-static const char rcsid[] = "$Id$";
+static const char rcsid[] = "$BINDId: res_send.c,v 8.38 2000/03/30 20:16:51 vixie Exp $";
#endif /* LIBC_SCCS and not lint */
/*
@@ -78,6 +78,9 @@ static const char rcsid[] = "$Id$";
#include <sys/time.h>
#include <sys/socket.h>
#include <sys/uio.h>
+#ifdef _LIBC
+#include <sys/poll.h>
+#endif
#include <netinet/in.h>
#include <arpa/nameser.h>
@@ -92,32 +95,36 @@ static const char rcsid[] = "$Id$";
#include <string.h>
#include <unistd.h>
-#include <sys/poll.h>
+#ifndef _LIBC
+#include <isc/eventlib.h>
+#else
-/* Options. Leave them on. */
-/* #undef DEBUG */
-#include "res_debug.h"
+/* From ev_streams.c. */
+
+static inline struct iovec
+evConsIovec(void *buf, size_t cnt) {
+ struct iovec ret;
-#ifdef NEED_PSELECT
-static int pselect(int, void *, void *, void *,
- struct timespec *,
- const sigset_t *);
-#endif
+ memset(&ret, 0xf5, sizeof ret);
+ ret.iov_base = buf;
+ ret.iov_len = cnt;
+ return (ret);
+}
-#define CHECK_SRVR_ADDR
+/* From ev_timers.c. */
-/* From bind lib/isc/ev_timers.c: */
#define BILLION 1000000000
-static struct timespec
+
+static inline struct timespec
evTimeSpec(struct timeval tv) {
- struct timespec ts;
+ struct timespec ts;
- ts.tv_sec = tv.tv_sec;
- ts.tv_nsec = tv.tv_usec * 1000;
- return (ts);
+ ts.tv_sec = tv.tv_sec;
+ ts.tv_nsec = tv.tv_usec * 1000;
+ return (ts);
}
-static struct timespec
+static inline struct timespec
evConsTime(time_t sec, long nsec) {
struct timespec x;
@@ -126,7 +133,7 @@ evConsTime(time_t sec, long nsec) {
return (x);
}
-static struct timespec
+static inline struct timespec
evAddTime(struct timespec addend1, struct timespec addend2) {
struct timespec x;
@@ -139,7 +146,7 @@ evAddTime(struct timespec addend1, struct timespec addend2) {
return (x);
}
-static struct timespec
+static inline struct timespec
evSubTime(struct timespec minuend, struct timespec subtrahend) {
struct timespec x;
@@ -153,7 +160,7 @@ evSubTime(struct timespec minuend, struct timespec subtrahend) {
return (x);
}
-static int
+static inline int
evCmpTime(struct timespec a, struct timespec b) {
long x = a.tv_sec - b.tv_sec;
@@ -162,7 +169,7 @@ evCmpTime(struct timespec a, struct timespec b) {
return (x < 0L ? (-1) : x > 0L ? (1) : (0));
}
-static struct timespec
+static inline struct timespec
evNowTime() {
struct timeval now;
@@ -170,41 +177,44 @@ evNowTime() {
return (evConsTime(0, 0));
return (evTimeSpec(now));
}
-/* End of code from bind lib/isc/ev_timers.c. */
-#ifdef DEBUG
- static void
- Aerror(const res_state statp, FILE *file, const char *string, int error,
- struct sockaddr_in address)
- {
- int save = errno;
+#endif
- if ((statp->options & RES_DEBUG) != 0) {
- char tmp[sizeof "255.255.255.255"];
+/* Options. Leave them on. */
+/* #undef DEBUG */
+#include "res_debug.h"
- fprintf(file, "res_send: %s ([%s].%u): %s\n",
- string,
- inet_ntop(address.sin_family, &address.sin_addr,
- tmp, sizeof tmp),
- ntohs(address.sin_port),
- strerror(error));
- }
- __set_errno (save);
- }
- static void
- Perror(const res_state statp, FILE *file, const char *string, int error) {
- int save = errno;
+#define EXT(res) ((res)->_u._ext)
- if ((statp->options & RES_DEBUG) != 0)
- fprintf(file, "res_send: %s: %s\n",
- string, strerror(error));
- __set_errno (save);
- }
+#ifndef _LIBC
+static const int highestFD = FD_SETSIZE - 1;
#endif
-static int cmpsock(struct sockaddr_in *a1, struct sockaddr_in *a2);
+/* Forward. */
+
+static int send_vc(res_state, const u_char *, int,
+ u_char *, int, int *, int);
+static int send_dg(res_state, const u_char *, int,
+ u_char *, int, int *, int,
+ int *, int *);
+#ifdef DEBUG
+static void Aerror(const res_state, FILE *, const char *, int,
+ struct sockaddr_in);
+static void Perror(const res_state, FILE *, const char *, int);
+#endif
+static int sock_eq(struct sockaddr_in *, struct sockaddr_in *);
+#ifdef NEED_PSELECT
+static int pselect(int, void *, void *, void *,
+ struct timespec *,
+ const sigset_t *);
+#endif
+
+/* Reachover. */
+
void res_pquery(const res_state, const u_char *, int, FILE *);
+/* Public. */
+
/* int
* res_isourserver(ina)
* looks up "ina" in _res.ns_addr_list[]
@@ -220,7 +230,7 @@ res_ourserver_p(const res_state statp, const struct sockaddr_in *inp) {
int ns;
ina = *inp;
- for (ns = 0; ns < statp->nscount; ns++) {
+ for (ns = 0; ns < statp->nscount; ns++) {
const struct sockaddr_in *srv = &statp->nsaddr_list[ns];
if (srv->sin_family == ina.sin_family &&
@@ -295,8 +305,8 @@ res_queriesmatch(const u_char *buf1, const u_char *eom1,
* Only header section present in replies to
* dynamic update packets.
*/
- if ( (((HEADER *)buf1)->opcode == ns_o_update) &&
- (((HEADER *)buf2)->opcode == ns_o_update) )
+ if ((((HEADER *)buf1)->opcode == ns_o_update) &&
+ (((HEADER *)buf2)->opcode == ns_o_update))
return (1);
if (qdcount != ntohs(((HEADER*)buf2)->qdcount))
@@ -323,12 +333,12 @@ int
res_nsend(res_state statp,
const u_char *buf, int buflen, u_char *ans, int anssiz)
{
- HEADER *hp = (HEADER *) buf;
- HEADER *anhp = (HEADER *) ans;
- int gotsomewhere, connreset, terrno, try, v_circuit, resplen, ns, n;
- u_int badns; /* XXX NSMAX can't exceed #/bits in this variable */
- static int highestFD = FD_SETSIZE - 1;
+ int gotsomewhere, terrno, try, v_circuit, resplen, ns, n;
+ if (statp->nscount == 0) {
+ __set_errno (ESRCH);
+ return (-1);
+ }
if (anssiz < HFIXEDSZ) {
__set_errno (EINVAL);
return (-1);
@@ -337,14 +347,46 @@ res_nsend(res_state statp,
(stdout, ";; res_send()\n"), buf, buflen);
v_circuit = (statp->options & RES_USEVC) || buflen > PACKETSZ;
gotsomewhere = 0;
- connreset = 0;
terrno = ETIMEDOUT;
- badns = 0;
/*
- * Some callers want to even out the load on their resolver list.
+ * If the ns_addr_list in the resolver context has changed, then
+ * invalidate our cached copy and the associated timing data.
*/
- if (statp->nscount > 0 && (statp->options & RES_ROTATE) != 0) {
+ if (EXT(statp).nscount != 0) {
+ int needclose = 0;
+
+ if (EXT(statp).nscount != statp->nscount)
+ needclose++;
+ else
+ for (ns = 0; ns < statp->nscount; ns++)
+ if (!sock_eq(&statp->nsaddr_list[ns],
+ &EXT(statp).nsaddrs[ns])) {
+ needclose++;
+ break;
+ }
+ if (needclose)
+ res_nclose(statp);
+ }
+
+ /*
+ * Maybe initialize our private copy of the ns_addr_list.
+ */
+ if (EXT(statp).nscount == 0) {
+ for (ns = 0; ns < statp->nscount; ns++) {
+ EXT(statp).nsaddrs[ns] = statp->nsaddr_list[ns];
+ EXT(statp).nstimes[ns] = RES_MAXTIME;
+ EXT(statp).nssocks[ns] = -1;
+ }
+ EXT(statp).nscount = statp->nscount;
+ }
+
+ /*
+ * Some resolvers want to even out the load on their nameservers.
+ * Note that RES_BLAST overrides RES_ROTATE.
+ */
+ if ((statp->options & RES_ROTATE) != 0 &&
+ (statp->options & RES_BLAST) == 0) {
struct sockaddr_in ina;
int lastns = statp->nscount - 1;
@@ -355,17 +397,12 @@ res_nsend(res_state statp,
}
/*
- * Send request, RETRY times, or until successful
+ * Send request, RETRY times, or until successful.
*/
for (try = 0; try < statp->retry; try++) {
for (ns = 0; ns < statp->nscount; ns++) {
struct sockaddr_in *nsap = &statp->nsaddr_list[ns];
same_ns:
- if (badns & (1 << ns)) {
- res_nclose(statp);
- goto next_ns;
- }
-
if (statp->qhook) {
int done = 0, loops = 0;
@@ -401,474 +438,45 @@ res_nsend(res_state statp,
ns + 1, inet_ntoa(nsap->sin_addr)));
if (v_circuit) {
- int truncated;
- struct iovec iov[2];
- u_short len;
- u_char *cp;
-
/* Use VC; at most one attempt per server. */
try = statp->retry;
- truncated = 0;
-
- /* Are we still talking to whom we want to talk to? */
- if (statp->_sock >= 0 &&
- (statp->_flags & RES_F_VC) != 0) {
- struct sockaddr_in peer;
- int size = sizeof(peer);
-
- if (getpeername(statp->_sock,
- (struct sockaddr *)&peer,
- &size) < 0) {
- res_nclose(statp);
- statp->_flags &= ~RES_F_VC;
- } else if (!cmpsock(&peer, nsap)) {
- res_nclose(statp);
- statp->_flags &= ~RES_F_VC;
- }
- }
-
- if (statp->_sock < 0 ||
- (statp->_flags & RES_F_VC) == 0) {
- if (statp->_sock >= 0)
- res_nclose(statp);
-
- statp->_sock = socket(PF_INET,
- SOCK_STREAM, 0);
- if (statp->_sock < 0 ||
- statp->_sock > highestFD) {
- terrno = errno;
- Perror(statp, stderr,
- "socket(vc)", errno);
- return (-1);
- }
- __set_errno (0);
- if (connect(statp->_sock,
- (struct sockaddr *)nsap,
- sizeof *nsap) < 0) {
- terrno = errno;
- Aerror(statp, stderr, "connect/vc",
- errno, *nsap);
- badns |= (1 << ns);
- res_nclose(statp);
- goto next_ns;
- }
- statp->_flags |= RES_F_VC;
- }
- /*
- * Send length & message
- */
- putshort((u_short)buflen, (u_char*)&len);
- iov[0].iov_base = (caddr_t)&len;
- iov[0].iov_len = INT16SZ;
- iov[1].iov_base = (caddr_t)buf;
- iov[1].iov_len = buflen;
- if (writev(statp->_sock, iov, 2) !=
- (INT16SZ + buflen)) {
- terrno = errno;
- Perror(statp, stderr, "write failed", errno);
- badns |= (1 << ns);
- res_nclose(statp);
+ n = send_vc(statp, buf, buflen, ans, anssiz, &terrno,
+ ns);
+ if (n < 0)
+ return (-1);
+ if (n == 0)
goto next_ns;
- }
- /*
- * Receive length & response
- */
- read_len:
- cp = ans;
- len = INT16SZ;
- while ((n = read(statp->_sock,
- (char *)cp, (int)len)) > 0) {
- cp += n;
- if ((len -= n) <= 0)
- break;
- }
- if (n <= 0) {
- terrno = errno;
- Perror(statp, stderr, "read failed", errno);
- res_nclose(statp);
- /*
- * A long running process might get its TCP
- * connection reset if the remote server was
- * restarted. Requery the server instead of
- * trying a new one. When there is only one
- * server, this means that a query might work
- * instead of failing. We only allow one reset
- * per query to prevent looping.
- */
- if (terrno == ECONNRESET && !connreset) {
- connreset = 1;
- res_nclose(statp);
- goto same_ns;
- }
- res_nclose(statp);
- goto next_ns;
- }
- resplen = ns_get16(ans);
- if (resplen > anssiz) {
- Dprint(statp->options & RES_DEBUG,
- (stdout, ";; response truncated\n")
- );
- truncated = 1;
- len = anssiz;
- } else
- len = resplen;
- if (len < HFIXEDSZ) {
- /*
- * Undersized message.
- */
- Dprint(statp->options & RES_DEBUG,
- (stdout, ";; undersized: %d\n", len));
- terrno = EMSGSIZE;
- badns |= (1 << ns);
- res_nclose(statp);
- goto next_ns;
- }
- cp = ans;
- while (len != 0 &&
- (n = read(statp->_sock, (char *)cp, (int)len))
- > 0) {
- cp += n;
- len -= n;
- }
- if (n <= 0) {
- terrno = errno;
- Perror(statp, stderr, "read(vc)", errno);
- res_nclose(statp);
- goto next_ns;
- }
- if (truncated) {
- /*
- * Flush rest of answer
- * so connection stays in synch.
- */
- anhp->tc = 1;
- len = resplen - anssiz;
- while (len != 0) {
- char junk[PACKETSZ];
-
- n = ((size_t)len > sizeof(junk)
- ? sizeof(junk)
- : len);
- n = read(statp->_sock, junk, n);
- if (n > 0)
- len -= n;
- else
- break;
- }
- }
- /*
- * The calling applicating has bailed out of
- * a previous call and failed to arrange to have
- * the circuit closed or the server has got
- * itself confused. Anyway drop the packet and
- * wait for the correct one.
- */
- if (hp->id != anhp->id) {
- DprintQ((statp->options & RES_DEBUG) ||
- (statp->pfcode & RES_PRF_REPLY),
- (stdout, ";; old answer (unexpected):\n"),
- ans, (resplen>anssiz)?anssiz:resplen);
- goto read_len;
- }
+ resplen = n;
} else {
- /*
- * Use datagrams.
- */
- struct timespec start, timeout, finish;
-#ifdef _LIBC
- struct pollfd pfd[1];
- int ptimeout;
-#else
- fd_set dsmask;
-#endif
- struct sockaddr_in from;
- int fromlen, seconds;
-
- if (statp->_sock < 0 ||
- (statp->_flags & RES_F_VC) != 0) {
- if ((statp->_flags & RES_F_VC) != 0)
- res_nclose(statp);
- statp->_sock = socket(PF_INET, SOCK_DGRAM, 0);
- if (statp->_sock < 0 ||
- statp->_sock > highestFD) {
-#ifndef CAN_RECONNECT
- bad_dg_sock:
-#endif
- terrno = errno;
- Perror(statp, stderr,
- "socket(dg)", errno);
- return (-1);
- }
- statp->_flags &= ~RES_F_CONN;
- }
-#ifndef CANNOT_CONNECT_DGRAM
- /*
- * On a 4.3BSD+ machine (client and server,
- * actually), sending to a nameserver datagram
- * port with no nameserver will cause an
- * ICMP port unreachable message to be returned.
- * If our datagram socket is "connected" to the
- * server, we get an ECONNREFUSED error on the next
- * socket operation, and select returns if the
- * error message is received. We can thus detect
- * the absence of a nameserver without timing out.
- * If we have sent queries to at least two servers,
- * however, we don't want to remain connected,
- * as we wish to receive answers from the first
- * server to respond.
- */
- if (statp->nscount == 1 || (try == 0 && ns == 0)) {
- /*
- * Connect only if we are sure we won't
- * receive a response from another server.
- */
- if ((statp->_flags & RES_F_CONN) == 0) {
- if (connect(statp->_sock,
- (struct sockaddr *)nsap,
- sizeof *nsap) < 0) {
- Aerror(statp, stderr,
- "connect(dg)",
- errno, *nsap);
- badns |= (1 << ns);
- res_nclose(statp);
- goto next_ns;
- }
- statp->_flags |= RES_F_CONN;
- }
- if (send(statp->_sock, (char*)buf, buflen, 0)
- != buflen) {
- Perror(statp, stderr, "send", errno);
- badns |= (1 << ns);
- res_nclose(statp);
- goto next_ns;
- }
- } else {
- /*
- * Disconnect if we want to listen
- * for responses from more than one server.
- */
- if ((statp->_flags & RES_F_CONN) != 0) {
-#ifdef CAN_RECONNECT
- struct sockaddr_in no_addr;
-
- no_addr.sin_family = AF_INET;
- no_addr.sin_addr.s_addr = INADDR_ANY;
- no_addr.sin_port = 0;
- (void) connect(statp->_sock,
- (struct sockaddr *)
- &no_addr,
- sizeof no_addr);
-#else
- struct sockaddr_in local_addr;
- int len, result, s1;
-
- len = sizeof(local_addr);
- s1 = socket(PF_INET, SOCK_DGRAM, 0);
- result = getsockname(statp->_sock,
- (struct sockaddr *)&local_addr,
- &len);
- if (s1 < 0)
- goto bad_dg_sock;
- (void) dup2(s1, statp->_sock);
- (void) close(s1);
- if (result == 0) {
- /*
- * Attempt to rebind to old
- * port. Note connected socket
- * has an sin_addr set.
- */
- local_addr.sin_addr.s_addr =
- htonl(0);
- (void)bind(statp->_sock,
- (struct sockaddr *)
- &local_addr, len);
- }
- Dprint(statp->options & RES_DEBUG,
- (stdout, ";; new DG socket\n"))
-#endif /* CAN_RECONNECT */
- statp->_flags &= ~RES_F_CONN;
- __set_errno (0);
- }
-#endif /* !CANNOT_CONNECT_DGRAM */
- if (sendto(statp->_sock,
- (char*)buf, buflen, 0,
- (struct sockaddr *)nsap,
- sizeof *nsap)
- != buflen) {
- Aerror(statp, stderr, "sendto", errno, *nsap);
- badns |= (1 << ns);
- res_nclose(statp);
- goto next_ns;
- }
-#ifndef CANNOT_CONNECT_DGRAM
- }
-#endif /* !CANNOT_CONNECT_DGRAM */
-
- if (statp->_sock < 0 || statp->_sock > highestFD) {
- Perror(statp, stderr,
- "fd out-of-bounds", EMFILE);
- res_nclose(statp);
+ /* Use datagrams. */
+ n = send_dg(statp, buf, buflen, ans, anssiz, &terrno,
+ ns, &v_circuit, &gotsomewhere);
+ if (n < 0)
+ return (-1);
+ if (n == 0)
goto next_ns;
- }
-
- /*
- * Wait for reply
- */
- seconds = (statp->retrans << try);
- if (try > 0)
- seconds /= statp->nscount;
- if (seconds <= 0)
- seconds = 1;
-
- start = evNowTime();
- timeout = evConsTime(seconds, 0);
- finish = evAddTime(start, timeout);
- wait:
-#ifdef _LIBC
- /* Convert struct timespec in milliseconds. */
- ptimeout = timeout.tv_sec * 1000
- + timeout.tv_nsec / 1000000;
-
- pfd[0].fd = statp->_sock;
- pfd[0].events = POLLIN;
- n = __poll (pfd, 1, ptimeout);
-#else
- FD_ZERO(&dsmask);
- FD_SET(statp->_sock, &dsmask);
- n = pselect(statp->_sock + 1,
- &dsmask, NULL, NULL,
- &timeout, NULL);
-#endif
- if (n == 0) {
- Dprint(statp->options & RES_DEBUG,
- (stdout, ";; timeout\n"));
- gotsomewhere = 1;
- goto next_ns;
- }
- if (n < 0) {
- if (errno == EINTR) {
- struct timespec now;
-
- now = evNowTime();
- if (evCmpTime(finish, now) >= 0) {
- timeout = evSubTime(finish,
- now);
- goto wait;
- }
- }
-#ifdef _LIBC
- Perror(statp, stderr, "poll", errno);
-#else
- Perror(statp, stderr, "select", errno);
-#endif
- res_nclose(statp);
- goto next_ns;
- }
- __set_errno (0);
- fromlen = sizeof(struct sockaddr_in);
- resplen = recvfrom(statp->_sock, (char*)ans, anssiz,0,
- (struct sockaddr *)&from, &fromlen);
- if (resplen <= 0) {
- Perror(statp, stderr, "recvfrom", errno);
- res_nclose(statp);
- goto next_ns;
- }
- gotsomewhere = 1;
- if (resplen < HFIXEDSZ) {
- /*
- * Undersized message.
- */
- Dprint(statp->options & RES_DEBUG,
- (stdout, ";; undersized: %d\n",
- resplen));
- terrno = EMSGSIZE;
- badns |= (1 << ns);
- res_nclose(statp);
- goto next_ns;
- }
- if (hp->id != anhp->id) {
- /*
- * response from old query, ignore it.
- * XXX - potential security hazard could
- * be detected here.
- */
- DprintQ((statp->options & RES_DEBUG) ||
- (statp->pfcode & RES_PRF_REPLY),
- (stdout, ";; old answer:\n"),
- ans, (resplen>anssiz)?anssiz:resplen);
- goto wait;
- }
-#ifdef CHECK_SRVR_ADDR
- if (!(statp->options & RES_INSECURE1) &&
- !res_ourserver_p(statp, &from)) {
- /*
- * response from wrong server? ignore it.
- * XXX - potential security hazard could
- * be detected here.
- */
- DprintQ((statp->options & RES_DEBUG) ||
- (statp->pfcode & RES_PRF_REPLY),
- (stdout, ";; not our server:\n"),
- ans, (resplen>anssiz)?anssiz:resplen);
- goto wait;
- }
-#endif
- if (!(statp->options & RES_INSECURE2) &&
- !res_queriesmatch(buf, buf + buflen,
- ans, ans + anssiz)) {
- /*
- * response contains wrong query? ignore it.
- * XXX - potential security hazard could
- * be detected here.
- */
- DprintQ((statp->options & RES_DEBUG) ||
- (statp->pfcode & RES_PRF_REPLY),
- (stdout, ";; wrong query name:\n"),
- ans, (resplen>anssiz)?anssiz:resplen);
- goto wait;
- }
- if (anhp->rcode == SERVFAIL ||
- anhp->rcode == NOTIMP ||
- anhp->rcode == REFUSED) {
- DprintQ(statp->options & RES_DEBUG,
- (stdout, "server rejected query:\n"),
- ans, (resplen>anssiz)?anssiz:resplen);
- badns |= (1 << ns);
- res_nclose(statp);
- /* don't retry if called from dig */
- if (!statp->pfcode)
- goto next_ns;
- }
- if (!(statp->options & RES_IGNTC) && anhp->tc) {
- /*
- * get rest of answer;
- * use TCP with same server.
- */
- Dprint(statp->options & RES_DEBUG,
- (stdout, ";; truncated answer\n"));
- v_circuit = 1;
- res_nclose(statp);
+ if (v_circuit)
goto same_ns;
- }
- } /*if vc/dg*/
+ resplen = n;
+ }
+
Dprint((statp->options & RES_DEBUG) ||
((statp->pfcode & RES_PRF_REPLY) &&
(statp->pfcode & RES_PRF_HEAD1)),
(stdout, ";; got answer:\n"));
+
DprintQ((statp->options & RES_DEBUG) ||
(statp->pfcode & RES_PRF_REPLY),
(stdout, ""),
- ans, (resplen>anssiz)?anssiz:resplen);
+ ans, (resplen > anssiz) ? anssiz : resplen);
+
/*
- * If using virtual circuits, we assume that the first server
- * is preferred over the rest (i.e. it is on the local
- * machine) and only keep that one open.
* If we have temporarily opened a virtual circuit,
* or if we haven't been asked to keep a socket open,
* close the socket.
*/
- if ((v_circuit && (!(statp->options & RES_USEVC) || ns != 0)) ||
- !(statp->options & RES_STAYOPEN)) {
+ if ((v_circuit && (statp->options & RES_USEVC) == 0) ||
+ (statp->options & RES_STAYOPEN) == 0) {
res_nclose(statp);
}
if (statp->rhook) {
@@ -915,25 +523,411 @@ res_nsend(res_state statp,
return (-1);
}
-/*
- * This routine is for closing the socket if a virtual circuit is used and
- * the program wants to close it. This provides support for endhostent()
- * which expects to close the socket.
- *
- * This routine is not expected to be user visible.
- */
-void
-res_nclose(res_state statp) {
- if (statp->_sock >= 0) {
- (void) close(statp->_sock);
- statp->_sock = -1;
- statp->_flags &= ~(RES_F_VC | RES_F_CONN);
+/* Private */
+
+static int
+send_vc(res_state statp,
+ const u_char *buf, int buflen, u_char *ans, int anssiz,
+ int *terrno, int ns)
+{
+ const HEADER *hp = (HEADER *) buf;
+ HEADER *anhp = (HEADER *) ans;
+ struct sockaddr_in *nsap = &statp->nsaddr_list[ns];
+ int truncating, connreset, resplen, n;
+ struct iovec iov[2];
+ u_short len;
+ u_char *cp;
+
+ connreset = 0;
+ same_ns:
+ truncating = 0;
+
+ /* Are we still talking to whom we want to talk to? */
+ if (statp->_vcsock >= 0 && (statp->_flags & RES_F_VC) != 0) {
+ struct sockaddr_in peer;
+ int size = sizeof peer;
+
+ if (getpeername(statp->_vcsock,
+ (struct sockaddr *)&peer, &size) < 0 ||
+ !sock_eq(&peer, nsap)) {
+ res_nclose(statp);
+ statp->_flags &= ~RES_F_VC;
+ }
+ }
+
+ if (statp->_vcsock < 0 || (statp->_flags & RES_F_VC) == 0) {
+ if (statp->_vcsock >= 0)
+ res_nclose(statp);
+
+ statp->_vcsock = socket(PF_INET, SOCK_STREAM, 0);
+#ifndef _LIBC
+ if (statp->_vcsock > highestFD) {
+ res_nclose(statp);
+ __set_errno (ENOTSOCK);
+ }
+#endif
+ if (statp->_vcsock < 0) {
+ *terrno = errno;
+ Perror(statp, stderr, "socket(vc)", errno);
+ return (-1);
+ }
+ __set_errno (0);
+ if (connect(statp->_vcsock, (struct sockaddr *)nsap,
+ sizeof *nsap) < 0) {
+ *terrno = errno;
+ Aerror(statp, stderr, "connect/vc", errno, *nsap);
+ res_nclose(statp);
+ return (0);
+ }
+ statp->_flags |= RES_F_VC;
}
+
+ /*
+ * Send length & message
+ */
+ putshort((u_short)buflen, (u_char*)&len);
+ iov[0] = evConsIovec(&len, INT16SZ);
+ iov[1] = evConsIovec((void*)buf, buflen);
+ if (writev(statp->_vcsock, iov, 2) != (INT16SZ + buflen)) {
+ *terrno = errno;
+ Perror(statp, stderr, "write failed", errno);
+ res_nclose(statp);
+ return (0);
+ }
+ /*
+ * Receive length & response
+ */
+ read_len:
+ cp = ans;
+ len = INT16SZ;
+ while ((n = read(statp->_vcsock, (char *)cp, (int)len)) > 0) {
+ cp += n;
+ if ((len -= n) <= 0)
+ break;
+ }
+ if (n <= 0) {
+ *terrno = errno;
+ Perror(statp, stderr, "read failed", errno);
+ res_nclose(statp);
+ /*
+ * A long running process might get its TCP
+ * connection reset if the remote server was
+ * restarted. Requery the server instead of
+ * trying a new one. When there is only one
+ * server, this means that a query might work
+ * instead of failing. We only allow one reset
+ * per query to prevent looping.
+ */
+ if (*terrno == ECONNRESET && !connreset) {
+ connreset = 1;
+ res_nclose(statp);
+ goto same_ns;
+ }
+ res_nclose(statp);
+ return (0);
+ }
+ resplen = ns_get16(ans);
+ if (resplen > anssiz) {
+ Dprint(statp->options & RES_DEBUG,
+ (stdout, ";; response truncated\n")
+ );
+ truncating = 1;
+ len = anssiz;
+ } else
+ len = resplen;
+ if (len < HFIXEDSZ) {
+ /*
+ * Undersized message.
+ */
+ Dprint(statp->options & RES_DEBUG,
+ (stdout, ";; undersized: %d\n", len));
+ *terrno = EMSGSIZE;
+ res_nclose(statp);
+ return (0);
+ }
+ cp = ans;
+ while (len != 0 && (n = read(statp->_vcsock, (char *)cp, (int)len)) > 0){
+ cp += n;
+ len -= n;
+ }
+ if (n <= 0) {
+ *terrno = errno;
+ Perror(statp, stderr, "read(vc)", errno);
+ res_nclose(statp);
+ return (0);
+ }
+ if (truncating) {
+ /*
+ * Flush rest of answer so connection stays in synch.
+ */
+ anhp->tc = 1;
+ len = resplen - anssiz;
+ while (len != 0) {
+ char junk[PACKETSZ];
+
+ n = read(statp->_vcsock, junk,
+ (len > sizeof junk) ? sizeof junk : len);
+ if (n > 0)
+ len -= n;
+ else
+ break;
+ }
+ }
+ /*
+ * If the calling applicating has bailed out of
+ * a previous call and failed to arrange to have
+ * the circuit closed or the server has got
+ * itself confused, then drop the packet and
+ * wait for the correct one.
+ */
+ if (hp->id != anhp->id) {
+ DprintQ((statp->options & RES_DEBUG) ||
+ (statp->pfcode & RES_PRF_REPLY),
+ (stdout, ";; old answer (unexpected):\n"),
+ ans, (resplen > anssiz) ? anssiz: resplen);
+ goto read_len;
+ }
+
+ /*
+ * All is well, or the error is fatal. Signal that the
+ * next nameserver ought not be tried.
+ */
+ return (resplen);
}
-/* Private */
static int
-cmpsock(struct sockaddr_in *a1, struct sockaddr_in *a2) {
+send_dg(res_state statp,
+ const u_char *buf, int buflen, u_char *ans, int anssiz,
+ int *terrno, int ns, int *v_circuit, int *gotsomewhere)
+{
+ const HEADER *hp = (HEADER *) buf;
+ HEADER *anhp = (HEADER *) ans;
+ const struct sockaddr_in *nsap = &statp->nsaddr_list[ns];
+ struct timespec now, timeout, finish;
+#ifdef _LIBC
+ struct pollfd pfd[1];
+ int ptimeout;
+#else
+ fd_set dsmask;
+#endif
+ struct sockaddr_in from;
+ int fromlen, resplen, seconds, n, s;
+
+ if (EXT(statp).nssocks[ns] == -1) {
+ EXT(statp).nssocks[ns] = socket(PF_INET, SOCK_DGRAM, 0);
+#ifndef _LIBC
+ if (EXT(statp).nssocks[ns] > highestFD) {
+ res_nclose(statp);
+ __set_errno (ENOTSOCK);
+ }
+#endif
+ if (EXT(statp).nssocks[ns] < 0) {
+ *terrno = errno;
+ Perror(statp, stderr, "socket(dg)", errno);
+ return (-1);
+ }
+#ifndef CANNOT_CONNECT_DGRAM
+ /*
+ * On a 4.3BSD+ machine (client and server,
+ * actually), sending to a nameserver datagram
+ * port with no nameserver will cause an
+ * ICMP port unreachable message to be returned.
+ * If our datagram socket is "connected" to the
+ * server, we get an ECONNREFUSED error on the next
+ * socket operation, and select returns if the
+ * error message is received. We can thus detect
+ * the absence of a nameserver without timing out.
+ */
+ if (connect(EXT(statp).nssocks[ns], (struct sockaddr *)nsap,
+ sizeof *nsap) < 0) {
+ Aerror(statp, stderr, "connect(dg)", errno, *nsap);
+ res_nclose(statp);
+ return (0);
+ }
+#endif /* !CANNOT_CONNECT_DGRAM */
+ Dprint(statp->options & RES_DEBUG,
+ (stdout, ";; new DG socket\n"))
+ }
+ s = EXT(statp).nssocks[ns];
+#ifndef CANNOT_CONNECT_DGRAM
+ if (send(s, (char*)buf, buflen, 0) != buflen) {
+ Perror(statp, stderr, "send", errno);
+ res_nclose(statp);
+ return (0);
+ }
+#else /* !CANNOT_CONNECT_DGRAM */
+ if (sendto(s, (char*)buf, buflen, 0,
+ (struct sockaddr *)nsap, sizeof *nsap) != buflen)
+ {
+ Aerror(statp, stderr, "sendto", errno, *nsap);
+ res_nclose(statp);
+ return (0);
+ }
+#endif /* !CANNOT_CONNECT_DGRAM */
+
+ /*
+ * Wait for reply.
+ */
+ seconds = (statp->retrans << ns);
+ if (ns > 0)
+ seconds /= statp->nscount;
+ if (seconds <= 0)
+ seconds = 1;
+ now = evNowTime();
+ timeout = evConsTime(seconds, 0);
+ finish = evAddTime(now, timeout);
+ wait:
+#ifdef _LIBC
+ /* Convert struct timespec in milliseconds. */
+ ptimeout = timeout.tv_sec * 1000 + timeout.tv_nsec / 1000000;
+
+ pfd[0].fd = s;
+ pfd[0].events = POLLIN;
+ n = __poll (pfd, 1, ptimeout);
+#else
+ FD_ZERO(&dsmask);
+ FD_SET(s, &dsmask);
+ n = pselect(s + 1, &dsmask, NULL, NULL, &timeout, NULL);
+#endif
+ if (n == 0) {
+ Dprint(statp->options & RES_DEBUG, (stdout, ";; timeout\n"));
+ *gotsomewhere = 1;
+ return (0);
+ }
+ if (n < 0) {
+ if (errno == EINTR) {
+ now = evNowTime();
+ if (evCmpTime(finish, now) > 0) {
+ timeout = evSubTime(finish, now);
+ goto wait;
+ }
+ }
+ Perror(statp, stderr, "select", errno);
+ res_nclose(statp);
+ return (0);
+ }
+ __set_errno (0);
+ fromlen = sizeof(struct sockaddr_in);
+ resplen = recvfrom(s, (char*)ans, anssiz,0,
+ (struct sockaddr *)&from, &fromlen);
+ if (resplen <= 0) {
+ Perror(statp, stderr, "recvfrom", errno);
+ res_nclose(statp);
+ return (0);
+ }
+ *gotsomewhere = 1;
+ if (resplen < HFIXEDSZ) {
+ /*
+ * Undersized message.
+ */
+ Dprint(statp->options & RES_DEBUG,
+ (stdout, ";; undersized: %d\n",
+ resplen));
+ *terrno = EMSGSIZE;
+ res_nclose(statp);
+ return (0);
+ }
+ if (hp->id != anhp->id) {
+ /*
+ * response from old query, ignore it.
+ * XXX - potential security hazard could
+ * be detected here.
+ */
+ DprintQ((statp->options & RES_DEBUG) ||
+ (statp->pfcode & RES_PRF_REPLY),
+ (stdout, ";; old answer:\n"),
+ ans, (resplen > anssiz) ? anssiz : resplen);
+ goto wait;
+ }
+ if (!(statp->options & RES_INSECURE1) &&
+ !res_ourserver_p(statp, &from)) {
+ /*
+ * response from wrong server? ignore it.
+ * XXX - potential security hazard could
+ * be detected here.
+ */
+ DprintQ((statp->options & RES_DEBUG) ||
+ (statp->pfcode & RES_PRF_REPLY),
+ (stdout, ";; not our server:\n"),
+ ans, (resplen > anssiz) ? anssiz : resplen);
+ goto wait;
+ }
+ if (!(statp->options & RES_INSECURE2) &&
+ !res_queriesmatch(buf, buf + buflen,
+ ans, ans + anssiz)) {
+ /*
+ * response contains wrong query? ignore it.
+ * XXX - potential security hazard could
+ * be detected here.
+ */
+ DprintQ((statp->options & RES_DEBUG) ||
+ (statp->pfcode & RES_PRF_REPLY),
+ (stdout, ";; wrong query name:\n"),
+ ans, (resplen > anssiz) ? anssiz : resplen);
+ goto wait;
+ }
+ if (anhp->rcode == SERVFAIL ||
+ anhp->rcode == NOTIMP ||
+ anhp->rcode == REFUSED) {
+ DprintQ(statp->options & RES_DEBUG,
+ (stdout, "server rejected query:\n"),
+ ans, (resplen > anssiz) ? anssiz : resplen);
+ res_nclose(statp);
+ /* don't retry if called from dig */
+ if (!statp->pfcode)
+ return (0);
+ }
+ if (!(statp->options & RES_IGNTC) && anhp->tc) {
+ /*
+ * To get the rest of answer,
+ * use TCP with same server.
+ */
+ Dprint(statp->options & RES_DEBUG,
+ (stdout, ";; truncated answer\n"));
+ *v_circuit = 1;
+ res_nclose(statp);
+ return (1);
+ }
+ /*
+ * All is well, or the error is fatal. Signal that the
+ * next nameserver ought not be tried.
+ */
+ return (resplen);
+}
+
+#ifdef DEBUG
+static void
+Aerror(const res_state statp, FILE *file, const char *string, int error,
+ struct sockaddr_in address)
+{
+ int save = errno;
+
+ if ((statp->options & RES_DEBUG) != 0) {
+ char tmp[sizeof "255.255.255.255"];
+
+ fprintf(file, "res_send: %s ([%s].%u): %s\n",
+ string,
+ inet_ntop(address.sin_family, &address.sin_addr,
+ tmp, sizeof tmp),
+ ntohs(address.sin_port),
+ strerror(error));
+ }
+ __set_errno (save);
+}
+
+static void
+Perror(const res_state statp, FILE *file, const char *string, int error) {
+ int save = errno;
+
+ if ((statp->options & RES_DEBUG) != 0)
+ fprintf(file, "res_send: %s: %s\n",
+ string, strerror(error));
+ __set_errno (save);
+}
+#endif
+
+static int
+sock_eq(struct sockaddr_in *a1, struct sockaddr_in *a2) {
return ((a1->sin_family == a2->sin_family) &&
(a1->sin_port == a2->sin_port) &&
(a1->sin_addr.s_addr == a2->sin_addr.s_addr));
@@ -943,8 +937,7 @@ cmpsock(struct sockaddr_in *a1, struct sockaddr_in *a2) {
/* XXX needs to move to the porting library. */
static int
pselect(int nfds, void *rfds, void *wfds, void *efds,
- struct timespec *tsp,
- const sigset_t *sigmask)
+ struct timespec *tsp, const sigset_t *sigmask)
{
struct timeval tv, *tvp;
sigset_t sigs;