aboutsummaryrefslogtreecommitdiff
path: root/evdns.c
diff options
context:
space:
mode:
authorCy Schubert <cy@FreeBSD.org>2023-11-14 23:02:42 +0000
committerCy Schubert <cy@FreeBSD.org>2023-11-14 23:02:42 +0000
commit5223d1d95fddcef6f9a36e264a5800bd907ade8b (patch)
tree818b1eba912c588e39058586485699385c3179fe /evdns.c
parentcbc620a473ce23d882ba3e9f91ec0c6c12dcd239 (diff)
downloadsrc-5223d1d95fddcef6f9a36e264a5800bd907ade8b.tar.gz
src-5223d1d95fddcef6f9a36e264a5800bd907ade8b.zip
Diffstat (limited to 'evdns.c')
-rw-r--r--evdns.c144
1 files changed, 112 insertions, 32 deletions
diff --git a/evdns.c b/evdns.c
index e9dbc35c6066..a5b31a3c0017 100644
--- a/evdns.c
+++ b/evdns.c
@@ -77,6 +77,7 @@
#include <stdarg.h>
#ifdef _WIN32
#include <winsock2.h>
+#include <winerror.h>
#include <ws2tcpip.h>
#ifndef _WIN32_IE
#define _WIN32_IE 0x400
@@ -346,6 +347,9 @@ struct evdns_base {
struct timeval global_getaddrinfo_allow_skew;
+ int so_rcvbuf;
+ int so_sndbuf;
+
int getaddrinfo_ipv4_timeouts;
int getaddrinfo_ipv6_timeouts;
int getaddrinfo_ipv4_answered;
@@ -863,6 +867,19 @@ reply_schedule_callback(struct request *const req, u32 ttl, u32 err, struct repl
&d->deferred);
}
+
+#define _QR_MASK 0x8000U
+#define _OP_MASK 0x7800U
+#define _AA_MASK 0x0400U
+#define _TC_MASK 0x0200U
+#define _RD_MASK 0x0100U
+#define _RA_MASK 0x0080U
+#define _Z_MASK 0x0040U
+#define _AD_MASK 0x0020U
+#define _CD_MASK 0x0010U
+#define _RCODE_MASK 0x000fU
+#define _Z_MASK_DEPRECATED 0x0070U
+
/* this processes a parsed reply packet */
static void
reply_handle(struct request *const req, u16 flags, u32 ttl, struct reply *reply) {
@@ -876,12 +893,12 @@ reply_handle(struct request *const req, u16 flags, u32 ttl, struct reply *reply)
ASSERT_LOCKED(req->base);
ASSERT_VALID_REQUEST(req);
- if (flags & 0x020f || !reply || !reply->have_answer) {
+ if (flags & (_RCODE_MASK | _TC_MASK) || !reply || !reply->have_answer) {
/* there was an error */
- if (flags & 0x0200) {
+ if (flags & _TC_MASK) {
error = DNS_ERR_TRUNCATED;
- } else if (flags & 0x000f) {
- u16 error_code = (flags & 0x000f) - 1;
+ } else if (flags & _RCODE_MASK) {
+ u16 error_code = (flags & _RCODE_MASK) - 1;
if (error_code > 4) {
error = DNS_ERR_UNKNOWN;
} else {
@@ -1046,8 +1063,8 @@ reply_parse(struct evdns_base *base, u8 *packet, int length) {
memset(&reply, 0, sizeof(reply));
/* If it's not an answer, it doesn't correspond to any request. */
- if (!(flags & 0x8000)) return -1; /* must be an answer */
- if ((flags & 0x020f) && (flags & 0x020f) != DNS_ERR_NOTEXIST) {
+ if (!(flags & _QR_MASK)) return -1; /* must be an answer */
+ if ((flags & (_RCODE_MASK|_TC_MASK)) && (flags & (_RCODE_MASK|_TC_MASK)) != DNS_ERR_NOTEXIST) {
/* there was an error and it's not NXDOMAIN */
goto err;
}
@@ -1236,8 +1253,8 @@ request_parse(u8 *packet, int length, struct evdns_server_port *port, struct soc
(void)additional;
(void)authority;
- if (flags & 0x8000) return -1; /* Must not be an answer. */
- flags &= 0x0110; /* Only RD and CD get preserved. */
+ if (flags & _QR_MASK) return -1; /* Must not be an answer. */
+ flags &= (_RD_MASK|_CD_MASK); /* Only RD and CD get preserved. */
server_req = mm_malloc(sizeof(struct server_request));
if (server_req == NULL) return -1;
@@ -1277,7 +1294,7 @@ request_parse(u8 *packet, int length, struct evdns_server_port *port, struct soc
port->refcnt++;
/* Only standard queries are supported. */
- if (flags & 0x7800) {
+ if (flags & _OP_MASK) {
evdns_server_request_respond(&(server_req->base), DNS_ERR_NOTIMPL);
return -1;
}
@@ -1286,14 +1303,12 @@ request_parse(u8 *packet, int length, struct evdns_server_port *port, struct soc
return 0;
err:
- if (server_req) {
- if (server_req->base.questions) {
- for (i = 0; i < server_req->base.nquestions; ++i)
- mm_free(server_req->base.questions[i]);
- mm_free(server_req->base.questions);
- }
- mm_free(server_req);
+ if (server_req->base.questions) {
+ for (i = 0; i < server_req->base.nquestions; ++i)
+ mm_free(server_req->base.questions[i]);
+ mm_free(server_req->base.questions);
}
+ mm_free(server_req);
return -1;
#undef SKIP_NAME
@@ -1751,6 +1766,7 @@ evdns_close_server_port(struct evdns_server_port *port)
server_port_free(port);
} else {
port->closing = 1;
+ EVDNS_UNLOCK(port);
}
}
@@ -1904,7 +1920,7 @@ evdns_server_request_format_response(struct server_request *req, int err)
/* Set response bit and error code; copy OPCODE and RD fields from
* question; copy RA and AA if set by caller. */
flags = req->base.flags;
- flags |= (0x8000 | err);
+ flags |= (_QR_MASK | err);
dnslabel_table_init(&table);
APPEND16(req->trans_id);
@@ -2265,10 +2281,11 @@ evdns_request_transmit(struct request *req) {
nameserver_write_waiting(req->ns, 1);
return 1;
case 2:
- /* failed to transmit the request entirely. */
+ /* failed to transmit the request entirely. we can fallthrough since
+ * we'll set a timeout, which will time out, and make us retransmit the
+ * request anyway. */
retcode = 1;
- /* fall through: we'll set a timeout, which will time out,
- * and make us retransmit the request anyway. */
+ EVUTIL_FALLTHROUGH;
default:
/* all ok */
log(EVDNS_LOG_DEBUG,
@@ -2525,6 +2542,23 @@ evdns_nameserver_add_impl_(struct evdns_base *base, const struct sockaddr *addre
}
}
+ if (base->so_rcvbuf) {
+ if (setsockopt(ns->socket, SOL_SOCKET, SO_RCVBUF,
+ (void *)&base->so_rcvbuf, sizeof(base->so_rcvbuf))) {
+ log(EVDNS_LOG_WARN, "Couldn't set SO_RCVBUF to %i", base->so_rcvbuf);
+ err = -SO_RCVBUF;
+ goto out2;
+ }
+ }
+ if (base->so_sndbuf) {
+ if (setsockopt(ns->socket, SOL_SOCKET, SO_SNDBUF,
+ (void *)&base->so_sndbuf, sizeof(base->so_sndbuf))) {
+ log(EVDNS_LOG_WARN, "Couldn't set SO_SNDBUF to %i", base->so_sndbuf);
+ err = -SO_SNDBUF;
+ goto out2;
+ }
+ }
+
memcpy(&ns->address, address, addrlen);
ns->addrlen = addrlen;
ns->state = 1;
@@ -3313,10 +3347,16 @@ search_request_finished(struct evdns_request *const handle) {
static void
evdns_resolv_set_defaults(struct evdns_base *base, int flags) {
+ int add_default = flags & DNS_OPTION_NAMESERVERS;
+ if (flags & DNS_OPTION_NAMESERVERS_NO_DEFAULT)
+ add_default = 0;
+
/* if the file isn't found then we assume a local resolver */
ASSERT_LOCKED(base);
- if (flags & DNS_OPTION_SEARCH) search_set_from_hostname(base);
- if (flags & DNS_OPTION_NAMESERVERS) evdns_base_nameserver_ip_add(base,"127.0.0.1");
+ if (flags & DNS_OPTION_SEARCH)
+ search_set_from_hostname(base);
+ if (add_default)
+ evdns_base_nameserver_ip_add(base, "127.0.0.1");
}
#ifndef EVENT__HAVE_STRTOK_R
@@ -3491,6 +3531,7 @@ evdns_base_set_option_impl(struct evdns_base *base,
base->global_max_retransmits = retries;
} else if (str_matches_option(option, "randomize-case:")) {
int randcase = strtoint(val);
+ if (randcase == -1) return -1;
if (!(flags & DNS_OPTION_MISC)) return 0;
base->global_randomize_case = randcase;
} else if (str_matches_option(option, "bind-to:")) {
@@ -3512,6 +3553,18 @@ evdns_base_set_option_impl(struct evdns_base *base,
val);
memcpy(&base->global_nameserver_probe_initial_timeout, &tv,
sizeof(tv));
+ } else if (str_matches_option(option, "so-rcvbuf:")) {
+ int buf = strtoint(val);
+ if (buf == -1) return -1;
+ if (!(flags & DNS_OPTION_MISC)) return 0;
+ log(EVDNS_LOG_DEBUG, "Setting SO_RCVBUF to %s", val);
+ base->so_rcvbuf = buf;
+ } else if (str_matches_option(option, "so-sndbuf:")) {
+ int buf = strtoint(val);
+ if (buf == -1) return -1;
+ if (!(flags & DNS_OPTION_MISC)) return 0;
+ log(EVDNS_LOG_DEBUG, "Setting SO_SNDBUF to %s", val);
+ base->so_sndbuf = buf;
}
return 0;
}
@@ -3612,9 +3665,14 @@ evdns_base_resolv_conf_parse_impl(struct evdns_base *base, int flags, const char
char *resolv;
char *start;
int err = 0;
+ int add_default;
log(EVDNS_LOG_DEBUG, "Parsing resolv.conf file %s", filename);
+ add_default = flags & DNS_OPTION_NAMESERVERS;
+ if (flags & DNS_OPTION_NAMESERVERS_NO_DEFAULT)
+ add_default = 0;
+
if (flags & DNS_OPTION_HOSTSFILE) {
char *fname = evdns_get_default_hosts_filename();
evdns_base_load_hosts(base, fname);
@@ -3622,6 +3680,11 @@ evdns_base_resolv_conf_parse_impl(struct evdns_base *base, int flags, const char
mm_free(fname);
}
+ if (!filename) {
+ evdns_resolv_set_defaults(base, flags);
+ return 1;
+ }
+
if ((err = evutil_read_file_(filename, &resolv, &n, 0)) < 0) {
if (err == -1) {
/* No file. */
@@ -3645,7 +3708,7 @@ evdns_base_resolv_conf_parse_impl(struct evdns_base *base, int flags, const char
}
}
- if (!base->server_head && (flags & DNS_OPTION_NAMESERVERS)) {
+ if (!base->server_head && add_default) {
/* no nameservers were configured. */
evdns_base_nameserver_ip_add(base, "127.0.0.1");
err = 6;
@@ -3946,7 +4009,12 @@ evdns_base_new(struct event_base *event_base, int flags)
TAILQ_INIT(&base->hostsdb);
-#define EVDNS_BASE_ALL_FLAGS (0x8001)
+#define EVDNS_BASE_ALL_FLAGS ( \
+ EVDNS_BASE_INITIALIZE_NAMESERVERS | \
+ EVDNS_BASE_DISABLE_WHEN_INACTIVE | \
+ EVDNS_BASE_NAMESERVERS_NO_DEFAULT | \
+ 0)
+
if (flags & ~EVDNS_BASE_ALL_FLAGS) {
flags = EVDNS_BASE_INITIALIZE_NAMESERVERS;
log(EVDNS_LOG_WARN,
@@ -3957,12 +4025,17 @@ evdns_base_new(struct event_base *event_base, int flags)
if (flags & EVDNS_BASE_INITIALIZE_NAMESERVERS) {
int r;
+ int opts = DNS_OPTIONS_ALL;
+ if (flags & EVDNS_BASE_NAMESERVERS_NO_DEFAULT) {
+ opts |= DNS_OPTION_NAMESERVERS_NO_DEFAULT;
+ }
+
#ifdef _WIN32
r = evdns_base_config_windows_nameservers(base);
#else
- r = evdns_base_resolv_conf_parse(base, DNS_OPTIONS_ALL, "/etc/resolv.conf");
+ r = evdns_base_resolv_conf_parse(base, opts, "/etc/resolv.conf");
#endif
- if (r == -1) {
+ if (r) {
evdns_base_free_and_unlock(base, 0);
return NULL;
}
@@ -4036,6 +4109,11 @@ evdns_base_free_and_unlock(struct evdns_base *base, int fail_requests)
/* TODO(nickm) we might need to refcount here. */
+ while (base->req_waiting_head) {
+ if (fail_requests)
+ reply_schedule_callback(base->req_waiting_head, 0, DNS_ERR_SHUTDOWN, NULL);
+ request_finished(base->req_waiting_head, &base->req_waiting_head, 1);
+ }
for (i = 0; i < base->n_req_heads; ++i) {
while (base->req_heads[i]) {
if (fail_requests)
@@ -4043,11 +4121,6 @@ evdns_base_free_and_unlock(struct evdns_base *base, int fail_requests)
request_finished(base->req_heads[i], &REQ_HEAD(base, base->req_heads[i]->trans_id), 1);
}
}
- while (base->req_waiting_head) {
- if (fail_requests)
- reply_schedule_callback(base->req_waiting_head, 0, DNS_ERR_SHUTDOWN, NULL);
- request_finished(base->req_waiting_head, &base->req_waiting_head, 1);
- }
base->global_requests_inflight = base->global_requests_waiting = 0;
for (server = base->server_head; server; server = server_next) {
@@ -4636,6 +4709,7 @@ evdns_getaddrinfo(struct evdns_base *dns_base,
int err;
int port = 0;
int want_cname = 0;
+ int started = 0;
if (!dns_base) {
dns_base = current_base;
@@ -4714,6 +4788,8 @@ evdns_getaddrinfo(struct evdns_base *dns_base,
* launching those requests. (XXX we don't do that yet.)
*/
+ EVDNS_LOCK(dns_base);
+
if (hints.ai_family != PF_INET6) {
log(EVDNS_LOG_DEBUG, "Sending request for %s on ipv4 as %p",
nodename, &data->ipv4_request);
@@ -4740,7 +4816,11 @@ evdns_getaddrinfo(struct evdns_base *dns_base,
evtimer_assign(&data->timeout, dns_base->event_base,
evdns_getaddrinfo_timeout_cb, data);
- if (data->ipv4_request.r || data->ipv6_request.r) {
+ started = (data->ipv4_request.r || data->ipv6_request.r);
+
+ EVDNS_UNLOCK(dns_base);
+
+ if (started) {
return data;
} else {
mm_free(data);