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