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