mirror of
https://github.com/NLnetLabs/unbound.git
synced 2025-12-20 23:00:56 -05:00
- Add stream-wait-size: 4m config option to limit the maximum
memory used by waiting tcp and tls stream replies. This avoids a denial of service where these replies use up all of the memory. git-svn-id: file:///svn/unbound/trunk@5046 be551aaa-1e26-0410-a405-d3ace91eadb9
This commit is contained in:
parent
f5dcd84d27
commit
d81e2c654f
11 changed files with 3471 additions and 3359 deletions
|
|
@ -3,6 +3,9 @@
|
|||
- Unit test for tcp request reorder and timeouts.
|
||||
- Unit tests for ssl out of order processing.
|
||||
- Fix that multiple dns fragments can be carried in one TLS frame.
|
||||
- Add stream-wait-size: 4m config option to limit the maximum
|
||||
memory used by waiting tcp and tls stream replies. This avoids
|
||||
a denial of service where these replies use up all of the memory.
|
||||
|
||||
17 January 2018: Wouter
|
||||
- For caps-for-id fallback, use the whitelist to avoid timeout
|
||||
|
|
|
|||
|
|
@ -123,6 +123,9 @@ server:
|
|||
# Suggested values are 512 to 4096. Default is 4096. 65536 disables it.
|
||||
# max-udp-size: 4096
|
||||
|
||||
# max memory to use for stream(tcp and tls) waiting result buffers.
|
||||
# stream-wait-size: 4m
|
||||
|
||||
# buffer size for handling DNS data. No messages larger than this
|
||||
# size can be sent or received, by UDP or TCP. In bytes.
|
||||
# msg-buffer-size: 65552
|
||||
|
|
|
|||
|
|
@ -55,6 +55,7 @@
|
|||
#include "sldns/sbuffer.h"
|
||||
#include "services/mesh.h"
|
||||
#include "util/fptr_wlist.h"
|
||||
#include "util/locks.h"
|
||||
|
||||
#ifdef HAVE_NETDB_H
|
||||
#include <netdb.h>
|
||||
|
|
@ -75,6 +76,13 @@
|
|||
/** number of simultaneous requests a client can have */
|
||||
#define TCP_MAX_REQ_SIMULTANEOUS 32
|
||||
|
||||
/** lock on the counter of stream buffer memory */
|
||||
static lock_basic_type stream_wait_count_lock;
|
||||
/** size (in bytes) of stream wait buffers */
|
||||
static size_t stream_wait_count = 0;
|
||||
/** is the lock initialised for stream wait buffers */
|
||||
static int stream_wait_lock_inited = 0;
|
||||
|
||||
/**
|
||||
* Debug print of the getaddrinfo returned address.
|
||||
* @param addr: the address returned.
|
||||
|
|
@ -1269,6 +1277,10 @@ listen_create(struct comm_base* base, struct listen_port* ports,
|
|||
free(front);
|
||||
return NULL;
|
||||
}
|
||||
if(!stream_wait_lock_inited) {
|
||||
lock_basic_init(&stream_wait_count_lock);
|
||||
stream_wait_lock_inited = 1;
|
||||
}
|
||||
|
||||
/* create comm points as needed */
|
||||
while(ports) {
|
||||
|
|
@ -1358,6 +1370,10 @@ listen_delete(struct listen_dnsport* front)
|
|||
#endif
|
||||
sldns_buffer_free(front->udp_buff);
|
||||
free(front);
|
||||
if(stream_wait_lock_inited) {
|
||||
stream_wait_lock_inited = 0;
|
||||
lock_basic_destroy(&stream_wait_count_lock);
|
||||
}
|
||||
}
|
||||
|
||||
struct listen_port*
|
||||
|
|
@ -1560,6 +1576,10 @@ void tcp_req_info_clear(struct tcp_req_info* req)
|
|||
item = req->done_req_list;
|
||||
while(item) {
|
||||
nitem = item->next;
|
||||
lock_basic_lock(&stream_wait_count_lock);
|
||||
stream_wait_count -= (sizeof(struct tcp_req_done_item)
|
||||
+item->len);
|
||||
lock_basic_unlock(&stream_wait_count_lock);
|
||||
free(item->buf);
|
||||
free(item);
|
||||
item = nitem;
|
||||
|
|
@ -1638,6 +1658,9 @@ tcp_req_info_pop_done(struct tcp_req_info* req)
|
|||
struct tcp_req_done_item* item;
|
||||
log_assert(req->num_done_req > 0 && req->done_req_list);
|
||||
item = req->done_req_list;
|
||||
lock_basic_lock(&stream_wait_count_lock);
|
||||
stream_wait_count -= (sizeof(struct tcp_req_done_item)+item->len);
|
||||
lock_basic_unlock(&stream_wait_count_lock);
|
||||
req->done_req_list = req->done_req_list->next;
|
||||
req->num_done_req --;
|
||||
return item;
|
||||
|
|
@ -1788,6 +1811,19 @@ tcp_req_info_add_result(struct tcp_req_info* req, uint8_t* buf, size_t len)
|
|||
{
|
||||
struct tcp_req_done_item* last = NULL;
|
||||
struct tcp_req_done_item* item;
|
||||
size_t space;
|
||||
|
||||
/* see if we have space */
|
||||
space = sizeof(struct tcp_req_done_item) + len;
|
||||
lock_basic_lock(&stream_wait_count_lock);
|
||||
if(stream_wait_count + space > stream_wait_max) {
|
||||
lock_basic_unlock(&stream_wait_count_lock);
|
||||
verbose(VERB_ALGO, "drop stream reply, no space left, in stream-wait-size");
|
||||
return 0;
|
||||
}
|
||||
stream_wait_count += stream_wait_max;
|
||||
lock_basic_unlock(&stream_wait_count_lock);
|
||||
|
||||
/* find last element */
|
||||
last = req->done_req_list;
|
||||
while(last && last->next)
|
||||
|
|
@ -1796,6 +1832,7 @@ tcp_req_info_add_result(struct tcp_req_info* req, uint8_t* buf, size_t len)
|
|||
/* create new element */
|
||||
item = (struct tcp_req_done_item*)malloc(sizeof(*item));
|
||||
if(!item) {
|
||||
log_err("malloc failure, for stream result list");
|
||||
return 0;
|
||||
}
|
||||
item->next = NULL;
|
||||
|
|
@ -1803,6 +1840,7 @@ tcp_req_info_add_result(struct tcp_req_info* req, uint8_t* buf, size_t len)
|
|||
item->buf = memdup(buf, len);
|
||||
if(!item->buf) {
|
||||
free(item);
|
||||
log_err("malloc failure, adding reply to stream result list");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -1842,6 +1880,18 @@ tcp_req_info_send_reply(struct tcp_req_info* req)
|
|||
/* queue up the answer behind the others already pending */
|
||||
if(!tcp_req_info_add_result(req, sldns_buffer_begin(req->spool_buffer),
|
||||
sldns_buffer_limit(req->spool_buffer))) {
|
||||
log_err("malloc failure adding reply to stream result list");
|
||||
/* drop the connection, we are out of resources */
|
||||
comm_point_drop_reply(&req->cp->repinfo);
|
||||
}
|
||||
}
|
||||
|
||||
size_t tcp_req_info_get_stream_buffer_size(void)
|
||||
{
|
||||
size_t s;
|
||||
if(!stream_wait_lock_inited)
|
||||
return stream_wait_count;
|
||||
lock_basic_lock(&stream_wait_count_lock);
|
||||
s = stream_wait_count;
|
||||
lock_basic_unlock(&stream_wait_count_lock);
|
||||
return s;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -364,4 +364,7 @@ void tcp_req_info_send_reply(struct tcp_req_info* req);
|
|||
*/
|
||||
int tcp_req_info_handle_read_close(struct tcp_req_info* req);
|
||||
|
||||
/** get the size of currently used tcp stream wait buffers (in bytes) */
|
||||
size_t tcp_req_info_get_stream_buffer_size(void);
|
||||
|
||||
#endif /* LISTEN_DNSPORT_H */
|
||||
|
|
|
|||
|
|
@ -76,6 +76,8 @@ uid_t cfg_uid = (uid_t)-1;
|
|||
gid_t cfg_gid = (gid_t)-1;
|
||||
/** for debug allow small timeout values for fast rollovers */
|
||||
int autr_permit_small_holddown = 0;
|
||||
/** size (in bytes) of stream wait buffers max */
|
||||
size_t stream_wait_max = 4 * 1024 * 1024;
|
||||
|
||||
/** global config during parsing */
|
||||
struct config_parser_state* cfg_parser = 0;
|
||||
|
|
@ -140,6 +142,7 @@ config_create(void)
|
|||
cfg->outgoing_num_tcp = 2; /* leaves 64-52=12 for: 4if,1stop,thread4 */
|
||||
cfg->incoming_num_tcp = 2;
|
||||
#endif
|
||||
cfg->stream_wait_size = 4 * 1024 * 1024;
|
||||
cfg->edns_buffer_size = 4096; /* 4k from rfc recommendation */
|
||||
cfg->msg_buffer_size = 65552; /* 64 k + a small margin */
|
||||
cfg->msg_cache_size = 4 * 1024 * 1024;
|
||||
|
|
@ -491,6 +494,7 @@ int config_set_option(struct config_file* cfg, const char* opt,
|
|||
else S_NUMBER_NONZERO("outgoing-range:", outgoing_num_ports)
|
||||
else S_SIZET_OR_ZERO("outgoing-num-tcp:", outgoing_num_tcp)
|
||||
else S_SIZET_OR_ZERO("incoming-num-tcp:", incoming_num_tcp)
|
||||
else S_MEMSIZE("stream-wait-size:", stream_wait_size)
|
||||
else S_SIZET_NONZERO("edns-buffer-size:", edns_buffer_size)
|
||||
else S_SIZET_NONZERO("msg-buffer-size:", msg_buffer_size)
|
||||
else S_MEMSIZE("msg-cache-size:", msg_cache_size)
|
||||
|
|
@ -877,6 +881,7 @@ config_get_option(struct config_file* cfg, const char* opt,
|
|||
else O_DEC(opt, "outgoing-range", outgoing_num_ports)
|
||||
else O_DEC(opt, "outgoing-num-tcp", outgoing_num_tcp)
|
||||
else O_DEC(opt, "incoming-num-tcp", incoming_num_tcp)
|
||||
else O_MEM(opt, "stream-wait-size", stream_wait_size)
|
||||
else O_DEC(opt, "edns-buffer-size", edns_buffer_size)
|
||||
else O_DEC(opt, "msg-buffer-size", msg_buffer_size)
|
||||
else O_MEM(opt, "msg-cache-size", msg_cache_size)
|
||||
|
|
@ -1910,6 +1915,7 @@ config_apply(struct config_file* config)
|
|||
UNKNOWN_SERVER_NICENESS = config->unknown_server_time_limit;
|
||||
log_set_time_asc(config->log_time_ascii);
|
||||
autr_permit_small_holddown = config->permit_small_holddown;
|
||||
stream_wait_max = config->stream_wait_size;
|
||||
}
|
||||
|
||||
void config_lookup_uid(struct config_file* cfg)
|
||||
|
|
|
|||
|
|
@ -132,6 +132,8 @@ struct config_file {
|
|||
|
||||
/** EDNS buffer size to use */
|
||||
size_t edns_buffer_size;
|
||||
/** size of the stream wait buffers, max */
|
||||
size_t stream_wait_size;
|
||||
/** number of bytes buffer size for DNS messages */
|
||||
size_t msg_buffer_size;
|
||||
/** size of the message cache */
|
||||
|
|
@ -575,6 +577,8 @@ extern uid_t cfg_uid;
|
|||
extern gid_t cfg_gid;
|
||||
/** debug and enable small timeouts */
|
||||
extern int autr_permit_small_holddown;
|
||||
/** size (in bytes) of stream wait buffers max */
|
||||
extern size_t stream_wait_max;
|
||||
|
||||
/**
|
||||
* Stub config options
|
||||
|
|
|
|||
4411
util/configlexer.c
4411
util/configlexer.c
File diff suppressed because it is too large
Load diff
|
|
@ -262,6 +262,7 @@ directory{COLON} { YDVAR(1, VAR_DIRECTORY) }
|
|||
logfile{COLON} { YDVAR(1, VAR_LOGFILE) }
|
||||
pidfile{COLON} { YDVAR(1, VAR_PIDFILE) }
|
||||
root-hints{COLON} { YDVAR(1, VAR_ROOT_HINTS) }
|
||||
stream-wait-size{COLON} { YDVAR(1, VAR_STREAM_WAIT_SIZE) }
|
||||
edns-buffer-size{COLON} { YDVAR(1, VAR_EDNS_BUFFER_SIZE) }
|
||||
msg-buffer-size{COLON} { YDVAR(1, VAR_MSG_BUFFER_SIZE) }
|
||||
msg-cache-size{COLON} { YDVAR(1, VAR_MSG_CACHE_SIZE) }
|
||||
|
|
|
|||
2329
util/configparser.c
2329
util/configparser.c
File diff suppressed because it is too large
Load diff
|
|
@ -304,7 +304,8 @@ extern int yydebug;
|
|||
VAR_LOG_SERVFAIL = 514,
|
||||
VAR_DENY_ANY = 515,
|
||||
VAR_UNKNOWN_SERVER_TIME_LIMIT = 516,
|
||||
VAR_LOG_TAG_QUERYREPLY = 517
|
||||
VAR_LOG_TAG_QUERYREPLY = 517,
|
||||
VAR_STREAM_WAIT_SIZE = 518
|
||||
};
|
||||
#endif
|
||||
/* Tokens. */
|
||||
|
|
@ -568,6 +569,7 @@ extern int yydebug;
|
|||
#define VAR_DENY_ANY 515
|
||||
#define VAR_UNKNOWN_SERVER_TIME_LIMIT 516
|
||||
#define VAR_LOG_TAG_QUERYREPLY 517
|
||||
#define VAR_STREAM_WAIT_SIZE 518
|
||||
|
||||
/* Value type. */
|
||||
#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
|
||||
|
|
@ -578,7 +580,7 @@ union YYSTYPE
|
|||
|
||||
char* str;
|
||||
|
||||
#line 582 "util/configparser.h" /* yacc.c:1909 */
|
||||
#line 584 "util/configparser.h" /* yacc.c:1909 */
|
||||
};
|
||||
|
||||
typedef union YYSTYPE YYSTYPE;
|
||||
|
|
|
|||
|
|
@ -165,6 +165,7 @@ extern struct config_parser_state* cfg_parser;
|
|||
%token VAR_ALLOW_NOTIFY VAR_TLS_WIN_CERT VAR_TCP_CONNECTION_LIMIT
|
||||
%token VAR_FORWARD_NO_CACHE VAR_STUB_NO_CACHE VAR_LOG_SERVFAIL VAR_DENY_ANY
|
||||
%token VAR_UNKNOWN_SERVER_TIME_LIMIT VAR_LOG_TAG_QUERYREPLY
|
||||
%token VAR_STREAM_WAIT_SIZE
|
||||
|
||||
%%
|
||||
toplevelvars: /* empty */ | toplevelvars toplevelvar ;
|
||||
|
|
@ -263,7 +264,8 @@ content_server: server_num_threads | server_verbosity | server_port |
|
|||
server_tls_cert_bundle | server_tls_additional_port | server_low_rtt |
|
||||
server_fast_server_permil | server_fast_server_num | server_tls_win_cert |
|
||||
server_tcp_connection_limit | server_log_servfail | server_deny_any |
|
||||
server_unknown_server_time_limit | server_log_tag_queryreply
|
||||
server_unknown_server_time_limit | server_log_tag_queryreply |
|
||||
server_stream_wait_size
|
||||
;
|
||||
stubstart: VAR_STUB_ZONE
|
||||
{
|
||||
|
|
@ -1127,6 +1129,14 @@ server_ip_freebind: VAR_IP_FREEBIND STRING_ARG
|
|||
free($2);
|
||||
}
|
||||
;
|
||||
server_stream_wait_size: VAR_STREAM_WAIT_SIZE STRING_ARG
|
||||
{
|
||||
OUTYY(("P(server_stream_wait_size:%s)\n", $2));
|
||||
if(!cfg_parse_memsize($2, &cfg_parser->cfg->stream_wait_size))
|
||||
yyerror("memory size expected");
|
||||
free($2);
|
||||
}
|
||||
;
|
||||
server_edns_buffer_size: VAR_EDNS_BUFFER_SIZE STRING_ARG
|
||||
{
|
||||
OUTYY(("P(server_edns_buffer_size:%s)\n", $2));
|
||||
|
|
|
|||
Loading…
Reference in a new issue