mirror of
https://github.com/NLnetLabs/unbound.git
synced 2024-09-21 06:37:08 +00:00
- Patch to implement tcp-connection-limit from Jim Hague (Sinodun).
This limits the number of simultaneous TCP client connections from a nominated netblock. And a simple test for TCP connection limit. git-svn-id: file:///svn/unbound/trunk@4835 be551aaa-1e26-0410-a405-d3ace91eadb9
This commit is contained in:
parent
070019c9be
commit
586b811b87
1428
Makefile.in
1428
Makefile.in
File diff suppressed because it is too large
Load Diff
@ -76,6 +76,7 @@
|
||||
#include "util/shm_side/shm_main.h"
|
||||
#include "util/storage/lookup3.h"
|
||||
#include "util/storage/slabhash.h"
|
||||
#include "util/tcp_conn_limit.h"
|
||||
#include "services/listen_dnsport.h"
|
||||
#include "services/cache/rrset.h"
|
||||
#include "services/cache/infra.h"
|
||||
@ -270,11 +271,20 @@ daemon_init(void)
|
||||
free(daemon);
|
||||
return NULL;
|
||||
}
|
||||
daemon->tcl = tcl_list_create();
|
||||
if(!daemon->tcl) {
|
||||
acl_list_delete(daemon->acl);
|
||||
edns_known_options_delete(daemon->env);
|
||||
free(daemon->env);
|
||||
free(daemon);
|
||||
return NULL;
|
||||
}
|
||||
if(gettimeofday(&daemon->time_boot, NULL) < 0)
|
||||
log_err("gettimeofday: %s", strerror(errno));
|
||||
daemon->time_last_stat = daemon->time_boot;
|
||||
if((daemon->env->auth_zones = auth_zones_create()) == 0) {
|
||||
acl_list_delete(daemon->acl);
|
||||
tcl_list_delete(daemon->tcl);
|
||||
edns_known_options_delete(daemon->env);
|
||||
free(daemon->env);
|
||||
free(daemon);
|
||||
@ -575,6 +585,8 @@ daemon_fork(struct daemon* daemon)
|
||||
|
||||
if(!acl_list_apply_cfg(daemon->acl, daemon->cfg, daemon->views))
|
||||
fatal_exit("Could not setup access control list");
|
||||
if(!tcl_list_apply_cfg(daemon->tcl, daemon->cfg))
|
||||
fatal_exit("Could not setup TCP connection limits");
|
||||
if(daemon->cfg->dnscrypt) {
|
||||
#ifdef USE_DNSCRYPT
|
||||
daemon->dnscenv = dnsc_create();
|
||||
@ -735,6 +747,7 @@ daemon_delete(struct daemon* daemon)
|
||||
ub_randfree(daemon->rand);
|
||||
alloc_clear(&daemon->superalloc);
|
||||
acl_list_delete(daemon->acl);
|
||||
tcl_list_delete(daemon->tcl);
|
||||
free(daemon->chroot);
|
||||
free(daemon->pidfile);
|
||||
free(daemon->env);
|
||||
|
@ -113,6 +113,8 @@ struct daemon {
|
||||
struct module_stack mods;
|
||||
/** access control, which client IPs are allowed to connect */
|
||||
struct acl_list* acl;
|
||||
/** TCP connection limit, limit connections from client IPs */
|
||||
struct tcl_list* tcl;
|
||||
/** local authority zones */
|
||||
struct local_zones* local_zones;
|
||||
/** last time of statistics printout */
|
||||
|
@ -1744,6 +1744,7 @@ worker_init(struct worker* worker, struct config_file *cfg,
|
||||
cfg->do_tcp_keepalive
|
||||
? cfg->tcp_keepalive_timeout
|
||||
: cfg->tcp_idle_timeout,
|
||||
worker->daemon->tcl,
|
||||
worker->daemon->listen_sslctx,
|
||||
dtenv, worker_handle_request, worker);
|
||||
if(!worker->front) {
|
||||
|
@ -5,6 +5,9 @@
|
||||
about missing privileges during startup). Add 'AF_INET6' to
|
||||
'RestrictAddressFamilies' (without it IPV6 can't work). From
|
||||
Guido Shanahan.
|
||||
- Patch to implement tcp-connection-limit from Jim Hague (Sinodun).
|
||||
This limits the number of simultaneous TCP client connections
|
||||
from a nominated netblock.
|
||||
|
||||
6 August 2018: Wouter
|
||||
- Fix for #4136: Fix to unconditionally call destroy in daemon.c.
|
||||
|
@ -497,6 +497,11 @@ Enable or disable whether the unbound server forks into the background as
|
||||
a daemon. Set the value to \fIno\fR when unbound runs as systemd service.
|
||||
Default is yes.
|
||||
.TP
|
||||
.B tcp\-connection\-limit: \fI<IP netblock> <limit>
|
||||
Allow up to \fIlimit\R simultaneous TCP connections from the given netblock.
|
||||
When at the limit, further connections are accepted but closed immediately.
|
||||
This option is experimental at this time.
|
||||
.TP
|
||||
.B access\-control: \fI<IP netblock> <action>
|
||||
The netblock is given as an IP4 or IP6 address with /size appended for a
|
||||
classless network block. The action can be \fIdeny\fR, \fIrefuse\fR,
|
||||
|
@ -1219,8 +1219,8 @@ listen_cp_insert(struct comm_point* c, struct listen_dnsport* front)
|
||||
struct listen_dnsport*
|
||||
listen_create(struct comm_base* base, struct listen_port* ports,
|
||||
size_t bufsize, int tcp_accept_count, int tcp_idle_timeout,
|
||||
void* sslctx, struct dt_env* dtenv,
|
||||
comm_point_callback_type* cb, void *cb_arg)
|
||||
struct tcl_list* tcp_conn_limit, void* sslctx,
|
||||
struct dt_env* dtenv, comm_point_callback_type* cb, void *cb_arg)
|
||||
{
|
||||
struct listen_dnsport* front = (struct listen_dnsport*)
|
||||
malloc(sizeof(struct listen_dnsport));
|
||||
@ -1247,11 +1247,11 @@ listen_create(struct comm_base* base, struct listen_port* ports,
|
||||
ports->ftype == listen_type_tcp_dnscrypt)
|
||||
cp = comm_point_create_tcp(base, ports->fd,
|
||||
tcp_accept_count, tcp_idle_timeout,
|
||||
bufsize, cb, cb_arg);
|
||||
tcp_conn_limit, bufsize, cb, cb_arg);
|
||||
else if(ports->ftype == listen_type_ssl) {
|
||||
cp = comm_point_create_tcp(base, ports->fd,
|
||||
tcp_accept_count, tcp_idle_timeout,
|
||||
bufsize, cb, cb_arg);
|
||||
tcp_conn_limit, bufsize, cb, cb_arg);
|
||||
cp->ssl = sslctx;
|
||||
} else if(ports->ftype == listen_type_udpancil ||
|
||||
ports->ftype == listen_type_udpancil_dnscrypt)
|
||||
|
@ -47,6 +47,7 @@ struct listen_list;
|
||||
struct config_file;
|
||||
struct addrinfo;
|
||||
struct sldns_buffer;
|
||||
struct tcl_list;
|
||||
|
||||
/**
|
||||
* Listening for queries structure.
|
||||
@ -138,6 +139,7 @@ void listening_ports_free(struct listen_port* list);
|
||||
* @param tcp_accept_count: max number of simultaneous TCP connections
|
||||
* from clients.
|
||||
* @param tcp_idle_timeout: idle timeout for TCP connections in msec.
|
||||
* @param tcp_conn_limit: TCP connection limit info.
|
||||
* @param sslctx: nonNULL if ssl context.
|
||||
* @param dtenv: nonNULL if dnstap enabled.
|
||||
* @param cb: callback function when a request arrives. It is passed
|
||||
@ -148,8 +150,8 @@ void listening_ports_free(struct listen_port* list);
|
||||
struct listen_dnsport* listen_create(struct comm_base* base,
|
||||
struct listen_port* ports, size_t bufsize,
|
||||
int tcp_accept_count, int tcp_idle_timeout,
|
||||
void* sslctx, struct dt_env *dtenv, comm_point_callback_type* cb,
|
||||
void* cb_arg);
|
||||
struct tcl_list* tcp_conn_limit, void* sslctx,
|
||||
struct dt_env *dtenv, comm_point_callback_type* cb, void* cb_arg);
|
||||
|
||||
/**
|
||||
* delete the listening structure
|
||||
|
@ -252,6 +252,23 @@ aclchecks(struct config_file* cfg)
|
||||
}
|
||||
}
|
||||
|
||||
/** check tcp connection limit ips */
|
||||
static void
|
||||
tcpconnlimitchecks(struct config_file* cfg)
|
||||
{
|
||||
int d;
|
||||
struct sockaddr_storage a;
|
||||
socklen_t alen;
|
||||
struct config_str2list* tcl;
|
||||
for(tcl=cfg->tcp_connection_limits; tcl; tcl = tcl->next) {
|
||||
if(!netblockstrtoaddr(tcl->str, UNBOUND_DNS_PORT, &a, &alen,
|
||||
&d)) {
|
||||
fatal_exit("cannot parse tcp connection limit address %s %s",
|
||||
tcl->str, tcl->str2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** true if fname is a file */
|
||||
static int
|
||||
is_file(const char* fname)
|
||||
@ -381,6 +398,7 @@ morechecks(struct config_file* cfg, const char* fname)
|
||||
warn_hosts("forward-host", cfg->forwards);
|
||||
interfacechecks(cfg);
|
||||
aclchecks(cfg);
|
||||
tcpconnlimitchecks(cfg);
|
||||
|
||||
if(cfg->verbosity < 0)
|
||||
fatal_exit("verbosity value < 0");
|
||||
|
@ -868,6 +868,7 @@ struct listen_dnsport*
|
||||
listen_create(struct comm_base* base, struct listen_port* ATTR_UNUSED(ports),
|
||||
size_t bufsize, int ATTR_UNUSED(tcp_accept_count),
|
||||
int ATTR_UNUSED(tcp_idle_timeout),
|
||||
struct tcl_list* ATTR_UNUSED(tcp_conn_limit),
|
||||
void* ATTR_UNUSED(sslctx), struct dt_env* ATTR_UNUSED(dtenv),
|
||||
comm_point_callback_type* cb, void* cb_arg)
|
||||
{
|
||||
|
16
testdata/tcp_conn_limit.tdir/tcp_conn_limit.conf
vendored
Normal file
16
testdata/tcp_conn_limit.tdir/tcp_conn_limit.conf
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
server:
|
||||
verbosity: 2
|
||||
# num-threads: 1
|
||||
interface: 127.0.0.1
|
||||
port: @PORT@
|
||||
use-syslog: no
|
||||
directory: .
|
||||
pidfile: "unbound.pid"
|
||||
chroot: ""
|
||||
username: ""
|
||||
do-not-query-localhost: no
|
||||
tcp-connection-limit: 0.0.0.0/0 0
|
||||
|
||||
forward-zone:
|
||||
name: "."
|
||||
forward-addr: "127.0.0.1@@TOPORT@"
|
16
testdata/tcp_conn_limit.tdir/tcp_conn_limit.dsc
vendored
Normal file
16
testdata/tcp_conn_limit.tdir/tcp_conn_limit.dsc
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
BaseName: tcp_conn_limit
|
||||
Version: 1.0
|
||||
Description: Test tcp-connection-limit setting.
|
||||
CreationDate: Fri Aug 3 17:18:00 BST 2018
|
||||
Maintainer: dr. J. Hague
|
||||
Category:
|
||||
Component:
|
||||
CmdDepends:
|
||||
Depends:
|
||||
Help:
|
||||
Pre: tcp_conn_limit.pre
|
||||
Post: tcp_conn_limit.post
|
||||
Test: tcp_conn_limit.test
|
||||
AuxFiles:
|
||||
Passed:
|
||||
Failure:
|
10
testdata/tcp_conn_limit.tdir/tcp_conn_limit.post
vendored
Normal file
10
testdata/tcp_conn_limit.tdir/tcp_conn_limit.post
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
# #-- tcp_conn_limit.post --#
|
||||
# source the master var file when it's there
|
||||
[ -f ../.tpkg.var.master ] && source ../.tpkg.var.master
|
||||
# source the test var file when it's there
|
||||
[ -f .tpkg.var.test ] && source .tpkg.var.test
|
||||
#
|
||||
# do your teardown here
|
||||
. ../common.sh
|
||||
kill_pid $FWD_PID
|
||||
kill_pid $UNBOUND_PID
|
31
testdata/tcp_conn_limit.tdir/tcp_conn_limit.pre
vendored
Normal file
31
testdata/tcp_conn_limit.tdir/tcp_conn_limit.pre
vendored
Normal file
@ -0,0 +1,31 @@
|
||||
# #-- tcp_conn_limit.pre--#
|
||||
# source the master var file when it's there
|
||||
[ -f ../.tpkg.var.master ] && source ../.tpkg.var.master
|
||||
# use .tpkg.var.test for in test variable passing
|
||||
[ -f .tpkg.var.test ] && source .tpkg.var.test
|
||||
|
||||
. ../common.sh
|
||||
get_random_port 2
|
||||
UNBOUND_PORT=$RND_PORT
|
||||
FWD_PORT=$(($RND_PORT + 1))
|
||||
echo "UNBOUND_PORT=$UNBOUND_PORT" >> .tpkg.var.test
|
||||
echo "FWD_PORT=$FWD_PORT" >> .tpkg.var.test
|
||||
|
||||
# start forwarder
|
||||
get_ldns_testns
|
||||
$LDNS_TESTNS -p $FWD_PORT tcp_conn_limit.testns >fwd.log 2>&1 &
|
||||
FWD_PID=$!
|
||||
echo "FWD_PID=$FWD_PID" >> .tpkg.var.test
|
||||
|
||||
# make config file
|
||||
sed -e 's/@PORT\@/'$UNBOUND_PORT'/' -e 's/@TOPORT\@/'$FWD_PORT'/' < tcp_conn_limit.conf > ub.conf
|
||||
# start unbound in the background
|
||||
PRE="../.."
|
||||
$PRE/unbound -d -c ub.conf >unbound.log 2>&1 &
|
||||
UNBOUND_PID=$!
|
||||
echo "UNBOUND_PID=$UNBOUND_PID" >> .tpkg.var.test
|
||||
|
||||
cat .tpkg.var.test
|
||||
wait_ldns_testns_up fwd.log
|
||||
wait_unbound_up unbound.log
|
||||
|
42
testdata/tcp_conn_limit.tdir/tcp_conn_limit.test
vendored
Normal file
42
testdata/tcp_conn_limit.tdir/tcp_conn_limit.test
vendored
Normal file
@ -0,0 +1,42 @@
|
||||
# #-- tcp_conn_limit.test --#
|
||||
# source the master var file when it's there
|
||||
[ -f ../.tpkg.var.master ] && source ../.tpkg.var.master
|
||||
# use .tpkg.var.test for in test variable passing
|
||||
[ -f .tpkg.var.test ] && source .tpkg.var.test
|
||||
|
||||
|
||||
# Set unbound to limit all IPv4 addresses to 0 connections.
|
||||
# Make sure TCP connection fails.
|
||||
|
||||
|
||||
if uname | grep MINGW >/dev/null; then
|
||||
echo "no job control in shell on windows. end test"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
PRE="../.."
|
||||
. ../common.sh
|
||||
get_make
|
||||
(cd $PRE; $MAKE streamtcp)
|
||||
|
||||
# test query should fail.
|
||||
echo "> query www.example.com."
|
||||
$PRE/streamtcp -f 127.0.0.1@$UNBOUND_PORT www.example.com. A IN >outfile 2>&1
|
||||
if test "$?" -eq 0; then
|
||||
echo "exit status OK"
|
||||
echo "> cat logfiles"
|
||||
cat outfile
|
||||
cat fwd.log
|
||||
cat unbound.log
|
||||
echo "Not OK"
|
||||
exit 1
|
||||
else
|
||||
echo "exit status not OK"
|
||||
fi
|
||||
echo "> cat logfiles"
|
||||
cat outfile
|
||||
cat fwd.log
|
||||
cat unbound.log
|
||||
echo "OK"
|
||||
|
||||
exit 0
|
42
testdata/tcp_conn_limit.tdir/tcp_conn_limit.testns
vendored
Normal file
42
testdata/tcp_conn_limit.tdir/tcp_conn_limit.testns
vendored
Normal file
@ -0,0 +1,42 @@
|
||||
; nameserver test file
|
||||
$ORIGIN example.com.
|
||||
$TTL 3600
|
||||
|
||||
ENTRY_BEGIN
|
||||
MATCH UDP opcode qtype qname
|
||||
REPLY QR AA NOERROR TC
|
||||
ADJUST copy_id
|
||||
SECTION QUESTION
|
||||
www IN A
|
||||
ENTRY_END
|
||||
|
||||
ENTRY_BEGIN
|
||||
MATCH TCP opcode qtype qname
|
||||
REPLY QR AA NOERROR
|
||||
ADJUST copy_id sleep=2
|
||||
SECTION QUESTION
|
||||
www IN A
|
||||
SECTION ANSWER
|
||||
www IN A 10.20.30.40
|
||||
ENTRY_END
|
||||
|
||||
ENTRY_BEGIN
|
||||
MATCH opcode qtype qname
|
||||
REPLY QR AA NOERROR
|
||||
ADJUST copy_id
|
||||
SECTION QUESTION
|
||||
www2 IN A
|
||||
SECTION ANSWER
|
||||
www2 IN A 10.20.30.42
|
||||
ENTRY_END
|
||||
|
||||
ENTRY_BEGIN
|
||||
MATCH opcode qtype qname
|
||||
REPLY QR AA NOERROR
|
||||
ADJUST copy_id
|
||||
SECTION QUESTION
|
||||
www3 IN A
|
||||
SECTION ANSWER
|
||||
www3 IN A 10.20.30.43
|
||||
ENTRY_END
|
||||
|
@ -195,6 +195,7 @@ config_create(void)
|
||||
#endif
|
||||
cfg->views = NULL;
|
||||
cfg->acls = NULL;
|
||||
cfg->tcp_connection_limits = NULL;
|
||||
cfg->harden_short_bufsize = 0;
|
||||
cfg->harden_large_queries = 0;
|
||||
cfg->harden_glue = 1;
|
||||
@ -945,6 +946,7 @@ config_get_option(struct config_file* cfg, const char* opt,
|
||||
else O_STR(opt, "control-cert-file", control_cert_file)
|
||||
else O_LST(opt, "root-hints", root_hints)
|
||||
else O_LS2(opt, "access-control", acls)
|
||||
else O_LS2(opt, "tcp-connection-limit", tcp_connection_limits)
|
||||
else O_LST(opt, "do-not-query-address", donotqueryaddrs)
|
||||
else O_LST(opt, "private-address", private_address)
|
||||
else O_LST(opt, "private-domain", private_domain)
|
||||
@ -1347,6 +1349,7 @@ config_delete(struct config_file* cfg)
|
||||
free(cfg->dlv_anchor_file);
|
||||
config_delstrlist(cfg->dlv_anchor_list);
|
||||
config_deldblstrlist(cfg->acls);
|
||||
config_deldblstrlist(cfg->tcp_connection_limits);
|
||||
free(cfg->val_nsec3_key_iterations);
|
||||
config_deldblstrlist(cfg->local_zones);
|
||||
config_delstrlist(cfg->local_zones_nodefault);
|
||||
|
@ -220,6 +220,9 @@ struct config_file {
|
||||
/** use default localhost donotqueryaddr entries */
|
||||
int donotquery_localhost;
|
||||
|
||||
/** list of tcp connection limitss, linked list */
|
||||
struct config_str2list* tcp_connection_limits;
|
||||
|
||||
/** harden against very small edns buffer sizes */
|
||||
int harden_short_bufsize;
|
||||
/** harden against very large query sizes */
|
||||
|
@ -466,6 +466,7 @@ redis-server-host{COLON} { YDVAR(1, VAR_CACHEDB_REDISHOST) }
|
||||
redis-server-port{COLON} { YDVAR(1, VAR_CACHEDB_REDISPORT) }
|
||||
redis-timeout{COLON} { YDVAR(1, VAR_CACHEDB_REDISTIMEOUT) }
|
||||
udp-upstream-without-downstream{COLON} { YDVAR(1, VAR_UDP_UPSTREAM_WITHOUT_DOWNSTREAM) }
|
||||
tcp-connection-limit{COLON} { YDVAR(2, VAR_TCP_CONNECTION_LIMIT) }
|
||||
<INITIAL,val>{NEWLINE} { LEXOUT(("NL\n")); cfg_parser->line++; }
|
||||
|
||||
/* Quoted strings. Strip leading and ending quotes */
|
||||
|
@ -158,7 +158,7 @@ extern struct config_parser_state* cfg_parser;
|
||||
%token VAR_UDP_UPSTREAM_WITHOUT_DOWNSTREAM VAR_FOR_UPSTREAM
|
||||
%token VAR_AUTH_ZONE VAR_ZONEFILE VAR_MASTER VAR_URL VAR_FOR_DOWNSTREAM
|
||||
%token VAR_FALLBACK_ENABLED VAR_TLS_ADDITIONAL_PORT VAR_LOW_RTT VAR_LOW_RTT_PERMIL
|
||||
%token VAR_ALLOW_NOTIFY VAR_TLS_WIN_CERT
|
||||
%token VAR_ALLOW_NOTIFY VAR_TLS_WIN_CERT VAR_TCP_CONNECTION_LIMIT
|
||||
|
||||
%%
|
||||
toplevelvars: /* empty */ | toplevelvars toplevelvar ;
|
||||
@ -251,7 +251,8 @@ content_server: server_num_threads | server_verbosity | server_port |
|
||||
server_ipsecmod_whitelist | server_ipsecmod_strict |
|
||||
server_udp_upstream_without_downstream | server_aggressive_nsec |
|
||||
server_tls_cert_bundle | server_tls_additional_port | server_low_rtt |
|
||||
server_low_rtt_permil | server_tls_win_cert
|
||||
server_low_rtt_permil | server_tls_win_cert |
|
||||
server_tcp_connection_limit
|
||||
;
|
||||
stubstart: VAR_STUB_ZONE
|
||||
{
|
||||
@ -2726,6 +2727,17 @@ redis_timeout: VAR_CACHEDB_REDISTIMEOUT STRING_ARG
|
||||
free($2);
|
||||
}
|
||||
;
|
||||
server_tcp_connection_limit: VAR_TCP_CONNECTION_LIMIT STRING_ARG STRING_ARG
|
||||
{
|
||||
OUTYY(("P(server_tcp_connection_limit:%s %s)\n", $2, $3));
|
||||
if (atoi($3) < 0)
|
||||
yyerror("positive number expected");
|
||||
else {
|
||||
if(!cfg_str2list_insert(&cfg_parser->cfg->tcp_connection_limits, $2, $3))
|
||||
fatal_exit("out of memory adding tcp connection limit");
|
||||
}
|
||||
}
|
||||
;
|
||||
%%
|
||||
|
||||
/* parse helper routines could be here */
|
||||
|
@ -43,6 +43,7 @@
|
||||
#include "util/ub_event.h"
|
||||
#include "util/log.h"
|
||||
#include "util/net_help.h"
|
||||
#include "util/tcp_conn_limit.h"
|
||||
#include "util/fptr_wlist.h"
|
||||
#include "sldns/pkthdr.h"
|
||||
#include "sldns/sbuffer.h"
|
||||
@ -850,6 +851,15 @@ int comm_point_perform_accept(struct comm_point* c,
|
||||
#endif
|
||||
return -1;
|
||||
}
|
||||
if(c->tcp_conn_limit && c->type == comm_tcp_accept) {
|
||||
c->tcl_addr = tcl_addr_lookup(c->tcp_conn_limit, addr, *addrlen);
|
||||
if(!tcl_new_connection(c->tcl_addr)) {
|
||||
log_err_addr("accept rejected",
|
||||
"connection limit exceeded", addr, *addrlen);
|
||||
close(new_fd);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
#ifndef HAVE_ACCEPT4
|
||||
fd_set_nonblock(new_fd);
|
||||
#endif
|
||||
@ -2544,6 +2554,8 @@ comm_point_create_tcp_handler(struct comm_base *base,
|
||||
c->tcp_byte_count = 0;
|
||||
c->tcp_parent = parent;
|
||||
c->tcp_timeout_msec = parent->tcp_timeout_msec;
|
||||
c->tcp_conn_limit = parent->tcp_conn_limit;
|
||||
c->tcl_addr = NULL;
|
||||
c->tcp_keepalive = 0;
|
||||
c->max_tcp_count = 0;
|
||||
c->cur_tcp_count = 0;
|
||||
@ -2586,7 +2598,7 @@ comm_point_create_tcp_handler(struct comm_base *base,
|
||||
|
||||
struct comm_point*
|
||||
comm_point_create_tcp(struct comm_base *base, int fd, int num,
|
||||
int idle_timeout, size_t bufsize,
|
||||
int idle_timeout, struct tcl_list* tcp_conn_limit, size_t bufsize,
|
||||
comm_point_callback_type* callback, void* callback_arg)
|
||||
{
|
||||
struct comm_point* c = (struct comm_point*)calloc(1,
|
||||
@ -2609,6 +2621,8 @@ comm_point_create_tcp(struct comm_base *base, int fd, int num,
|
||||
c->tcp_is_reading = 0;
|
||||
c->tcp_byte_count = 0;
|
||||
c->tcp_timeout_msec = idle_timeout;
|
||||
c->tcp_conn_limit = tcp_conn_limit;
|
||||
c->tcl_addr = NULL;
|
||||
c->tcp_keepalive = 0;
|
||||
c->tcp_parent = NULL;
|
||||
c->max_tcp_count = num;
|
||||
@ -2689,6 +2703,8 @@ comm_point_create_tcp_out(struct comm_base *base, size_t bufsize,
|
||||
c->tcp_is_reading = 0;
|
||||
c->tcp_byte_count = 0;
|
||||
c->tcp_timeout_msec = TCP_QUERY_TIMEOUT;
|
||||
c->tcp_conn_limit = NULL;
|
||||
c->tcl_addr = NULL;
|
||||
c->tcp_keepalive = 0;
|
||||
c->tcp_parent = NULL;
|
||||
c->max_tcp_count = 0;
|
||||
@ -2931,6 +2947,7 @@ comm_point_close(struct comm_point* c)
|
||||
log_err("could not event_del on close");
|
||||
}
|
||||
}
|
||||
tcl_close_connection(c->tcl_addr);
|
||||
/* close fd after removing from event lists, or epoll.. is messed up */
|
||||
if(c->fd != -1 && !c->do_not_close) {
|
||||
if(c->type == comm_tcp || c->type == comm_http) {
|
||||
|
@ -65,6 +65,7 @@
|
||||
struct sldns_buffer;
|
||||
struct comm_point;
|
||||
struct comm_reply;
|
||||
struct tcl_list;
|
||||
struct ub_event_base;
|
||||
|
||||
/* internal event notification data storage structure. */
|
||||
@ -262,6 +263,11 @@ struct comm_point {
|
||||
/** if set, checks for pending error from nonblocking connect() call.*/
|
||||
int tcp_check_nb_connect;
|
||||
|
||||
/** if set, check for connection limit on tcp accept. */
|
||||
struct tcl_list* tcp_conn_limit;
|
||||
/** the entry for the connection. */
|
||||
struct tcl_addr* tcl_addr;
|
||||
|
||||
#ifdef USE_MSG_FASTOPEN
|
||||
/** used to track if the sendto() call should be done when using TFO. */
|
||||
int tcp_do_fastopen;
|
||||
@ -447,6 +453,7 @@ struct comm_point* comm_point_create_udp_ancil(struct comm_base* base,
|
||||
* @param num: becomes max_tcp_count, the routine allocates that
|
||||
* many tcp handler commpoints.
|
||||
* @param idle_timeout: TCP idle timeout in ms.
|
||||
* @param tcp_conn_limit: TCP connection limit info.
|
||||
* @param bufsize: size of buffer to create for handlers.
|
||||
* @param callback: callback function pointer for TCP handlers.
|
||||
* @param callback_arg: will be passed to your callback function.
|
||||
@ -456,8 +463,8 @@ struct comm_point* comm_point_create_udp_ancil(struct comm_base* base,
|
||||
* Inits timeout to NULL. All handlers are on the free list.
|
||||
*/
|
||||
struct comm_point* comm_point_create_tcp(struct comm_base* base,
|
||||
int fd, int num, int idle_timeout, size_t bufsize,
|
||||
comm_point_callback_type* callback, void* callback_arg);
|
||||
int fd, int num, int idle_timeout, struct tcl_list* tcp_conn_limit,
|
||||
size_t bufsize, comm_point_callback_type* callback, void* callback_arg);
|
||||
|
||||
/**
|
||||
* Create an outgoing TCP commpoint. No file descriptor is opened, left at -1.
|
||||
|
191
util/tcp_conn_limit.c
Normal file
191
util/tcp_conn_limit.c
Normal file
@ -0,0 +1,191 @@
|
||||
/*
|
||||
* daemon/tcp_conn_limit.c - client TCP connection limit storage for the server.
|
||||
*
|
||||
* Copyright (c) 2018, NLnet Labs. All rights reserved.
|
||||
*
|
||||
* This software is open source.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of the NLNET LABS nor the names of its contributors may
|
||||
* be used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file
|
||||
*
|
||||
* This file helps the server discard excess TCP connections.
|
||||
*/
|
||||
#include "config.h"
|
||||
#include "util/regional.h"
|
||||
#include "util/log.h"
|
||||
#include "util/config_file.h"
|
||||
#include "util/net_help.h"
|
||||
#include "util/tcp_conn_limit.h"
|
||||
#include "services/localzone.h"
|
||||
#include "sldns/str2wire.h"
|
||||
|
||||
struct tcl_list*
|
||||
tcl_list_create(void)
|
||||
{
|
||||
struct tcl_list* tcl = (struct tcl_list*)calloc(1,
|
||||
sizeof(struct tcl_list));
|
||||
if(!tcl)
|
||||
return NULL;
|
||||
tcl->region = regional_create();
|
||||
if(!tcl->region) {
|
||||
tcl_list_delete(tcl);
|
||||
return NULL;
|
||||
}
|
||||
return tcl;
|
||||
}
|
||||
|
||||
static void
|
||||
tcl_list_free_node(rbnode_type* node, void* ATTR_UNUSED(arg))
|
||||
{
|
||||
struct tcl_addr* n = (struct tcl_addr*) node;
|
||||
lock_quick_destroy(&n->lock);
|
||||
}
|
||||
|
||||
void
|
||||
tcl_list_delete(struct tcl_list* tcl)
|
||||
{
|
||||
if(!tcl)
|
||||
return;
|
||||
traverse_postorder(&tcl->tree, tcl_list_free_node, NULL);
|
||||
regional_destroy(tcl->region);
|
||||
free(tcl);
|
||||
}
|
||||
|
||||
/** insert new address into tcl_list structure */
|
||||
static struct tcl_addr*
|
||||
tcl_list_insert(struct tcl_list* tcl, struct sockaddr_storage* addr,
|
||||
socklen_t addrlen, int net, uint32_t limit,
|
||||
int complain_duplicates)
|
||||
{
|
||||
struct tcl_addr* node = regional_alloc_zero(tcl->region,
|
||||
sizeof(struct tcl_addr));
|
||||
if(!node)
|
||||
return NULL;
|
||||
lock_quick_init(&node->lock);
|
||||
node->limit = limit;
|
||||
if(!addr_tree_insert(&tcl->tree, &node->node, addr, addrlen, net)) {
|
||||
if(complain_duplicates)
|
||||
verbose(VERB_QUERY, "duplicate tcl address ignored.");
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
/** apply tcl_list string */
|
||||
static int
|
||||
tcl_list_str_cfg(struct tcl_list* tcl, const char* str, const char* s2,
|
||||
int complain_duplicates)
|
||||
{
|
||||
struct sockaddr_storage addr;
|
||||
int net;
|
||||
socklen_t addrlen;
|
||||
uint32_t limit;
|
||||
if(atoi(s2) < 0) {
|
||||
log_err("bad connection limit %s", s2);
|
||||
return 0;
|
||||
}
|
||||
limit = (uint32_t)atoi(s2);
|
||||
if(!netblockstrtoaddr(str, UNBOUND_DNS_PORT, &addr, &addrlen, &net)) {
|
||||
log_err("cannot parse connection limit netblock: %s", str);
|
||||
return 0;
|
||||
}
|
||||
if(!tcl_list_insert(tcl, &addr, addrlen, net, limit,
|
||||
complain_duplicates)) {
|
||||
log_err("out of memory");
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** read tcl_list config */
|
||||
static int
|
||||
read_tcl_list(struct tcl_list* tcl, struct config_file* cfg)
|
||||
{
|
||||
struct config_str2list* p;
|
||||
for(p = cfg->tcp_connection_limits; p; p = p->next) {
|
||||
log_assert(p->str && p->str2);
|
||||
if(!tcl_list_str_cfg(tcl, p->str, p->str2, 1))
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
tcl_list_apply_cfg(struct tcl_list* tcl, struct config_file* cfg)
|
||||
{
|
||||
regional_free_all(tcl->region);
|
||||
addr_tree_init(&tcl->tree);
|
||||
if(!read_tcl_list(tcl, cfg))
|
||||
return 0;
|
||||
addr_tree_init_parents(&tcl->tree);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
tcl_new_connection(struct tcl_addr* tcl)
|
||||
{
|
||||
if(tcl) {
|
||||
int res = 1;
|
||||
lock_quick_lock(&tcl->lock);
|
||||
if(tcl->count >= tcl->limit)
|
||||
res = 0;
|
||||
else
|
||||
tcl->count++;
|
||||
lock_quick_unlock(&tcl->lock);
|
||||
return res;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
void
|
||||
tcl_close_connection(struct tcl_addr* tcl)
|
||||
{
|
||||
if(tcl) {
|
||||
lock_quick_lock(&tcl->lock);
|
||||
log_assert(tcl->count > 0);
|
||||
tcl->count--;
|
||||
lock_quick_unlock(&tcl->lock);
|
||||
}
|
||||
}
|
||||
|
||||
struct tcl_addr*
|
||||
tcl_addr_lookup(struct tcl_list* tcl, struct sockaddr_storage* addr,
|
||||
socklen_t addrlen)
|
||||
{
|
||||
return (struct tcl_addr*)addr_tree_lookup(&tcl->tree,
|
||||
addr, addrlen);
|
||||
}
|
||||
|
||||
size_t
|
||||
tcl_list_get_mem(struct tcl_list* tcl)
|
||||
{
|
||||
if(!tcl) return 0;
|
||||
return sizeof(*tcl) + regional_get_mem(tcl->region);
|
||||
}
|
130
util/tcp_conn_limit.h
Normal file
130
util/tcp_conn_limit.h
Normal file
@ -0,0 +1,130 @@
|
||||
/*
|
||||
* daemon/tcp_conn_limit.h - client TCP connection limit storage for the server.
|
||||
*
|
||||
* Copyright (c) 2018, NLnet Labs. All rights reserved.
|
||||
*
|
||||
* This software is open source.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of the NLNET LABS nor the names of its contributors may
|
||||
* be used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file
|
||||
*
|
||||
* This file keeps track of the limit on the number of TCP connections
|
||||
* each client makes the server.
|
||||
*/
|
||||
|
||||
#ifndef DAEMON_TCP_CONN_LIMIT_H
|
||||
#define DAEMON_TCP_CONN_LIMIT_H
|
||||
#include "util/storage/dnstree.h"
|
||||
#include "util/locks.h"
|
||||
struct config_file;
|
||||
struct regional;
|
||||
|
||||
/**
|
||||
* TCP connection limit storage structure
|
||||
*/
|
||||
struct tcl_list {
|
||||
/** regional for allocation */
|
||||
struct regional* region;
|
||||
/**
|
||||
* Tree of the addresses that are TCP connection limited.
|
||||
* contents of type tcl_addr.
|
||||
*/
|
||||
rbtree_type tree;
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* An address span with connection limit information
|
||||
*/
|
||||
struct tcl_addr {
|
||||
/** node in address tree */
|
||||
struct addr_tree_node node;
|
||||
/** lock on structure data */
|
||||
lock_quick_type lock;
|
||||
/** connection limit on this netblock */
|
||||
uint32_t limit;
|
||||
/** current connection count on this netblock */
|
||||
uint32_t count;
|
||||
};
|
||||
|
||||
/**
|
||||
* Create TCP connection limit structure
|
||||
* @return new structure or NULL on error.
|
||||
*/
|
||||
struct tcl_list* tcl_list_create(void);
|
||||
|
||||
/**
|
||||
* Delete TCP connection limit structure.
|
||||
* @param tcl: to delete.
|
||||
*/
|
||||
void tcl_list_delete(struct tcl_list* tcl);
|
||||
|
||||
/**
|
||||
* Process TCP connection limit config.
|
||||
* @param tcl: where to store.
|
||||
* @param cfg: config options.
|
||||
* @return 0 on error.
|
||||
*/
|
||||
int tcl_list_apply_cfg(struct tcl_list* tcl, struct config_file* cfg);
|
||||
|
||||
/**
|
||||
* Increment TCP connection count if found, provided the
|
||||
* count was below the limit.
|
||||
* @param tcl: structure for tcl storage, or NULL.
|
||||
* @return: 0 if limit reached, 1 if tcl was NULL or limit not reached.
|
||||
*/
|
||||
int tcl_new_connection(struct tcl_addr* tcl);
|
||||
|
||||
/**
|
||||
* Decrement TCP connection count if found.
|
||||
* @param tcl: structure for tcl storage, or NULL.
|
||||
*/
|
||||
void tcl_close_connection(struct tcl_addr* tcl);
|
||||
|
||||
/**
|
||||
* Lookup address to see its TCP connection limit structure
|
||||
* @param tcl: structure for address storage.
|
||||
* @param addr: address to check
|
||||
* @param addrlen: length of addr.
|
||||
* @return: tcl structure from this address.
|
||||
*/
|
||||
struct tcl_addr*
|
||||
tcl_addr_lookup(struct tcl_list* acl, struct sockaddr_storage* addr,
|
||||
socklen_t addrlen);
|
||||
|
||||
/**
|
||||
* Get memory used by TCP connection limit structure.
|
||||
* @param tcl: structure for address storage.
|
||||
* @return bytes in use.
|
||||
*/
|
||||
size_t tcl_list_get_mem(struct tcl_list* tcl);
|
||||
|
||||
#endif /* DAEMON_TCP_CONN_LIMIT_H */
|
Loading…
Reference in New Issue
Block a user