- make all filedescriptors close-on-exec

- use SOCK_NOSIGPIPE.
- add kqueue handling (not enabled by default, from FreeBSD)
- add RES_INSECURE1 handling (from FreeBSD)
This commit is contained in:
christos 2013-02-15 14:08:25 +00:00
parent 38e436ce90
commit 650d188146

View File

@ -1,4 +1,4 @@
/* $NetBSD: res_send.c,v 1.25 2012/03/21 00:34:54 christos Exp $ */
/* $NetBSD: res_send.c,v 1.26 2013/02/15 14:08:25 christos Exp $ */
/*
* Portions Copyright (C) 2004-2009 Internet Systems Consortium, Inc. ("ISC")
@ -93,7 +93,7 @@
static const char sccsid[] = "@(#)res_send.c 8.1 (Berkeley) 6/4/93";
static const char rcsid[] = "Id: res_send.c,v 1.22 2009/01/22 23:49:23 tbox Exp";
#else
__RCSID("$NetBSD: res_send.c,v 1.25 2012/03/21 00:34:54 christos Exp $");
__RCSID("$NetBSD: res_send.c,v 1.26 2013/02/15 14:08:25 christos Exp $");
#endif
#endif /* LIBC_SCCS and not lint */
@ -104,13 +104,18 @@ __RCSID("$NetBSD: res_send.c,v 1.25 2012/03/21 00:34:54 christos Exp $");
#include "namespace.h"
#include "port_before.h"
#ifndef USE_KQUEUE
#include "fd_setsize.h"
#endif /* USE_KQUEUE */
#include <sys/types.h>
#include <sys/param.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <sys/uio.h>
#ifdef USE_KQUEUE
#include <sys/event.h>
#endif /* USE_KQUEUE */
#include <netinet/in.h>
#include <arpa/nameser.h>
@ -122,6 +127,7 @@ __RCSID("$NetBSD: res_send.c,v 1.25 2012/03/21 00:34:54 christos Exp $");
#include <resolv.h>
#include <signal.h>
#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
@ -139,6 +145,12 @@ __weak_alias(res_nsend,__res_nsend)
#endif
#endif
#ifndef SOCK_NOSIGPIPE
#define SOCK_NOSIGPIPE 0
#endif
#ifndef SOCK_NOCLOEXEC
#define SOCK_NOCLOEXEC 0
#endif
#ifdef USE_POLL
#ifdef HAVE_STROPTS_H
@ -156,7 +168,7 @@ __weak_alias(res_nsend,__res_nsend)
#define EXT(res) ((res)->_u._ext)
#ifndef USE_POLL
#if !defined(USE_POLL) && !defined(USE_KQUEUE)
static const int highestFD = FD_SETSIZE - 1;
#endif
@ -166,14 +178,18 @@ static int get_salen(const struct sockaddr *);
static struct sockaddr * get_nsaddr(res_state, size_t);
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,
static int send_dg(res_state,
#ifdef USE_KQUEUE
int,
#endif
const u_char *, int,
u_char *, int, int *, int, int,
int *, int *);
static void Aerror(const res_state, FILE *, const char *, int,
const struct sockaddr *, int);
static void Perror(const res_state, FILE *, const char *, int);
static int sock_eq(struct sockaddr *, struct sockaddr *);
#if defined(NEED_PSELECT) && !defined(USE_POLL)
#if defined(NEED_PSELECT) && !defined(USE_POLL) && !defined(USE_KQUEUE)
static int pselect(int, void *, void *, void *,
struct timespec *,
const sigset_t *);
@ -330,6 +346,9 @@ res_nsend(res_state statp,
const u_char *buf, int buflen, u_char *ans, int anssiz)
{
int gotsomewhere, terrno, tries, v_circuit, resplen, ns, n;
#ifdef USE_KQUEUE
int kq;
#endif
char abuf[NI_MAXHOST];
(void)res_check(statp, NULL);
@ -349,6 +368,12 @@ res_nsend(res_state statp,
gotsomewhere = 0;
terrno = ETIMEDOUT;
#ifdef USE_KQUEUE
if ((kq = kqueue1(O_CLOEXEC)) == -1) {
return (-1);
}
#endif
/*
* If the ns_addr_list in the resolver context has changed, then
* invalidate our cached copy and the associated timing data.
@ -464,6 +489,9 @@ res_nsend(res_state statp,
res_nclose(statp);
goto next_ns;
case res_done:
#ifdef USE_KQUEUE
close(kq);
#endif
return (resplen);
case res_modified:
/* give the hook another try */
@ -497,8 +525,12 @@ res_nsend(res_state statp,
resplen = n;
} else {
/* Use datagrams. */
n = send_dg(statp, buf, buflen, ans, anssiz, &terrno,
ns, tries, &v_circuit, &gotsomewhere);
n = send_dg(statp,
#ifdef USE_KQUEUE
kq,
#endif
buf, buflen, ans, anssiz, &terrno,
ns, tries, &v_circuit, &gotsomewhere);
if (n < 0)
goto fail;
if (n == 0)
@ -556,11 +588,17 @@ res_nsend(res_state statp,
} while (!done);
}
#ifdef USE_KQUEUE
close(kq);
#endif
return (resplen);
next_ns: ;
} /*foreach ns*/
} /*foreach retry*/
res_nclose(statp);
#ifdef USE_KQUEUE
close(kq);
#endif
if (!v_circuit) {
if (!gotsomewhere)
errno = ECONNREFUSED; /*%< no nameservers found */
@ -571,6 +609,9 @@ res_nsend(res_state statp,
return (-1);
fail:
res_nclose(statp);
#ifdef USE_KQUEUE
close(kq);
#endif
return (-1);
}
@ -633,7 +674,7 @@ send_vc(res_state statp,
u_short len;
u_char *cp;
void *tmp;
#ifdef SO_NOSIGPIPE
#if defined(SO_NOSIGPIPE) && SOCK_NOSIGPIPE == 0
int on = 1;
#endif
@ -661,8 +702,12 @@ send_vc(res_state statp,
if (statp->_vcsock >= 0)
res_nclose(statp);
statp->_vcsock = socket(nsap->sa_family, SOCK_STREAM, 0);
#ifndef USE_POLL
statp->_vcsock = socket(nsap->sa_family, SOCK_STREAM
| SOCK_NOSIGPIPE | SOCK_CLOEXEC, 0);
#if SOCK_CLOEXEC == 0
fcntl(statp->_vcsock, F_SETFD, FD_CLOEXEC);
#endif
#if !defined(USE_POLL) && !defined(USE_KQUEUE)
if (statp->_vcsock > highestFD) {
res_nclose(statp);
errno = ENOTSOCK;
@ -683,7 +728,7 @@ send_vc(res_state statp,
return (-1);
}
}
#ifdef SO_NOSIGPIPE
#if defined(SO_NOSIGPIPE) && SOCK_NOSIGPIPE == 0
/*
* Disable generation of SIGPIPE when writing to a closed
* socket. Write should return -1 and set errno to EPIPE
@ -692,7 +737,7 @@ send_vc(res_state statp,
* Push on even if setsockopt(SO_NOSIGPIPE) fails.
*/
(void)setsockopt(statp->_vcsock, SOL_SOCKET, SO_NOSIGPIPE, &on,
(unsigned int)sizeof(on));
(socklen_t)sizeof(on));
#endif
errno = 0;
if (connect(statp->_vcsock, nsap, (socklen_t)nsaplen) < 0) {
@ -820,7 +865,11 @@ send_vc(res_state statp,
}
static int
send_dg(res_state statp, const u_char *buf, int buflen, u_char *ans,
send_dg(res_state statp,
#ifdef USE_KQUEUE
int kq,
#endif
const u_char *buf, int buflen, u_char *ans,
int anssiz, int *terrno, int ns, int tries, int *v_circuit,
int *gotsomewhere)
{
@ -833,18 +882,26 @@ send_dg(res_state statp, const u_char *buf, int buflen, u_char *ans,
ISC_SOCKLEN_T fromlen;
ssize_t resplen;
int seconds, n, s;
#ifdef USE_KQUEUE
struct kevent kv;
#else
#ifdef USE_POLL
int polltimeout;
struct pollfd pollfd;
#else
fd_set dsmask;
#endif
#endif
nsap = get_nsaddr(statp, (size_t)ns);
nsaplen = get_salen(nsap);
if (EXT(statp).nssocks[ns] == -1) {
EXT(statp).nssocks[ns] = socket(nsap->sa_family, SOCK_DGRAM, 0);
#ifndef USE_POLL
EXT(statp).nssocks[ns] = socket(nsap->sa_family, SOCK_DGRAM
| SOCK_CLOEXEC, 0);
#if SOCK_CLOEXEC == 0
fcntl(EXT(statp)nssocks[ns], F_SETFD, FD_CLOEXEC);
#endif
#if !defined(USE_POLL) && !defined(USE_KQUEUE)
if (EXT(statp).nssocks[ns] > highestFD) {
res_nclose(statp);
errno = ENOTSOCK;
@ -876,8 +933,16 @@ send_dg(res_state statp, const u_char *buf, int buflen, u_char *ans,
* 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], nsap, (socklen_t)nsaplen) < 0) {
*
* When the option "insecure1" is specified, we'd
* rather expect to see responses from an "unknown"
* address. In order to let the kernel accept such
* responses, do not connect the socket here.
* XXX: or do we need an explicit option to disable
* connecting?
*/
if (!(statp->options & RES_INSECURE1) &&
connect(EXT(statp).nssocks[ns], nsap, (socklen_t)nsaplen) < 0) {
Aerror(statp, stderr, "connect(dg)", errno, nsap,
nsaplen);
res_nclose(statp);
@ -889,13 +954,20 @@ send_dg(res_state statp, const u_char *buf, int buflen, u_char *ans,
}
s = EXT(statp).nssocks[ns];
#ifndef CANNOT_CONNECT_DGRAM
if (send(s, (const char*)buf, (size_t)buflen, 0) != buflen) {
if (statp->options & RES_INSECURE1) {
if (sendto(s,
(const char*)buf, buflen, 0, nsap, (socklen_t)nsaplen) != buflen) {
Aerror(statp, stderr, "sendto", errno, nsap, nsaplen);
res_nclose(statp);
return (0);
}
} else if (send(s, (const char*)buf, (size_t)buflen, 0) != buflen) {
Perror(statp, stderr, "send", errno);
res_nclose(statp);
return (0);
}
#else /* !CANNOT_CONNECT_DGRAM */
if (sendto(s, (const char*)buf, buflen, 0, nsap, nsaplen) != buflen)
if (sendto(s, (const char*)buf, buflen, 0, nsap, (socklen_t)nsaplen) != buflen)
{
Aerror(statp, stderr, "sendto", errno, nsap, nsaplen);
res_nclose(statp);
@ -919,13 +991,18 @@ send_dg(res_state statp, const u_char *buf, int buflen, u_char *ans,
now = evNowTime();
nonow:
#ifndef USE_POLL
FD_ZERO(&dsmask);
FD_SET(s, &dsmask);
if (evCmpTime(finish, now) > 0)
timeout = evSubTime(finish, now);
else
timeout = evConsTime(0L, 0L);
#ifdef USE_KQUEUE
EV_SET(&kv, s, EVFILT_READ, EV_ADD | EV_ONESHOT, 0, 0, 0);
n = kevent(kq, &kv, 1, &kv, 1, &timeout);
#else
FD_ZERO(&dsmask);
FD_SET(s, &dsmask);
n = pselect(s + 1, &dsmask, NULL, NULL, &timeout, NULL);
#endif
#else
timeout = evSubTime(finish, now);
if (timeout.tv_sec < 0)
@ -943,16 +1020,23 @@ send_dg(res_state statp, const u_char *buf, int buflen, u_char *ans,
return (0);
}
if (n < 0) {
#if defined(USE_POLL)
static const char *fun = "poll";
#elif defined(USE_SELECT)
static const char *fun = "select";
#elif defined(USE_KQUEUE)
static const char *fun = "kevent";
#endif
if (errno == EINTR)
goto wait;
#ifndef USE_POLL
Perror(statp, stderr, "select", errno);
#else
Perror(statp, stderr, "poll", errno);
#endif /* USE_POLL */
Perror(statp, stderr, fun, errno);
res_nclose(statp);
return (0);
}
#ifdef USE_KQUEUE
if ((int)kv.ident != s)
goto wait;
#endif
errno = 0;
fromlen = sizeof(from);
resplen = recvfrom(s, (char*)ans, (size_t)anssiz,0,
@ -1118,7 +1202,7 @@ sock_eq(struct sockaddr *a, struct sockaddr *b) {
}
}
#if defined(NEED_PSELECT) && !defined(USE_POLL)
#if defined(NEED_PSELECT) && !defined(USE_POLL) && !defined(USE_KQUEUE)
/* XXX needs to move to the porting library. */
static int
pselect(int nfds, void *rfds, void *wfds, void *efds,