aboutsummaryrefslogtreecommitdiff
path: root/util/netevent.c
diff options
context:
space:
mode:
Diffstat (limited to 'util/netevent.c')
-rw-r--r--util/netevent.c192
1 files changed, 151 insertions, 41 deletions
diff --git a/util/netevent.c b/util/netevent.c
index aedcb5e07a30..432fead1b19d 100644
--- a/util/netevent.c
+++ b/util/netevent.c
@@ -951,6 +951,10 @@ static int consume_pp2_header(struct sldns_buffer* buf, struct comm_reply* rep,
{
struct sockaddr_in* addr =
(struct sockaddr_in*)&rep->client_addr;
+ if(ntohs(header->len) < PP2_HEADER_LEN_INET) {
+ verbose(VERB_OPS, "proxy_protocol: header too short for IPv4 address");
+ return 0;
+ }
addr->sin_family = AF_INET;
addr->sin_addr.s_addr = header->addr.addr4.src_addr;
addr->sin_port = header->addr.addr4.src_port;
@@ -963,6 +967,10 @@ static int consume_pp2_header(struct sldns_buffer* buf, struct comm_reply* rep,
{
struct sockaddr_in6* addr =
(struct sockaddr_in6*)&rep->client_addr;
+ if(ntohs(header->len) < PP2_HEADER_LEN_INET6) {
+ verbose(VERB_OPS, "proxy_protocol: header too short for IPv6 address");
+ return 0;
+ }
memset(addr, 0, sizeof(*addr));
addr->sin6_family = AF_INET6;
memcpy(&addr->sin6_addr,
@@ -1827,7 +1835,6 @@ doq_send_retry(struct comm_point* c, struct doq_pkt_addr* paddr,
char host[256], port[32];
struct ngtcp2_cid scid;
uint8_t token[NGTCP2_CRYPTO_MAX_RETRY_TOKENLEN];
- ngtcp2_tstamp ts;
ngtcp2_ssize tokenlen, ret;
if(!doq_print_addr_port(&paddr->addr, paddr->addrlen, host,
@@ -1841,12 +1848,10 @@ doq_send_retry(struct comm_point* c, struct doq_pkt_addr* paddr,
scid.datalen = c->doq_socket->sv_scidlen;
doq_cid_randfill(&scid, scid.datalen, c->doq_socket->rnd);
- ts = doq_get_timestamp_nanosec();
-
tokenlen = ngtcp2_crypto_generate_retry_token(token,
c->doq_socket->static_secret, c->doq_socket->static_secret_len,
hd->version, (void*)&paddr->addr, paddr->addrlen, &scid,
- &hd->dcid, ts);
+ &hd->dcid, doq_get_timestamp_nanosec());
if(tokenlen < 0) {
log_err("ngtcp2_crypto_generate_retry_token failed: %s",
ngtcp2_strerror(tokenlen));
@@ -1895,13 +1900,11 @@ doq_verify_retry_token(struct comm_point* c, struct doq_pkt_addr* paddr,
struct ngtcp2_cid* ocid, struct ngtcp2_pkt_hd* hd)
{
char host[256], port[32];
- ngtcp2_tstamp ts;
if(!doq_print_addr_port(&paddr->addr, paddr->addrlen, host,
sizeof(host), port, sizeof(port))) {
log_err("doq_verify_retry_token failed");
return 0;
}
- ts = doq_get_timestamp_nanosec();
verbose(VERB_ALGO, "doq: verifying retry token from %s %s", host,
port);
if(ngtcp2_crypto_verify_retry_token(ocid,
@@ -1913,7 +1916,7 @@ doq_verify_retry_token(struct comm_point* c, struct doq_pkt_addr* paddr,
c->doq_socket->static_secret,
c->doq_socket->static_secret_len, hd->version,
(void*)&paddr->addr, paddr->addrlen, &hd->dcid,
- 10*NGTCP2_SECONDS, ts) != 0) {
+ 10*NGTCP2_SECONDS, doq_get_timestamp_nanosec()) != 0) {
verbose(VERB_ALGO, "doq: could not verify retry token "
"from %s %s", host, port);
return 0;
@@ -1928,13 +1931,11 @@ doq_verify_token(struct comm_point* c, struct doq_pkt_addr* paddr,
struct ngtcp2_pkt_hd* hd)
{
char host[256], port[32];
- ngtcp2_tstamp ts;
if(!doq_print_addr_port(&paddr->addr, paddr->addrlen, host,
sizeof(host), port, sizeof(port))) {
log_err("doq_verify_token failed");
return 0;
}
- ts = doq_get_timestamp_nanosec();
verbose(VERB_ALGO, "doq: verifying token from %s %s", host, port);
if(ngtcp2_crypto_verify_regular_token(
#ifdef HAVE_STRUCT_NGTCP2_PKT_HD_TOKENLEN
@@ -1944,7 +1945,7 @@ doq_verify_token(struct comm_point* c, struct doq_pkt_addr* paddr,
#endif
c->doq_socket->static_secret, c->doq_socket->static_secret_len,
(void*)&paddr->addr, paddr->addrlen, 3600*NGTCP2_SECONDS,
- ts) != 0) {
+ doq_get_timestamp_nanosec()) != 0) {
verbose(VERB_ALGO, "doq: could not verify token from %s %s",
host, port);
return 0;
@@ -2171,6 +2172,7 @@ doq_pickup_timer(struct comm_point* c)
{
struct doq_timer* t;
struct timeval tv;
+ ngtcp2_tstamp ts = 0;
int have_time = 0;
memset(&tv, 0, sizeof(tv));
@@ -2180,27 +2182,24 @@ doq_pickup_timer(struct comm_point* c)
t->worker_doq_socket == c->doq_socket) {
/* pick up this element */
t->worker_doq_socket = c->doq_socket;
+ memcpy(&tv, &t->time_real, sizeof(tv));
+ ts = t->time_mono;
have_time = 1;
- memcpy(&tv, &t->time, sizeof(tv));
break;
}
}
lock_rw_unlock(&c->doq_socket->table->lock);
-
+ c->doq_socket->marked_time = ts;
if(have_time) {
struct timeval rel;
timeval_subtract(&rel, &tv, c->doq_socket->now_tv);
comm_timer_set(c->doq_socket->timer, &rel);
- memcpy(&c->doq_socket->marked_time, &tv,
- sizeof(c->doq_socket->marked_time));
verbose(VERB_ALGO, "doq pickup timer at %d.%6.6d in %d.%6.6d",
(int)tv.tv_sec, (int)tv.tv_usec, (int)rel.tv_sec,
(int)rel.tv_usec);
} else {
if(comm_timer_is_set(c->doq_socket->timer))
comm_timer_disable(c->doq_socket->timer);
- memset(&c->doq_socket->marked_time, 0,
- sizeof(c->doq_socket->marked_time));
verbose(VERB_ALGO, "doq timer disabled");
}
}
@@ -2213,13 +2212,14 @@ doq_done_setup_timer_and_write(struct comm_point* c, struct doq_conn* conn)
uint8_t cid[NGTCP2_MAX_CIDLEN];
rbnode_type* node;
struct timeval new_tv;
+ ngtcp2_tstamp new_ts;
int write_change = 0, timer_change = 0;
/* No longer in callbacks, so the pointer to doq_socket is back
* to NULL. */
conn->doq_socket = NULL;
- if(doq_conn_check_timer(conn, &new_tv))
+ if(doq_conn_check_timer(conn, &new_tv, &new_ts))
timer_change = 1;
if( (conn->write_interest && !conn->on_write_list) ||
(!conn->write_interest && conn->on_write_list))
@@ -2265,7 +2265,7 @@ doq_done_setup_timer_and_write(struct comm_point* c, struct doq_conn* conn)
}
if(timer_change) {
doq_timer_set(c->doq_socket->table, &conn->timer,
- c->doq_socket, &new_tv);
+ c->doq_socket, &new_tv, new_ts);
}
lock_rw_unlock(&c->doq_socket->table->lock);
lock_basic_unlock(&conn->lock);
@@ -2429,7 +2429,7 @@ doq_write_blocked_pkt(struct comm_point* c)
return 1;
}
-/** doq find a timer that timeouted and return the conn, locked. */
+/** doq find a timer that timed out and return the conn, locked. */
static struct doq_conn*
doq_timer_timeout_conn(struct doq_server_socket* doq_socket)
{
@@ -2442,7 +2442,7 @@ doq_timer_timeout_conn(struct doq_server_socket* doq_socket)
conn = t->conn;
/* If now < timer then no further timeouts in tree. */
- if(timeval_smaller(doq_socket->now_tv, &t->time)) {
+ if(timeval_smaller(doq_socket->now_tv, &t->time_real)) {
lock_rw_unlock(&doq_socket->table->lock);
return NULL;
}
@@ -2465,11 +2465,11 @@ doq_timer_erase_marker(struct doq_server_socket* doq_socket)
{
struct doq_timer* t;
lock_rw_wrlock(&doq_socket->table->lock);
- t = doq_timer_find_time(doq_socket->table, &doq_socket->marked_time);
+ t = doq_timer_find_time(doq_socket->table, doq_socket->marked_time);
if(t && t->worker_doq_socket == doq_socket)
t->worker_doq_socket = NULL;
lock_rw_unlock(&doq_socket->table->lock);
- memset(&doq_socket->marked_time, 0, sizeof(doq_socket->marked_time));
+ doq_socket->marked_time = 0;
}
void
@@ -2723,6 +2723,7 @@ doq_server_socket_create(struct doq_table* table, struct ub_randstate* rnd,
{
size_t doq_buffer_size = 4096; /* bytes buffer size, for one packet. */
struct doq_server_socket* doq_socket;
+ log_assert(table != NULL);
doq_socket = calloc(1, sizeof(*doq_socket));
if(!doq_socket) {
return NULL;
@@ -2775,7 +2776,7 @@ doq_server_socket_create(struct doq_table* table, struct ub_randstate* rnd,
free(doq_socket);
return NULL;
}
- memset(&doq_socket->marked_time, 0, sizeof(doq_socket->marked_time));
+ doq_socket->marked_time = 0;
comm_base_timept(base, &doq_socket->now_tt, &doq_socket->now_tv);
doq_socket->cfg = cfg;
return doq_socket;
@@ -2804,6 +2805,7 @@ doq_lookup_repinfo(struct doq_table* table, struct comm_reply* repinfo)
{
struct doq_conn* conn;
struct doq_conn_key key;
+ log_assert(table != NULL);
doq_conn_key_from_repinfo(&key, repinfo);
lock_rw_rdlock(&table->lock);
conn = doq_conn_find(table, &key.paddr.addr,
@@ -2938,6 +2940,8 @@ setup_tcp_handler(struct comm_point* c, int fd, int cur, int max)
c->tcp_is_reading = 1;
c->tcp_byte_count = 0;
c->tcp_keepalive = 0;
+ /* reset to configured value before applying load-based reduction */
+ c->tcp_timeout_msec = c->tcp_parent->tcp_timeout_msec;
/* if more than half the tcp handlers are in use, use a shorter
* timeout for this TCP connection, we need to make space for
* other connections to be able to get attention */
@@ -2973,6 +2977,62 @@ void comm_base_handle_slow_accept(int ATTR_UNUSED(fd),
}
}
+/** out of resources in the accept path: pause all listening for
+ * NETEVENT_SLOW_ACCEPT_TIME and re-arm via comm_base_handle_slow_accept.
+ *
+ * If the routine fails, the socket is accepted and then closed, draining it
+ * from the waiting list of connections to be accepted.
+ * @param c: the comm point that is a listening socket.
+ * @param msec: if 0: uses the slow accept time. Otherwise, sets the time
+ * to wait.
+ */
+static void
+comm_point_slow_accept(struct comm_point* c, int msec)
+{
+ struct comm_base* b = c->ev->base;
+ struct timeval tv;
+ struct ub_event* slowev;
+ if(!b->stop_accept)
+ return;
+ if(b->eb->slow_accept_enabled)
+ return;
+ /* Allocate the event */
+ slowev = ub_event_new(b->eb->base, -1, UB_EV_TIMEOUT,
+ comm_base_handle_slow_accept, b);
+ if(!slowev) {
+ /* The slow accept was not enabled yet, to handle
+ * the allocation failure, instead drain the incoming
+ * connection. */
+ int new_fd = accept(c->fd, NULL, NULL);
+ if(new_fd != -1) {
+ verbose(VERB_ALGO, "slow accept: event_new failed, "
+ "drop connection");
+ sock_close(new_fd);
+ }
+ return;
+ }
+ ub_comm_base_now(b);
+ if(b->eb->last_slow_log+SLOW_LOG_TIME <= b->eb->secs) {
+ b->eb->last_slow_log = b->eb->secs;
+ verbose(VERB_OPS, "out of resources on accept, "
+ "slow down accept for %d msec",
+ NETEVENT_SLOW_ACCEPT_TIME);
+ }
+ b->eb->slow_accept_enabled = 1;
+ fptr_ok(fptr_whitelist_stop_accept(b->stop_accept));
+ (*b->stop_accept)(b->cb_arg);
+ /* set timeout, no mallocs */
+ if(msec == 0)
+ msec = NETEVENT_SLOW_ACCEPT_TIME;
+ tv.tv_sec = msec/1000;
+ tv.tv_usec = (msec%1000)*1000;
+ b->eb->slow_accept = slowev;
+ if(ub_event_add(b->eb->slow_accept, &tv) != 0) {
+ /* we do not want to log here,
+ * error: "event_add failed." */
+ }
+}
+
int comm_point_perform_accept(struct comm_point* c,
struct sockaddr_storage* addr, socklen_t* addrlen)
{
@@ -3006,6 +3066,14 @@ int comm_point_perform_accept(struct comm_point* c,
if(c->ev->base->stop_accept) {
struct comm_base* b = c->ev->base;
struct timeval tv;
+ struct ub_event* slowev = ub_event_new(
+ b->eb->base, -1, UB_EV_TIMEOUT,
+ comm_base_handle_slow_accept, b);
+ if(!slowev) {
+ verbose(VERB_ALGO, "slow accept: "
+ "event_new failed");
+ return -1;
+ }
verbose(VERB_ALGO, "out of file descriptors: "
"slow accept");
ub_comm_base_now(b);
@@ -3025,15 +3093,8 @@ int comm_point_perform_accept(struct comm_point* c,
/* set timeout, no mallocs */
tv.tv_sec = NETEVENT_SLOW_ACCEPT_TIME/1000;
tv.tv_usec = (NETEVENT_SLOW_ACCEPT_TIME%1000)*1000;
- b->eb->slow_accept = ub_event_new(b->eb->base,
- -1, UB_EV_TIMEOUT,
- comm_base_handle_slow_accept, b);
- if(b->eb->slow_accept == NULL) {
- /* we do not want to log here, because
- * that would spam the logfiles.
- * error: "event_base_set failed." */
- }
- else if(ub_event_add(b->eb->slow_accept, &tv)
+ b->eb->slow_accept = slowev;
+ if(ub_event_add(b->eb->slow_accept, &tv)
!= 0) {
/* we do not want to log here,
* error: "event_add failed." */
@@ -3172,7 +3233,7 @@ static void http2_stream_delete(struct http2_session* h2_session,
{
if(h2_stream->mesh_state) {
mesh_state_remove_reply(h2_stream->mesh, h2_stream->mesh_state,
- h2_session->c);
+ h2_session->c, h2_stream, NULL);
h2_stream->mesh_state = NULL;
}
http2_req_stream_clear(h2_stream);
@@ -3214,6 +3275,13 @@ comm_point_tcp_accept_callback(int fd, short event, void* arg)
/* find free tcp handler. */
if(!c->tcp_free) {
log_warn("accepted too many tcp, connections full");
+ /* Wait for a short moment (say 50msec) so that other
+ * TCP connections can complete. Or timeout, at the busy
+ * timeout of about 200msec. That stops this routine from
+ * spinning endlessly, and gives time to complete the other
+ * requests. But it is not as slow as the 2000msec wait
+ * time for when the kernel is out of buffers. */
+ comm_point_slow_accept(c, NETEVENT_SLOW_ACCEPT_QUEUE_TIME);
return;
}
/* accept incoming connection. */
@@ -3235,6 +3303,7 @@ comm_point_tcp_accept_callback(int fd, short event, void* arg)
if(!c_hdl->h2_session ||
!http2_session_server_create(c_hdl->h2_session)) {
log_warn("failed to create nghttp2");
+ comm_point_slow_accept(c, 0);
return;
}
if(!c_hdl->h2_session ||
@@ -3242,6 +3311,7 @@ comm_point_tcp_accept_callback(int fd, short event, void* arg)
log_warn("failed to submit http2 settings");
if(c_hdl->h2_session)
http2_session_server_delete(c_hdl->h2_session);
+ comm_point_slow_accept(c, 0);
return;
}
if(!c->ssl) {
@@ -3258,11 +3328,12 @@ comm_point_tcp_accept_callback(int fd, short event, void* arg)
comm_point_tcp_handle_callback, c_hdl);
}
if(!c_hdl->ev->ev) {
- log_warn("could not ub_event_new, dropped tcp");
+ log_warn("could not ub_event_new, for new tcp");
#ifdef HAVE_NGHTTP2
if(c_hdl->type == comm_http && c_hdl->h2_session)
http2_session_server_delete(c_hdl->h2_session);
#endif
+ comm_point_slow_accept(c, 0);
return;
}
log_assert(fd != -1);
@@ -3276,6 +3347,10 @@ comm_point_tcp_accept_callback(int fd, short event, void* arg)
#endif
return;
}
+ /* move per-netblock TCP-connection-limit handle to the handler so that
+ * comm_point_close() on the handler decrements the count on close */
+ c_hdl->tcl_addr = c->tcl_addr;
+ c->tcl_addr = NULL;
/* Copy remote_address to client_address.
* Simplest way/time for streams to do that. */
c_hdl->repinfo.client_addrlen = c_hdl->repinfo.remote_addrlen;
@@ -4868,8 +4943,17 @@ http_process_initial_header(struct comm_point* c)
return 0;
}
} else if(strncasecmp(line, "Content-Length: ", 16) == 0) {
- if(!c->http_is_chunked)
- c->tcp_byte_count = (size_t)atoi(line+16);
+ if(!c->http_is_chunked) {
+ char* end = NULL;
+ long long cl;
+ errno = 0;
+ cl = strtoll(line+16, &end, 10);
+ if(end == line+16 || errno != 0 || cl < 0) {
+ verbose(VERB_ALGO, "http invalid Content-Length: " ARG_LL "d", cl);
+ return 0; /* reject */
+ }
+ c->tcp_byte_count = (size_t)cl;
+ }
} else if(strncasecmp(line, "Transfer-Encoding: chunked", 19+7) == 0) {
c->tcp_byte_count = 0;
c->http_is_chunked = 1;
@@ -4925,9 +5009,15 @@ http_process_chunk_header(struct comm_point* c)
if(c->http_in_chunk_headers == 1) {
/* read chunked start line */
char* end = NULL;
- c->tcp_byte_count = (size_t)strtol(line, &end, 16);
- if(end == line)
+ long chunk_sz;
+ errno = 0;
+ chunk_sz = strtol(line, &end, 16);
+ if(end == line || errno != 0 || chunk_sz < 0) {
+ verbose(VERB_ALGO, "http invalid chunk size: %ld",
+ chunk_sz);
return 0;
+ }
+ c->tcp_byte_count = (size_t)chunk_sz;
c->http_in_chunk_headers = 0;
/* remove header text from front of buffer */
http_moveover_buffer(c->buffer);
@@ -5005,6 +5095,14 @@ http_chunked_segment(struct comm_point* c)
c->http_stored = 0;
sldns_buffer_skip(c->buffer, (ssize_t)c->tcp_byte_count);
sldns_buffer_clear(c->http_temp);
+ if(sldns_buffer_remaining(c->buffer) >
+ sldns_buffer_capacity(c->http_temp)) {
+ verbose(VERB_OPS, "http chunked: surplus %d exceeds "
+ "temp buffer %d", (int)sldns_buffer_remaining(
+ c->buffer), (int)sldns_buffer_capacity(
+ c->http_temp));
+ return 0;
+ }
sldns_buffer_write(c->http_temp,
sldns_buffer_current(c->buffer),
sldns_buffer_remaining(c->buffer));
@@ -5335,6 +5433,13 @@ comm_point_http_handle_read(int fd, struct comm_point* c)
if(c->http_in_headers || c->http_in_chunk_headers) {
/* if header is done, process the header */
if(!http_header_done(c->buffer)) {
+ if(sldns_buffer_limit(c->buffer) ==
+ sldns_buffer_capacity(c->buffer)) {
+ verbose(VERB_OPS, "http header line "
+ "exceeds %d bytes, transfer "
+ "failed", (int)sldns_buffer_capacity(c->buffer));
+ return 0;
+ }
/* copy remaining data to front of buffer
* and set rest for writing into it */
http_moveover_buffer(c->buffer);
@@ -5883,6 +5988,7 @@ comm_point_create_doq(struct comm_base *base, int fd, sldns_buffer* buffer,
struct comm_point* c = (struct comm_point*)calloc(1,
sizeof(struct comm_point));
short evbits;
+ log_assert(table != NULL);
if(!c)
return NULL;
c->ev = (struct internal_event*)calloc(1,
@@ -6574,7 +6680,10 @@ comm_point_close(struct comm_point* c)
c->event_added = 0;
}
}
- tcl_close_connection(c->tcl_addr);
+ if(c->tcl_addr) {
+ tcl_close_connection(c->tcl_addr);
+ c->tcl_addr = NULL;
+ }
if(c->tcp_req_info)
tcp_req_info_clear(c->tcp_req_info);
if(c->h2_session)
@@ -6667,7 +6776,9 @@ comm_point_send_reply(struct comm_reply *repinfo)
log_assert(repinfo && repinfo->c);
#ifdef USE_DNSCRYPT
buffer = repinfo->c->dnscrypt_buffer;
- if(!dnsc_handle_uncurved_request(repinfo)) {
+ if(!dnsc_handle_uncurved_request(repinfo,
+ repinfo->c->tcp_req_info?
+ repinfo->c->tcp_req_info->spool_buffer:repinfo->c->buffer)) {
return;
}
#else
@@ -6728,7 +6839,6 @@ comm_point_send_reply(struct comm_reply *repinfo)
tcp_req_info_send_reply(repinfo->c->tcp_req_info);
} else if(repinfo->c->use_h2) {
if(!http2_submit_dns_response(repinfo->c->h2_session)) {
- comm_point_drop_reply(repinfo);
return;
}
repinfo->c->h2_stream = NULL;