1 /* $OpenBSD: sdl.c,v 1.7 2003/07/06 21:57:27 deraadt Exp $ */ 2 /* 3 * Copyright (c) 2003 Bob Beck. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 15 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 18 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 19 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 20 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 21 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 23 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26 /* 27 * sdl.c - Implement spamd source lists 28 * 29 * This consists of everything we need to do to determine which lists 30 * someone is on. Spamd gets the connecting address, and looks it up 31 * against all lists to determine what deferral messages to feed back 32 * to the connecting machine. - The redirection to spamd will happen 33 * from pf in the kernel, first macth will rdr to us. Spamd (along with 34 * setup) must keep track of *all* matches, so as to tell someone all the 35 * lists that they are on. 36 */ 37 38 #include <sys/types.h> 39 #include <sys/socket.h> 40 #include <netinet/in.h> 41 #include <arpa/inet.h> 42 #include <errno.h> 43 #include <stdio.h> 44 #include <stdlib.h> 45 #include <string.h> 46 #include "sdl.h" 47 48 static void sdl_free(struct sdlist *); 49 static void sdl_clear(struct sdlist *); 50 int match_addr(struct sdaddr *a, struct sdaddr *m, struct sdaddr *b, 51 sa_family_t af); 52 53 extern int debug; 54 struct sdlist *blacklists = NULL; 55 int blc = 0, blu = 0; 56 57 int 58 sdl_add(char *sdname, char *sdstring, char ** addrs, int addrc) 59 { 60 int i, index = -1; 61 char astring[40]; 62 unsigned int maskbits; 63 struct sdaddr *m, *n; 64 65 /* 66 * if a blacklist of same tag name is already there, replace it, 67 * otherwise append. 68 */ 69 for (i = 0; i < blu; i++) { 70 if (strcmp(blacklists[i].tag, sdname) == 0) { 71 index = i; 72 break; 73 } 74 } 75 if (index != -1) { 76 if (debug > 0) 77 printf("replacing list %s\n", blacklists[index].tag); 78 sdl_free(&blacklists[index]); 79 } else { 80 if (debug > 0) 81 printf("adding list %s\n", sdname); 82 index = blu; 83 } 84 if (index == blu && blu == blc) { 85 struct sdlist *tmp; 86 tmp = realloc (blacklists, (blc + 128) * 87 sizeof(struct sdlist)); 88 if (tmp == NULL) 89 return (-1); 90 blacklists = tmp; 91 blc += 128; 92 sdl_clear(&blacklists[index]); 93 } 94 95 if ((blacklists[index].tag = strdup(sdname)) == NULL) 96 goto misc_error; 97 if ((blacklists[index].string = strdup(sdstring)) == NULL) 98 goto misc_error; 99 100 blacklists[index].naddrs = addrc; 101 102 /* 103 * Cycle through addrs, converting. We assume they are correctly 104 * formatted v4 and v6 addrs, if they don't all convert correcly, the 105 * add fails. Each address should be address/maskbits 106 */ 107 blacklists[index].addrs = malloc(addrc * sizeof(struct sdentry)); 108 if (blacklists[index].addrs == NULL) 109 goto misc_error; 110 111 for(i = 0; i < addrc; i++) { 112 int j, k, af; 113 114 n = &blacklists[index].addrs[i].sda; 115 m = &blacklists[index].addrs[i].sdm; 116 117 j = sscanf(addrs[i], "%39[^/]/%u", astring, &maskbits); 118 if (j != 2) 119 goto parse_error; 120 if (maskbits > 128) 121 goto parse_error; 122 /* 123 * sanity check! we don't allow a 0 mask - 124 * don't blacklist the entire net. 125 */ 126 if (maskbits == 0) 127 goto parse_error; 128 if (strchr(astring, ':') != NULL) 129 af = AF_INET6; 130 else 131 af = AF_INET; 132 if (af == AF_INET && maskbits > 32) 133 goto parse_error; 134 j = inet_pton(af, astring, n); 135 if (j != 1) 136 goto parse_error; 137 if (debug > 0) 138 printf("added %s/%u\n", astring, maskbits); 139 140 /* set mask, borrowed from pf */ 141 k = 0; 142 for (j = 0; j < 4; j++) 143 m->addr32[j] = 0; 144 while (maskbits >= 32) { 145 m->addr32[k++] = 0xffffffff; 146 maskbits -= 32; 147 } 148 for (j = 31; j > 31 - maskbits; --j) 149 m->addr32[k] |= (1 << j); 150 if (maskbits) 151 m->addr32[k] = htonl(m->addr32[k]); 152 153 /* mask off address bits that won't ever be used */ 154 for (j = 0; j < 4; j++) 155 n->addr32[j] = n->addr32[j] & m->addr32[j]; 156 } 157 if (index == blu) { 158 blu++; 159 blacklists[blu].tag = NULL; 160 } 161 return (0); 162 parse_error: 163 if (debug > 0) 164 printf("sdl_add: parse error, \"%s\"\n", addrs[i]); 165 misc_error: 166 sdl_free(&blacklists[index]); 167 return (-1); 168 } 169 170 171 /* 172 * Return 1 if the addresses a (with mask m) matches address b 173 * otherwise return 0. It is assumed that address a has been 174 * pre-masked out, we only need to mask b. 175 */ 176 int 177 match_addr(struct sdaddr *a, struct sdaddr *m, struct sdaddr *b, 178 sa_family_t af) 179 { 180 int match = 0; 181 182 switch (af) { 183 case AF_INET: 184 if ((a->addr32[0]) == 185 (b->addr32[0] & m->addr32[0])) 186 match++; 187 break; 188 case AF_INET6: 189 if (((a->addr32[0]) == 190 (b->addr32[0] & m->addr32[0])) && 191 ((a->addr32[1]) == 192 (b->addr32[1] & m->addr32[1])) && 193 ((a->addr32[2]) == 194 (b->addr32[2] & m->addr32[2])) && 195 ((a->addr32[3]) == 196 (b->addr32[3] & m->addr32[3]))) 197 match++; 198 break; 199 } 200 return (match); 201 } 202 203 204 /* 205 * Given an address and address family 206 * return list of pointers to matching nodes. or NULL if none. 207 */ 208 struct sdlist ** 209 sdl_lookup(struct sdlist *head, int af, void * src) 210 { 211 int i, matches = 0; 212 struct sdlist *sdl; 213 struct sdentry *sda; 214 struct sdaddr *source = (struct sdaddr *) src; 215 static int sdnewlen = 0; 216 static struct sdlist **sdnew = NULL; 217 218 if (head == NULL) 219 return (NULL); 220 else 221 sdl = head; 222 while (sdl->tag != NULL) { 223 for (i = 0; i < sdl->naddrs; i++) { 224 sda = sdl->addrs + i; 225 if (match_addr(&sda->sda, &sda->sdm, source, af)) { 226 if (matches == sdnewlen) { 227 struct sdlist **tmp; 228 229 tmp = realloc(sdnew, 230 (sdnewlen + 128) * 231 sizeof(struct sdlist *)); 232 if (tmp == NULL) 233 /* 234 * XXX out of memory - 235 * return what we have 236 */ 237 return (sdnew); 238 sdnew = tmp; 239 sdnewlen += 128; 240 } 241 sdnew[matches]= sdl; 242 matches++; 243 sdnew[matches]=NULL; 244 break; 245 } 246 } 247 sdl++; 248 } 249 return (sdnew); 250 } 251 252 static void 253 sdl_free(struct sdlist *sdl) 254 { 255 if (sdl->tag != NULL) 256 free(sdl->tag); 257 258 if (sdl->string != NULL) 259 free(sdl->string); 260 261 if (sdl->addrs != NULL) 262 free(sdl->addrs); 263 264 sdl_clear(sdl); 265 } 266 267 static void 268 sdl_clear(struct sdlist *sdl) 269 { 270 sdl->tag = NULL; 271 sdl->string = NULL; 272 sdl->addrs = NULL; 273 sdl->naddrs = 0; 274 } 275 276