1*ae8c6e27Sflorian /*
2*ae8c6e27Sflorian * daemon/tcp_conn_limit.c - client TCP connection limit storage for the server.
3*ae8c6e27Sflorian *
4*ae8c6e27Sflorian * Copyright (c) 2018, NLnet Labs. All rights reserved.
5*ae8c6e27Sflorian *
6*ae8c6e27Sflorian * This software is open source.
7*ae8c6e27Sflorian *
8*ae8c6e27Sflorian * Redistribution and use in source and binary forms, with or without
9*ae8c6e27Sflorian * modification, are permitted provided that the following conditions
10*ae8c6e27Sflorian * are met:
11*ae8c6e27Sflorian *
12*ae8c6e27Sflorian * Redistributions of source code must retain the above copyright notice,
13*ae8c6e27Sflorian * this list of conditions and the following disclaimer.
14*ae8c6e27Sflorian *
15*ae8c6e27Sflorian * Redistributions in binary form must reproduce the above copyright notice,
16*ae8c6e27Sflorian * this list of conditions and the following disclaimer in the documentation
17*ae8c6e27Sflorian * and/or other materials provided with the distribution.
18*ae8c6e27Sflorian *
19*ae8c6e27Sflorian * Neither the name of the NLNET LABS nor the names of its contributors may
20*ae8c6e27Sflorian * be used to endorse or promote products derived from this software without
21*ae8c6e27Sflorian * specific prior written permission.
22*ae8c6e27Sflorian *
23*ae8c6e27Sflorian * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24*ae8c6e27Sflorian * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25*ae8c6e27Sflorian * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
26*ae8c6e27Sflorian * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
27*ae8c6e27Sflorian * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
28*ae8c6e27Sflorian * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
29*ae8c6e27Sflorian * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
30*ae8c6e27Sflorian * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
31*ae8c6e27Sflorian * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
32*ae8c6e27Sflorian * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
33*ae8c6e27Sflorian * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34*ae8c6e27Sflorian */
35*ae8c6e27Sflorian
36*ae8c6e27Sflorian /**
37*ae8c6e27Sflorian * \file
38*ae8c6e27Sflorian *
39*ae8c6e27Sflorian * This file helps the server discard excess TCP connections.
40*ae8c6e27Sflorian */
41*ae8c6e27Sflorian #include "config.h"
42*ae8c6e27Sflorian #include "util/regional.h"
43*ae8c6e27Sflorian #include "util/log.h"
44*ae8c6e27Sflorian #include "util/config_file.h"
45*ae8c6e27Sflorian #include "util/net_help.h"
46*ae8c6e27Sflorian #include "util/tcp_conn_limit.h"
47*ae8c6e27Sflorian #include "services/localzone.h"
48*ae8c6e27Sflorian #include "sldns/str2wire.h"
49*ae8c6e27Sflorian
50*ae8c6e27Sflorian struct tcl_list*
tcl_list_create(void)51*ae8c6e27Sflorian tcl_list_create(void)
52*ae8c6e27Sflorian {
53*ae8c6e27Sflorian struct tcl_list* tcl = (struct tcl_list*)calloc(1,
54*ae8c6e27Sflorian sizeof(struct tcl_list));
55*ae8c6e27Sflorian if(!tcl)
56*ae8c6e27Sflorian return NULL;
57*ae8c6e27Sflorian tcl->region = regional_create();
58*ae8c6e27Sflorian if(!tcl->region) {
59*ae8c6e27Sflorian tcl_list_delete(tcl);
60*ae8c6e27Sflorian return NULL;
61*ae8c6e27Sflorian }
62*ae8c6e27Sflorian return tcl;
63*ae8c6e27Sflorian }
64*ae8c6e27Sflorian
65*ae8c6e27Sflorian static void
tcl_list_free_node(rbnode_type * node,void * ATTR_UNUSED (arg))66*ae8c6e27Sflorian tcl_list_free_node(rbnode_type* node, void* ATTR_UNUSED(arg))
67*ae8c6e27Sflorian {
68*ae8c6e27Sflorian struct tcl_addr* n = (struct tcl_addr*) node;
69*ae8c6e27Sflorian lock_quick_destroy(&n->lock);
70*ae8c6e27Sflorian #ifdef THREADS_DISABLED
71*ae8c6e27Sflorian (void)n;
72*ae8c6e27Sflorian #endif
73*ae8c6e27Sflorian }
74*ae8c6e27Sflorian
75*ae8c6e27Sflorian void
tcl_list_delete(struct tcl_list * tcl)76*ae8c6e27Sflorian tcl_list_delete(struct tcl_list* tcl)
77*ae8c6e27Sflorian {
78*ae8c6e27Sflorian if(!tcl)
79*ae8c6e27Sflorian return;
80*ae8c6e27Sflorian traverse_postorder(&tcl->tree, tcl_list_free_node, NULL);
81*ae8c6e27Sflorian regional_destroy(tcl->region);
82*ae8c6e27Sflorian free(tcl);
83*ae8c6e27Sflorian }
84*ae8c6e27Sflorian
85*ae8c6e27Sflorian /** insert new address into tcl_list structure */
86*ae8c6e27Sflorian 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)87*ae8c6e27Sflorian tcl_list_insert(struct tcl_list* tcl, struct sockaddr_storage* addr,
88*ae8c6e27Sflorian socklen_t addrlen, int net, uint32_t limit,
89*ae8c6e27Sflorian int complain_duplicates)
90*ae8c6e27Sflorian {
91*ae8c6e27Sflorian struct tcl_addr* node = regional_alloc_zero(tcl->region,
92*ae8c6e27Sflorian sizeof(struct tcl_addr));
93*ae8c6e27Sflorian if(!node)
94*ae8c6e27Sflorian return NULL;
95*ae8c6e27Sflorian lock_quick_init(&node->lock);
96*ae8c6e27Sflorian node->limit = limit;
97*ae8c6e27Sflorian if(!addr_tree_insert(&tcl->tree, &node->node, addr, addrlen, net)) {
98*ae8c6e27Sflorian if(complain_duplicates)
99*ae8c6e27Sflorian verbose(VERB_QUERY, "duplicate tcl address ignored.");
100*ae8c6e27Sflorian }
101*ae8c6e27Sflorian return node;
102*ae8c6e27Sflorian }
103*ae8c6e27Sflorian
104*ae8c6e27Sflorian /** apply tcl_list string */
105*ae8c6e27Sflorian static int
tcl_list_str_cfg(struct tcl_list * tcl,const char * str,const char * s2,int complain_duplicates)106*ae8c6e27Sflorian tcl_list_str_cfg(struct tcl_list* tcl, const char* str, const char* s2,
107*ae8c6e27Sflorian int complain_duplicates)
108*ae8c6e27Sflorian {
109*ae8c6e27Sflorian struct sockaddr_storage addr;
110*ae8c6e27Sflorian int net;
111*ae8c6e27Sflorian socklen_t addrlen;
112*ae8c6e27Sflorian uint32_t limit;
113*ae8c6e27Sflorian if(atoi(s2) < 0) {
114*ae8c6e27Sflorian log_err("bad connection limit %s", s2);
115*ae8c6e27Sflorian return 0;
116*ae8c6e27Sflorian }
117*ae8c6e27Sflorian limit = (uint32_t)atoi(s2);
118*ae8c6e27Sflorian if(!netblockstrtoaddr(str, UNBOUND_DNS_PORT, &addr, &addrlen, &net)) {
119*ae8c6e27Sflorian log_err("cannot parse connection limit netblock: %s", str);
120*ae8c6e27Sflorian return 0;
121*ae8c6e27Sflorian }
122*ae8c6e27Sflorian if(!tcl_list_insert(tcl, &addr, addrlen, net, limit,
123*ae8c6e27Sflorian complain_duplicates)) {
124*ae8c6e27Sflorian log_err("out of memory");
125*ae8c6e27Sflorian return 0;
126*ae8c6e27Sflorian }
127*ae8c6e27Sflorian return 1;
128*ae8c6e27Sflorian }
129*ae8c6e27Sflorian
130*ae8c6e27Sflorian /** read tcl_list config */
131*ae8c6e27Sflorian static int
read_tcl_list(struct tcl_list * tcl,struct config_file * cfg)132*ae8c6e27Sflorian read_tcl_list(struct tcl_list* tcl, struct config_file* cfg)
133*ae8c6e27Sflorian {
134*ae8c6e27Sflorian struct config_str2list* p;
135*ae8c6e27Sflorian for(p = cfg->tcp_connection_limits; p; p = p->next) {
136*ae8c6e27Sflorian log_assert(p->str && p->str2);
137*ae8c6e27Sflorian if(!tcl_list_str_cfg(tcl, p->str, p->str2, 1))
138*ae8c6e27Sflorian return 0;
139*ae8c6e27Sflorian }
140*ae8c6e27Sflorian return 1;
141*ae8c6e27Sflorian }
142*ae8c6e27Sflorian
143*ae8c6e27Sflorian int
tcl_list_apply_cfg(struct tcl_list * tcl,struct config_file * cfg)144*ae8c6e27Sflorian tcl_list_apply_cfg(struct tcl_list* tcl, struct config_file* cfg)
145*ae8c6e27Sflorian {
146*ae8c6e27Sflorian regional_free_all(tcl->region);
147*ae8c6e27Sflorian addr_tree_init(&tcl->tree);
148*ae8c6e27Sflorian if(!read_tcl_list(tcl, cfg))
149*ae8c6e27Sflorian return 0;
150*ae8c6e27Sflorian addr_tree_init_parents(&tcl->tree);
151*ae8c6e27Sflorian return 1;
152*ae8c6e27Sflorian }
153*ae8c6e27Sflorian
154*ae8c6e27Sflorian int
tcl_new_connection(struct tcl_addr * tcl)155*ae8c6e27Sflorian tcl_new_connection(struct tcl_addr* tcl)
156*ae8c6e27Sflorian {
157*ae8c6e27Sflorian if(tcl) {
158*ae8c6e27Sflorian int res = 1;
159*ae8c6e27Sflorian lock_quick_lock(&tcl->lock);
160*ae8c6e27Sflorian if(tcl->count >= tcl->limit)
161*ae8c6e27Sflorian res = 0;
162*ae8c6e27Sflorian else
163*ae8c6e27Sflorian tcl->count++;
164*ae8c6e27Sflorian lock_quick_unlock(&tcl->lock);
165*ae8c6e27Sflorian return res;
166*ae8c6e27Sflorian }
167*ae8c6e27Sflorian return 1;
168*ae8c6e27Sflorian }
169*ae8c6e27Sflorian
170*ae8c6e27Sflorian void
tcl_close_connection(struct tcl_addr * tcl)171*ae8c6e27Sflorian tcl_close_connection(struct tcl_addr* tcl)
172*ae8c6e27Sflorian {
173*ae8c6e27Sflorian if(tcl) {
174*ae8c6e27Sflorian lock_quick_lock(&tcl->lock);
175*ae8c6e27Sflorian log_assert(tcl->count > 0);
176*ae8c6e27Sflorian tcl->count--;
177*ae8c6e27Sflorian lock_quick_unlock(&tcl->lock);
178*ae8c6e27Sflorian }
179*ae8c6e27Sflorian }
180*ae8c6e27Sflorian
181*ae8c6e27Sflorian struct tcl_addr*
tcl_addr_lookup(struct tcl_list * tcl,struct sockaddr_storage * addr,socklen_t addrlen)182*ae8c6e27Sflorian tcl_addr_lookup(struct tcl_list* tcl, struct sockaddr_storage* addr,
183*ae8c6e27Sflorian socklen_t addrlen)
184*ae8c6e27Sflorian {
185*ae8c6e27Sflorian return (struct tcl_addr*)addr_tree_lookup(&tcl->tree,
186*ae8c6e27Sflorian addr, addrlen);
187*ae8c6e27Sflorian }
188*ae8c6e27Sflorian
189*ae8c6e27Sflorian size_t
tcl_list_get_mem(struct tcl_list * tcl)190*ae8c6e27Sflorian tcl_list_get_mem(struct tcl_list* tcl)
191*ae8c6e27Sflorian {
192*ae8c6e27Sflorian if(!tcl) return 0;
193*ae8c6e27Sflorian return sizeof(*tcl) + regional_get_mem(tcl->region);
194*ae8c6e27Sflorian }
195