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