1/* $NetBSD: srclimit.c,v 1.3 2021/04/19 14:40:15 christos Exp $ */ 2 3/* 4 * Copyright (c) 2020 Darren Tucker <dtucker@openbsd.org> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18#include "includes.h" 19__RCSID("$NetBSD: srclimit.c,v 1.3 2021/04/19 14:40:15 christos Exp $"); 20 21#include <sys/socket.h> 22#include <sys/types.h> 23 24#include <limits.h> 25#include <netdb.h> 26#include <stdio.h> 27#include <string.h> 28 29#include "addr.h" 30#include "canohost.h" 31#include "log.h" 32#include "misc.h" 33#include "srclimit.h" 34#include "xmalloc.h" 35 36static int max_children, max_persource, ipv4_masklen, ipv6_masklen; 37 38/* Per connection state, used to enforce unauthenticated connection limit. */ 39static struct child_info { 40 int id; 41 struct xaddr addr; 42} *child; 43 44void 45srclimit_init(int max, int persource, int ipv4len, int ipv6len) 46{ 47 int i; 48 49 max_children = max; 50 ipv4_masklen = ipv4len; 51 ipv6_masklen = ipv6len; 52 max_persource = persource; 53 if (max_persource == INT_MAX) /* no limit */ 54 return; 55 debug("%s: max connections %d, per source %d, masks %d,%d", __func__, 56 max, persource, ipv4len, ipv6len); 57 if (max <= 0) 58 fatal("%s: invalid number of sockets: %d", __func__, max); 59 child = xcalloc(max_children, sizeof(*child)); 60 for (i = 0; i < max_children; i++) 61 child[i].id = -1; 62} 63 64/* returns 1 if connection allowed, 0 if not allowed. */ 65int 66srclimit_check_allow(int sock, int id) 67{ 68 struct xaddr xa, xb, xmask; 69 struct sockaddr_storage addr; 70 socklen_t addrlen = sizeof(addr); 71 struct sockaddr *sa = (struct sockaddr *)&addr; 72 int i, bits, first_unused, count = 0; 73 char xas[NI_MAXHOST]; 74 75 if (max_persource == INT_MAX) /* no limit */ 76 return 1; 77 78 debug("%s: sock %d id %d limit %d", __func__, sock, id, max_persource); 79 if (getpeername(sock, sa, &addrlen) != 0) 80 return 1; /* not remote socket? */ 81 if (addr_sa_to_xaddr(sa, addrlen, &xa) != 0) 82 return 1; /* unknown address family? */ 83 84 /* Mask address off address to desired size. */ 85 bits = xa.af == AF_INET ? ipv4_masklen : ipv6_masklen; 86 if (addr_netmask(xa.af, bits, &xmask) != 0 || 87 addr_and(&xb, &xa, &xmask) != 0) { 88 debug3("%s: invalid mask %d bits", __func__, bits); 89 return 1; 90 } 91 92 first_unused = max_children; 93 /* Count matching entries and find first unused one. */ 94 for (i = 0; i < max_children; i++) { 95 if (child[i].id == -1) { 96 if (i < first_unused) 97 first_unused = i; 98 } else if (addr_cmp(&child[i].addr, &xb) == 0) { 99 count++; 100 } 101 } 102 if (addr_ntop(&xa, xas, sizeof(xas)) != 0) { 103 debug3("%s: addr ntop failed", __func__); 104 return 1; 105 } 106 debug3("%s: new unauthenticated connection from %s/%d, at %d of %d", 107 __func__, xas, bits, count, max_persource); 108 109 if (first_unused == max_children) { /* no free slot found */ 110 debug3("%s: no free slot", __func__); 111 return 0; 112 } 113 if (first_unused < 0 || first_unused >= max_children) 114 fatal("%s: internal error: first_unused out of range", 115 __func__); 116 117 if (count >= max_persource) 118 return 0; 119 120 /* Connection allowed, store masked address. */ 121 child[first_unused].id = id; 122 memcpy(&child[first_unused].addr, &xb, sizeof(xb)); 123 return 1; 124} 125 126void 127srclimit_done(int id) 128{ 129 int i; 130 131 if (max_persource == INT_MAX) /* no limit */ 132 return; 133 134 debug("%s: id %d", __func__, id); 135 /* Clear corresponding state entry. */ 136 for (i = 0; i < max_children; i++) { 137 if (child[i].id == id) { 138 child[i].id = -1; 139 return; 140 } 141 } 142} 143