_res_close();
res_close();
res_send(buf, buflen, ans, anssiz)	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;	register int n;	u_int badns;	/* XXX NSMAX can't exceed #/bits in this var */	if ((_res.options & RES_INIT) == 0 && res_init() == -1) {		/* errno should have been set by res_init() in this case. */		return (-1);	}	DprintQ((_res.options & RES_DEBUG) || (_res.pfcode & RES_PRF_QUERY),		(stdout, ";; res_send()\n"), buf, buflen);	v_circuit = (_res.options & RES_USEVC) || buflen > PACKETSZ;	gotsomewhere = 0;	connreset = 0;	terrno = ETIMEDOUT;	badns = 0;	/*	 * Send request, RETRY times, or until successful	 */	for (try = 0; try < _res.retry; try++) {	    for (ns = 0; ns < _res.nscount; ns++) {		struct sockaddr_in *nsap = &_res.nsaddr_list[ns];    same_ns:		if (badns & (1 << ns)) {			_res_close();			goto next_ns;		}		if (Qhook) {			int done = 0, loops = 0;			do {				res_sendhookact act;				act = (*Qhook)(&nsap, &buf, &buflen,					       ans, anssiz, &resplen);				switch (act) {				case res_goahead:					done = 1;					break;				case res_nextns:					_res_close();					goto next_ns;				case res_done:					return (resplen);				case res_modified:					/* give the hook another try */					if (++loops < 42) /*doug adams*/						break;					/*FALLTHROUGH*/				case res_error:					/*FALLTHROUGH*/				default:					return (-1);				}			} while (!done);		}		Dprint(_res.options & RES_DEBUG,		       (stdout, ";; Querying server (# %d) address = %s\n",			ns + 1, inet_ntoa(nsap->sin_addr)));		if (v_circuit) {			int truncated;			struct iovec iov[2];			u_short len;			u_char *cp;			/*			 * Use virtual circuit;			 * at most one attempt per server.			 */			try = _res.retry;			truncated = 0;			if ((s < 0) || (!vc)) {				if (s >= 0)					_res_close();				s = socket(PF_INET, SOCK_STREAM, 0);				if (s < 0) {					terrno = errno;					Perror(stderr, "socket(vc)", errno);					return (-1);				}				__set_errno (0);				if (connect(s, (struct sockaddr *)nsap,					    sizeof(struct sockaddr)) < 0) {					terrno = errno;					Aerror(stderr, "connect/vc",					       errno, *nsap);					badns |= (1 << ns);					_res_close();					goto next_ns;				}				vc = 1;			}			/*			 * 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(s, iov, 2) != (INT16SZ + buflen)) {				terrno = errno;				Perror(stderr, "write failed", errno);				badns |= (1 << ns);				_res_close();				goto next_ns;			}			/*			 * Receive length & response			 */read_len:			cp = ans;			len = INT16SZ;			while ((n = read(s, (char *)cp, (int)len)) > 0) {				cp += n;				if ((len -= n) <= 0)					break;			}			if (n <= 0) {				terrno = errno;				Perror(stderr, "read failed", errno);				_res_close();				/*				 * 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_close();					goto same_ns;				}				_res_close();				goto next_ns;			}			resplen = _getshort(ans);			if (resplen > anssiz) {				Dprint(_res.options & RES_DEBUG,				       (stdout, ";; response truncated\n")				       );				truncated = 1;				len = anssiz;			} else				len = resplen;			cp = ans;			while (len != 0 &&			       (n = read(s, (char *)cp, (int)len)) > 0) {				cp += n;				len -= n;			}			if (n <= 0) {				terrno = errno;				Perror(stderr, "read(vc)", errno);				_res_close();				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 = (len > sizeof(junk)					     ? sizeof(junk)					     : len);					if ((n = read(s, junk, 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((_res.options & RES_DEBUG) ||					(_res.pfcode & RES_PRF_REPLY),					(stdout, ";; old answer (unexpected):\n"),					ans, (resplen>anssiz)?anssiz:resplen);				goto read_len;			}		} else {			/*			 * Use datagrams.			 */			struct timeval timeout;			fd_set dsmask;			struct sockaddr_in from;			size_t fromlen;			if ((s < 0) || vc) {				if (vc)					_res_close();				s = socket(PF_INET, SOCK_DGRAM, 0);				if (s < 0) {#if !CAN_RECONNECT bad_dg_sock:#endif					terrno = errno;					Perror(stderr, "socket(dg)", errno);					return (-1);				}				connected = 0;			}			/*			 * 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 (_res.nscount == 1 || (try == 0 && ns == 0)) {				/*				 * Connect only if we are sure we won't				 * receive a response from another server.				 */				if (!connected) {					if (connect(s, (struct sockaddr *)nsap,						    sizeof(struct sockaddr)						    ) < 0) {						Aerror(stderr,						       "connect(dg)",						       errno, *nsap);						badns |= (1 << ns);						_res_close();						goto next_ns;					}					connected = 1;				}				if (send(s, (char*)buf, buflen, 0) != buflen) {					Perror(stderr, "send", errno);					badns |= (1 << ns);					_res_close();					goto next_ns;				}			} else {				/*				 * Disconnect if we want to listen				 * for responses from more than one server.				 */				if (connected) {#if 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(s,						       (struct sockaddr *)						        &no_addr,						       sizeof(no_addr));#else					int s1 = socket(PF_INET, SOCK_DGRAM,0);					if (s1 < 0)						goto bad_dg_sock;					(void) dup2(s1, s);					(void) close(s1);					Dprint(_res.options & RES_DEBUG,					       (stdout, ";; new DG socket\n"))#endif					connected = 0;					__set_errno (0);				}				if (sendto(s, (char*)buf, buflen, 0,					   (struct sockaddr *)nsap,					   sizeof(struct sockaddr))				    != buflen) {					Aerror(stderr, "sendto", errno, *nsap);					badns |= (1 << ns);					_res_close();					goto next_ns;				}			}			/*			 * Wait for reply			 */			timeout.tv_sec = (_res.retrans << try);			if (try > 0)				timeout.tv_sec /= _res.nscount;			if ((long) timeout.tv_sec <= 0)				timeout.tv_sec = 1;			timeout.tv_usec = 0;    wait:			FD_ZERO(&dsmask);			FD_SET(s, &dsmask);			n = select(s+1, &dsmask, (fd_set *)NULL,				   (fd_set *)NULL, &timeout);			if (n < 0) {				if (errno == EINTR)					goto wait;				Perror(stderr, "select", errno);				_res_close();				goto next_ns;			}			if (n == 0) {				/*				 * timeout				 */				Dprint(_res.options & RES_DEBUG,				       (stdout, ";; timeout\n"));				gotsomewhere = 1;				_res_close();				goto next_ns;			}			__set_errno (0);			fromlen = sizeof(struct sockaddr_in);			resplen = recvfrom(s, (char*)ans, anssiz, 0,					   (struct sockaddr *)&from, &fromlen);			if (resplen <= 0) {				Perror(stderr, "recvfrom", errno);				_res_close();				goto next_ns;			}			gotsomewhere = 1;			if (hp->id != anhp->id) {				/*				 * response from old query, ignore it.				 * XXX - potential security hazard could				 *	 be detected here.				 */				DprintQ((_res.options & RES_DEBUG) ||					(_res.pfcode & RES_PRF_REPLY),					(stdout, ";; old answer:\n"),					ans, (resplen>anssiz)?anssiz:resplen);				goto wait;			}#if CHECK_SRVR_ADDR			if (!(_res.options & RES_INSECURE1) &&			    !res_isourserver(&from)) {				/*				 * response from wrong server? ignore it.				 * XXX - potential security hazard could				 *	 be detected here.				 */				DprintQ((_res.options & RES_DEBUG) ||					(_res.pfcode & RES_PRF_REPLY),					(stdout, ";; not our server:\n"),					ans, (resplen>anssiz)?anssiz:resplen);				goto wait;			}#endif			if (!(_res.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((_res.options & RES_DEBUG) ||					(_res.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(_res.options & RES_DEBUG,					(stdout, "server rejected query:\n"),					ans, (resplen>anssiz)?anssiz:resplen);				badns |= (1 << ns);				_res_close();				/* don't retry if called from dig */				if (!_res.pfcode)					goto next_ns;			}			if (!(_res.options & RES_IGNTC) && anhp->tc) {				/*				 * get rest of answer;				 * use TCP with same server.				 */				Dprint(_res.options & RES_DEBUG,				       (stdout, ";; truncated answer\n"));				v_circuit = 1;				_res_close();				goto same_ns;			}		} /*if vc/dg*/		Dprint((_res.options & RES_DEBUG) ||		       ((_res.pfcode & RES_PRF_REPLY) &&			(_res.pfcode & RES_PRF_HEAD1)),		       (stdout, ";; got answer:\n"));		DprintQ((_res.options & RES_DEBUG) ||			(_res.pfcode & RES_PRF_REPLY),			(stdout, ""),			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 && (!(_res.options & RES_USEVC) || ns != 0)) ||		    !(_res.options & RES_STAYOPEN)) {			_res_close();		}		if (Rhook) {			int done = 0, loops = 0;			do {				res_sendhookact act;				act = (*Rhook)(nsap, buf, buflen,					       ans, anssiz, &resplen);				switch (act) {				case res_goahead:				case res_done:					done = 1;					break;				case res_nextns:					_res_close();					goto next_ns;				case res_modified:					/* give the hook another try */					if (++loops < 42) /*doug adams*/						break;					/*FALLTHROUGH*/				case res_error:					/*FALLTHROUGH*/				default:					return (-1);				}			} while (!done);		}		return (resplen);    next_ns: ;	   } /*foreach ns*/	} /*foreach retry*/	_res_close();	if (!v_circuit)		if (!gotsomewhere)			__set_errno (ECONNREFUSED); /* no nameservers found */		else			__set_errno (ETIMEDOUT);    /* no answer obtained */	else		__set_errno (terrno);	return (-1);}