* Copyright (c) 1985, 1989 Regents of the University of California.
* %sccs.include.redist.c%
* 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
* CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
* PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
* ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
#if defined(LIBC_SCCS) && !defined(lint)
static char sccsid
[] = "@(#)res_send.c 6.28 (Berkeley) %G%";
static char rcsid
[] = "$Id: res_send.c,v 4.9.1.1 1993/05/02 22:43:03 vixie Rel $";
#endif /* LIBC_SCCS and not lint */
* Send query to name server and wait for reply.
#include <arpa/nameser.h>
static int s
= -1; /* socket used for communications */
static struct sockaddr no_addr
;
#define FD_SET(n, p) ((p)->fds_bits[(n)/NFDBITS] |= (1 << ((n) % NFDBITS)))
#define FD_CLR(n, p) ((p)->fds_bits[(n)/NFDBITS] &= ~(1 << ((n) % NFDBITS)))
#define FD_ISSET(n, p) ((p)->fds_bits[(n)/NFDBITS] & (1 << ((n) % NFDBITS)))
#define FD_ZERO(p) bzero((char *)(p), sizeof(*(p)))
res_send(buf
, buflen
, answer
, anslen
)
int try, v_circuit
, resplen
, ns
;
int gotsomewhere
= 0, connected
= 0;
HEADER
*hp
= (HEADER
*) buf
;
HEADER
*anhp
= (HEADER
*) answer
;
u_int badns
; /* XXX NSMAX can't exceed #/bits per this */
if ((_res
.options
& RES_DEBUG
) || (_res
.pfcode
& RES_PRF_QUERY
)) {
printf(";; res_send()\n");
if (!(_res
.options
& RES_INIT
))
v_circuit
= (_res
.options
& RES_USEVC
) || buflen
> PACKETSZ
;
* Send request, RETRY times, or until successful
for (try = 0; try < _res
.retry
; try++) {
for (ns
= 0; ns
< _res
.nscount
; ns
++) {
if (_res
.options
& RES_DEBUG
)
printf(";; Querying server (# %d) address = %s\n",
inet_ntoa(_res
.nsaddr_list
[ns
].sin_addr
));
* at most one attempt per server.
s
= socket(AF_INET
, SOCK_STREAM
, 0);
if (_res
.options
& RES_DEBUG
)
perror("socket (vc) failed");
(struct sockaddr
*)&(_res
.nsaddr_list
[ns
]),
sizeof(struct sockaddr
)) < 0) {
if (_res
.options
& RES_DEBUG
)
perror("connect failed");
len
= htons((u_short
)buflen
);
iov
[0].iov_base
= (caddr_t
)&len
;
iov
[0].iov_len
= sizeof(len
);
iov
[1].iov_base
= (char *)buf
;
if (writev(s
, iov
, 2) != sizeof(len
) + buflen
) {
if (_res
.options
& RES_DEBUG
)
* Receive length & response
(n
= read(s
, (char *)cp
, (int)len
)) > 0) {
if (_res
.options
& RES_DEBUG
)
* 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
) {
if ((resplen
= ntohs(*(u_short
*)cp
)) > anslen
) {
if (_res
.options
& RES_DEBUG
)
";; response truncated\n");
(n
= read(s
, (char *)cp
, (int)len
)) > 0) {
if (_res
.options
& RES_DEBUG
)
* so connection stays in synch.
n
= (len
> sizeof(junk
) ?
if ((n
= read(s
, junk
, n
)) > 0)
s
= socket(AF_INET
, SOCK_DGRAM
, 0);
if (_res
.options
& RES_DEBUG
)
perror("socket (dg) failed");
* I'm tired of answering this question, so:
* 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
if (_res
.nscount
== 1 || (try == 0 && ns
== 0)) {
* Don't use connect if we might
* still receive a response
sizeof(struct sockaddr
)) < 0) {
if (_res
.options
& RES_DEBUG
)
if (send(s
, buf
, buflen
, 0) != buflen
) {
if (_res
.options
& RES_DEBUG
)
* Disconnect if we want to listen
* for responses from more than one server.
(void) connect(s
, &no_addr
,
if (sendto(s
, buf
, buflen
, 0,
(struct sockaddr
*)&_res
.nsaddr_list
[ns
],
sizeof(struct sockaddr
)) != buflen
) {
if (_res
.options
& RES_DEBUG
)
timeout
.tv_sec
= (_res
.retrans
<< try);
timeout
.tv_sec
/= _res
.nscount
;
if ((long) timeout
.tv_sec
<= 0)
n
= select(s
+1, &dsmask
, (fd_set
*)NULL
,
(fd_set
*)NULL
, &timeout
);
if (_res
.options
& RES_DEBUG
)
if (_res
.options
& RES_DEBUG
)
if ((resplen
= recv(s
, answer
, anslen
, 0)) <= 0) {
if (_res
.options
& RES_DEBUG
)
* response from old query, ignore it
if ((_res
.options
& RES_DEBUG
) ||
(_res
.pfcode
& RES_PRF_REPLY
)) {
printf(";; old answer:\n");
if (anhp
->rcode
== SERVFAIL
|| anhp
->rcode
== NOTIMP
||
anhp
->rcode
== REFUSED
) {
if (_res
.options
& RES_DEBUG
) {
printf("server rejected query:\n");
if (!(_res
.options
& RES_IGNTC
) && anhp
->tc
) {
* use TCP with same server.
if (_res
.options
& RES_DEBUG
)
printf(";; truncated answer\n");
if (_res
.options
& RES_DEBUG
)
printf(";; got answer:\n");
if ((_res
.options
& RES_DEBUG
) ||
(_res
.pfcode
& RES_PRF_REPLY
))
* 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,
((_res
.options
& RES_USEVC
) == 0 || ns
!= 0)) ||
(_res
.options
& RES_STAYOPEN
) == 0) {
errno
= ECONNREFUSED
; /* no nameservers found */
errno
= ETIMEDOUT
; /* no answer obtained */
* 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.