1 /* $OpenBSD: sdl.c,v 1.18 2007/11/03 19:16:07 beck Exp $ */ 2 3 /* 4 * Copyright (c) 2003-2007 Bob Beck. All rights reserved. 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 19 /* 20 * sdl.c - Implement spamd source lists 21 * 22 * This consists of everything we need to do to determine which lists 23 * someone is on. Spamd gets the connecting address, and looks it up 24 * against all lists to determine what deferral messages to feed back 25 * to the connecting machine. - The redirection to spamd will happen 26 * from pf in the kernel, first macth will rdr to us. Spamd (along with 27 * setup) must keep track of *all* matches, so as to tell someone all the 28 * lists that they are on. 29 */ 30 31 #include <sys/types.h> 32 #include <sys/socket.h> 33 #include <netinet/in.h> 34 #include <arpa/inet.h> 35 #include <errno.h> 36 #include <stdio.h> 37 #include <stdlib.h> 38 #include <string.h> 39 #include "sdl.h" 40 41 static void sdl_free(struct sdlist *); 42 static void sdl_clear(struct sdlist *); 43 int match_addr(struct sdaddr *a, struct sdaddr *m, struct sdaddr *b, 44 sa_family_t af); 45 46 extern int debug; 47 struct sdlist *blacklists = NULL; 48 int blc = 0, blu = 0; 49 50 int 51 sdl_add(char *sdname, char *sdstring, char ** addrs, int addrc) 52 { 53 int i, idx = -1; 54 char astring[40]; 55 unsigned int maskbits; 56 struct sdaddr *m, *n; 57 58 /* 59 * if a blacklist of same tag name is already there, replace it, 60 * otherwise append. 61 */ 62 for (i = 0; i < blu; i++) { 63 if (strcmp(blacklists[i].tag, sdname) == 0) { 64 idx = i; 65 break; 66 } 67 } 68 if (idx != -1) { 69 if (debug > 0) 70 printf("replacing list %s; %d new entries\n", 71 blacklists[idx].tag, addrc); 72 sdl_free(&blacklists[idx]); 73 } else { 74 if (debug > 0) 75 printf("adding list %s; %d entries\n", sdname, addrc); 76 idx = blu; 77 } 78 if (idx == blu && blu == blc) { 79 struct sdlist *tmp; 80 81 tmp = realloc(blacklists, (blc + 128) * 82 sizeof(struct sdlist)); 83 if (tmp == NULL) 84 return (-1); 85 blacklists = tmp; 86 blc += 128; 87 sdl_clear(&blacklists[idx]); 88 } 89 90 if ((blacklists[idx].tag = strdup(sdname)) == NULL) 91 goto misc_error; 92 if ((blacklists[idx].string = strdup(sdstring)) == NULL) 93 goto misc_error; 94 95 blacklists[idx].naddrs = addrc; 96 97 /* 98 * Cycle through addrs, converting. We assume they are correctly 99 * formatted v4 and v6 addrs, if they don't all convert correctly, the 100 * add fails. Each address should be address/maskbits 101 */ 102 blacklists[idx].addrs = calloc(addrc, sizeof(struct sdentry)); 103 if (blacklists[idx].addrs == NULL) 104 goto misc_error; 105 106 for (i = 0; i < addrc; i++) { 107 int j, k, af; 108 109 n = &blacklists[idx].addrs[i].sda; 110 m = &blacklists[idx].addrs[i].sdm; 111 112 j = sscanf(addrs[i], "%39[^/]/%u", astring, &maskbits); 113 if (j != 2) 114 goto parse_error; 115 if (maskbits > 128) 116 goto parse_error; 117 /* 118 * sanity check! we don't allow a 0 mask - 119 * don't blacklist the entire net. 120 */ 121 if (maskbits == 0) 122 goto parse_error; 123 if (strchr(astring, ':') != NULL) 124 af = AF_INET6; 125 else 126 af = AF_INET; 127 if (af == AF_INET && maskbits > 32) 128 goto parse_error; 129 j = inet_pton(af, astring, n); 130 if (j != 1) 131 goto parse_error; 132 if (debug > 0) 133 printf("added %s/%u\n", astring, maskbits); 134 135 /* set mask, borrowed from pf */ 136 k = 0; 137 for (j = 0; j < 4; j++) 138 m->addr32[j] = 0; 139 while (maskbits >= 32) { 140 m->addr32[k++] = 0xffffffff; 141 maskbits -= 32; 142 } 143 for (j = 31; j > 31 - maskbits; --j) 144 m->addr32[k] |= (1 << j); 145 if (maskbits) 146 m->addr32[k] = htonl(m->addr32[k]); 147 148 /* mask off address bits that won't ever be used */ 149 for (j = 0; j < 4; j++) 150 n->addr32[j] = n->addr32[j] & m->addr32[j]; 151 } 152 if (idx == blu) { 153 blu++; 154 blacklists[blu].tag = NULL; 155 } 156 return (0); 157 parse_error: 158 if (debug > 0) 159 printf("sdl_add: parse error, \"%s\"\n", addrs[i]); 160 misc_error: 161 sdl_free(&blacklists[idx]); 162 return (-1); 163 } 164 165 void 166 sdl_del(char *sdname) 167 { 168 int i, idx = -1; 169 170 for (i = 0; i < blu; i++) { 171 if (strcmp(blacklists[i].tag, sdname) == 0) { 172 idx = i; 173 break; 174 } 175 } 176 if (idx != -1) { 177 if (debug > 0) 178 printf("clearing list %s\n", sdname); 179 free(blacklists[idx].string); 180 free(blacklists[idx].addrs); 181 blacklists[idx].string = NULL; 182 blacklists[idx].addrs = NULL; 183 blacklists[idx].naddrs = 0; 184 } 185 } 186 187 /* 188 * Return 1 if the addresses a (with mask m) matches address b 189 * otherwise return 0. It is assumed that address a has been 190 * pre-masked out, we only need to mask b. 191 */ 192 int 193 match_addr(struct sdaddr *a, struct sdaddr *m, struct sdaddr *b, 194 sa_family_t af) 195 { 196 int match = 0; 197 198 switch (af) { 199 case AF_INET: 200 if ((a->addr32[0]) == 201 (b->addr32[0] & m->addr32[0])) 202 match++; 203 break; 204 case AF_INET6: 205 if (((a->addr32[0]) == 206 (b->addr32[0] & m->addr32[0])) && 207 ((a->addr32[1]) == 208 (b->addr32[1] & m->addr32[1])) && 209 ((a->addr32[2]) == 210 (b->addr32[2] & m->addr32[2])) && 211 ((a->addr32[3]) == 212 (b->addr32[3] & m->addr32[3]))) 213 match++; 214 break; 215 } 216 return (match); 217 } 218 219 220 /* 221 * Given an address and address family 222 * return list of pointers to matching nodes. or NULL if none. 223 */ 224 struct sdlist ** 225 sdl_lookup(struct sdlist *head, int af, void * src) 226 { 227 int i, matches = 0; 228 struct sdlist *sdl; 229 struct sdentry *sda; 230 struct sdaddr *source = (struct sdaddr *) src; 231 int sdnewlen = 0; 232 struct sdlist **sdnew = NULL; 233 234 if (head == NULL) 235 return (NULL); 236 else 237 sdl = head; 238 while (sdl->tag != NULL) { 239 for (i = 0; i < sdl->naddrs; i++) { 240 sda = sdl->addrs + i; 241 if (match_addr(&sda->sda, &sda->sdm, source, af)) { 242 if (matches == sdnewlen) { 243 struct sdlist **tmp; 244 245 tmp = realloc(sdnew, 246 (sdnewlen + 128) * 247 sizeof(struct sdlist *)); 248 if (tmp == NULL) 249 /* 250 * XXX out of memory - 251 * return what we have 252 */ 253 return (sdnew); 254 sdnew = tmp; 255 sdnewlen += 128; 256 } 257 sdnew[matches]= sdl; 258 matches++; 259 sdnew[matches]=NULL; 260 break; 261 } 262 } 263 sdl++; 264 } 265 return (sdnew); 266 } 267 268 static void 269 sdl_free(struct sdlist *sdl) 270 { 271 free(sdl->tag); 272 free(sdl->string); 273 free(sdl->addrs); 274 sdl_clear(sdl); 275 } 276 277 static void 278 sdl_clear(struct sdlist *sdl) 279 { 280 sdl->tag = NULL; 281 sdl->string = NULL; 282 sdl->addrs = NULL; 283 sdl->naddrs = 0; 284 } 285 286