1 /* $OpenBSD: spamd-setup.c,v 1.27 2006/05/11 15:37:21 dhill Exp $ */ 2 3 /* 4 * Copyright (c) 2003 Bob Beck. All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27 #include <sys/param.h> 28 #include <sys/socket.h> 29 #include <netinet/in.h> 30 #include <arpa/inet.h> 31 #include <errno.h> 32 #include <fcntl.h> 33 #include <stdio.h> 34 #include <stdlib.h> 35 #include <string.h> 36 #include <unistd.h> 37 #include <err.h> 38 #include <netinet/ip_ipsp.h> 39 #include <netdb.h> 40 #include <zlib.h> 41 42 #define PATH_FTP "/usr/bin/ftp" 43 #define PATH_PFCTL "/sbin/pfctl" 44 #define PATH_SPAMD_CONF "/etc/spamd.conf" 45 #define SPAMD_ARG_MAX 256 /* max # of args to an exec */ 46 47 struct cidr { 48 u_int32_t addr; 49 u_int8_t bits; 50 }; 51 52 struct bl { 53 u_int32_t addr; 54 int8_t b; 55 int8_t w; 56 }; 57 58 struct blacklist { 59 char *name; 60 char *message; 61 struct bl *bl; 62 size_t blc, bls; 63 u_int8_t black; 64 int count; 65 }; 66 67 u_int32_t imask(u_int8_t); 68 u_int8_t maxblock(u_int32_t, u_int8_t); 69 u_int8_t maxdiff(u_int32_t, u_int32_t); 70 struct cidr *range2cidrlist(u_int32_t, u_int32_t); 71 void cidr2range(struct cidr, u_int32_t *, u_int32_t *); 72 char *atop(u_int32_t); 73 int parse_netblock(char *, struct bl *, struct bl *, int); 74 int open_child(char *, char **); 75 int fileget(char *); 76 int open_file(char *, char *); 77 char *fix_quoted_colons(char *); 78 void do_message(FILE *, char *); 79 struct bl *add_blacklist(struct bl *, size_t *, size_t *, gzFile, int); 80 int cmpbl(const void *, const void *); 81 struct cidr **collapse_blacklist(struct bl *, size_t); 82 int configure_spamd(u_short, char *, char *, struct cidr **); 83 int configure_pf(struct cidr **); 84 int getlist(char **, char *, struct blacklist *, struct blacklist *); 85 86 int debug; 87 int dryrun; 88 89 u_int32_t 90 imask(u_int8_t b) 91 { 92 u_int32_t j = 0; 93 int i; 94 95 for (i = 31; i > 31 - b; --i) 96 j |= (1 << i); 97 return (j); 98 } 99 100 u_int8_t 101 maxblock(u_int32_t addr, u_int8_t bits) 102 { 103 u_int32_t m; 104 105 while (bits > 0) { 106 m = imask(bits - 1); 107 108 if ((addr & m) != addr) 109 return (bits); 110 bits--; 111 } 112 return (bits); 113 } 114 115 u_int8_t 116 maxdiff(u_int32_t a, u_int32_t b) 117 { 118 u_int8_t bits = 0; 119 u_int32_t m; 120 121 b++; 122 while (bits < 32) { 123 m = imask(bits); 124 125 if ((a & m) != (b & m)) 126 return (bits); 127 bits++; 128 } 129 return (bits); 130 } 131 132 struct cidr * 133 range2cidrlist(u_int32_t start, u_int32_t end) 134 { 135 struct cidr *list = NULL; 136 size_t cs = 0, cu = 0; 137 u_int8_t maxsize, diff; 138 struct cidr *tmp; 139 140 while (end >= start) { 141 maxsize = maxblock(start, 32); 142 diff = maxdiff(start, end); 143 144 maxsize = MAX(maxsize, diff); 145 if (cs == cu) { 146 tmp = realloc(list, (cs + 32) * sizeof(struct cidr)); 147 if (tmp == NULL) 148 errx(1, "malloc failed"); 149 list = tmp; 150 cs += 32; 151 } 152 list[cu].addr = start; 153 list[cu].bits = maxsize; 154 cu++; 155 list[cu].addr = 0; 156 list[cu].bits = 0; 157 start = start + (1 << (32 - maxsize)); 158 } 159 return (list); 160 } 161 162 void 163 cidr2range(struct cidr cidr, u_int32_t *start, u_int32_t *end) 164 { 165 *start = cidr.addr; 166 *end = cidr.addr + (1 << (32 - cidr.bits)) - 1; 167 } 168 169 char * 170 atop(u_int32_t addr) 171 { 172 struct in_addr in; 173 174 memset(&in, 0, sizeof(in)); 175 in.s_addr = htonl(addr); 176 return (inet_ntoa(in)); 177 } 178 179 int 180 parse_netblock(char *buf, struct bl *start, struct bl *end, int white) 181 { 182 char astring[16], astring2[16]; 183 unsigned maskbits; 184 struct cidr c; 185 186 /* skip leading spaces */ 187 while (*buf == ' ') 188 buf++; 189 /* bail if it's a comment */ 190 if (*buf == '#') 191 return (0); 192 /* otherwise, look for a netblock of some sort */ 193 if (sscanf(buf, "%15[^/]/%u", astring, &maskbits) == 2) { 194 /* looks like a cidr */ 195 memset(&c.addr, 0, sizeof(c.addr)); 196 if (inet_net_pton(AF_INET, astring, &c.addr, sizeof(c.addr)) 197 == -1) 198 return (0); 199 c.addr = ntohl(c.addr); 200 if (maskbits > 32) 201 return (0); 202 c.bits = maskbits; 203 cidr2range(c, &start->addr, &end->addr); 204 end->addr += 1; 205 } else if (sscanf(buf, "%15[0123456789.]%*[ -]%15[0123456789.]", 206 astring, astring2) == 2) { 207 /* looks like start - end */ 208 memset(&start->addr, 0, sizeof(start->addr)); 209 memset(&end->addr, 0, sizeof(end->addr)); 210 if (inet_net_pton(AF_INET, astring, &start->addr, 211 sizeof(start->addr)) == -1) 212 return (0); 213 start->addr = ntohl(start->addr); 214 if (inet_net_pton(AF_INET, astring2, &end->addr, 215 sizeof(end->addr)) == -1) 216 return (0); 217 end->addr = ntohl(end->addr) + 1; 218 if (start > end) 219 return (0); 220 } else if (sscanf(buf, "%15[0123456789.]", astring) == 1) { 221 /* just a single address */ 222 memset(&start->addr, 0, sizeof(start->addr)); 223 if (inet_net_pton(AF_INET, astring, &start->addr, 224 sizeof(start->addr)) == -1) 225 return (0); 226 start->addr = ntohl(start->addr); 227 end->addr = start->addr + 1; 228 } else 229 return (0); 230 231 if (white) { 232 start->b = 0; 233 start->w = 1; 234 end->b = 0; 235 end->w = -1; 236 } else { 237 start->b = 1; 238 start->w = 0; 239 end->b = -1; 240 end->w = 0; 241 } 242 return (1); 243 } 244 245 int 246 open_child(char *file, char **argv) 247 { 248 int pdes[2]; 249 250 if (pipe(pdes) != 0) 251 return (-1); 252 switch (fork()) { 253 case -1: 254 close(pdes[0]); 255 close(pdes[1]); 256 return (-1); 257 case 0: 258 /* child */ 259 close(pdes[0]); 260 if (pdes[1] != STDOUT_FILENO) { 261 dup2(pdes[1], STDOUT_FILENO); 262 close(pdes[1]); 263 } 264 execvp(file, argv); 265 _exit(1); 266 } 267 268 /* parent */ 269 close(pdes[1]); 270 return (pdes[0]); 271 } 272 273 int 274 fileget(char *url) 275 { 276 char *argv[6]; 277 278 argv[0] = "ftp"; 279 argv[1] = "-V"; 280 argv[2] = "-o"; 281 argv[3] = "-"; 282 argv[4] = url; 283 argv[5] = NULL; 284 285 if (debug) 286 fprintf(stderr, "Getting %s\n", url); 287 288 return (open_child(PATH_FTP, argv)); 289 } 290 291 int 292 open_file(char *method, char *file) 293 { 294 char *url; 295 char **ap, **argv; 296 int len, i, oerrno; 297 298 if ((method == NULL) || (strcmp(method, "file") == 0)) 299 return (open(file, O_RDONLY)); 300 if ((strcmp(method, "http") == 0) || 301 strcmp(method, "ftp") == 0) { 302 asprintf(&url, "%s://%s", method, file); 303 if (url == NULL) 304 return (-1); 305 i = fileget(url); 306 free(url); 307 return (i); 308 } else if (strcmp(method, "exec") == 0) { 309 len = strlen(file); 310 argv = malloc(len * sizeof(char *)); 311 if (argv == NULL) 312 errx(1, "malloc failed"); 313 for (ap = argv; ap < &argv[len - 1] && 314 (*ap = strsep(&file, " \t")) != NULL;) { 315 if (**ap != '\0') 316 ap++; 317 } 318 *ap = NULL; 319 i = open_child(argv[0], argv); 320 oerrno = errno; 321 free(argv); 322 errno = oerrno; 323 return (i); 324 } 325 errx(1, "Unknown method %s", method); 326 return (-1); /* NOTREACHED */ 327 } 328 329 /* 330 * fix_quoted_colons walks through a buffer returned by cgetent. We 331 * look for quoted strings, to escape colons (:) in quoted strings for 332 * getcap by replacing them with \C so cgetstr() deals with it correctly 333 * without having to see the \C bletchery in a configuration file that 334 * needs to have urls in it. Frees the buffer passed to it, passes back 335 * another larger one, with can be used with cgetxxx(), like the original 336 * buffer, it must be freed by the caller. 337 * This should really be a temporary fix until there is a sanctioned 338 * way to make getcap(3) handle quoted strings like this in a nicer 339 * way. 340 */ 341 char * 342 fix_quoted_colons(char *buf) 343 { 344 int in = 0; 345 size_t nbs, i, j = 0; 346 char *newbuf, last, *tmp; 347 348 nbs = strlen(buf) + 128; 349 newbuf = malloc(nbs); 350 if (newbuf == NULL) 351 return (NULL); 352 last = '\0'; 353 for (i = 0; i < strlen(buf); i++) { 354 switch (buf[i]) { 355 case ':': 356 if (in) { 357 newbuf[j++] = '\\'; 358 newbuf[j++] = 'C'; 359 } else 360 newbuf[j++] = buf[i]; 361 break; 362 case '"': 363 if (last != '\\') 364 in = !in; 365 newbuf[j++] = buf[i]; 366 break; 367 default: 368 newbuf[j++] = buf[i]; 369 } 370 if (j == nbs) { 371 nbs += 128; 372 tmp = realloc(newbuf, nbs); 373 if (tmp == NULL) 374 errx(1, "malloc failed"); 375 newbuf = tmp; 376 } 377 } 378 free(buf); 379 newbuf[j] = '\0'; 380 return (newbuf); 381 } 382 383 void 384 do_message(FILE *sdc, char *msg) 385 { 386 size_t i, bs = 0, bu = 0, len; 387 ssize_t n; 388 char *buf = NULL, last, *tmp; 389 int fd; 390 391 len = strlen(msg); 392 if (msg[0] == '"' && msg[len - 1] == '"') { 393 /* quoted msg, escape newlines and send it out */ 394 msg[len - 1] = '\0'; 395 buf = msg + 1; 396 bu = len - 2; 397 goto sendit; 398 } else { 399 /* 400 * message isn't quoted - try to open a local 401 * file and read the message from it. 402 */ 403 fd = open(msg, O_RDONLY); 404 if (fd == -1) 405 err(1, "Can't open message from %s", msg); 406 for (;;) { 407 if (bu == bs) { 408 tmp = realloc(buf, bs + 8192); 409 if (tmp == NULL) 410 errx(1, "malloc failed"); 411 bs += 8192; 412 buf = tmp; 413 } 414 415 n = read(fd, buf + bu, bs - bu); 416 if (n == 0) { 417 goto sendit; 418 } else if (n == -1) { 419 err(1, "Can't read from %s", msg); 420 } else 421 bu += n; 422 } 423 buf[bu]='\0'; 424 } 425 sendit: 426 fprintf(sdc, ";\""); 427 last = '\0'; 428 for (i = 0; i < bu; i++) { 429 /* handle escaping the things spamd wants */ 430 switch (buf[i]) { 431 case 'n': 432 if (last == '\\') 433 fprintf(sdc, "\\\\n"); 434 else 435 fputc('n', sdc); 436 last = '\0'; 437 break; 438 case '\n': 439 fprintf(sdc, "\\n"); 440 last = '\0'; 441 break; 442 case '"': 443 fputc('\\', sdc); 444 /* FALLTHROUGH */ 445 default: 446 fputc(buf[i], sdc); 447 last = '\0'; 448 } 449 } 450 fputc('"', sdc); 451 if (bs != 0) 452 free(buf); 453 } 454 455 /* retrieve a list from fd. add to blacklist bl */ 456 struct bl * 457 add_blacklist(struct bl *bl, size_t *blc, size_t *bls, gzFile gzf, int white) 458 { 459 int i, n, start, bu = 0, bs = 0, serrno = 0; 460 char *buf = NULL, *tmp; 461 struct bl *blt; 462 463 for (;;) { 464 /* read in gzf, then parse */ 465 if (bu == bs) { 466 tmp = realloc(buf, bs + 8192 + 1); 467 if (tmp == NULL) { 468 free(buf); 469 buf = NULL; 470 bs = 0; 471 serrno = errno; 472 goto bldone; 473 } 474 bs += 8192; 475 buf = tmp; 476 } 477 478 n = gzread(gzf, buf + bu, bs - bu); 479 if (n == 0) 480 goto parse; 481 else if (n == -1) { 482 serrno = errno; 483 goto bldone; 484 } else 485 bu += n; 486 } 487 parse: 488 start = 0; 489 for (i = 0; i <= bu; i++) { 490 if (*blc == *bls) { 491 *bls += 1024; 492 blt = realloc(bl, *bls * sizeof(struct bl)); 493 if (blt == NULL) { 494 *bls -= 1024; 495 serrno = errno; 496 goto bldone; 497 } 498 bl = blt; 499 } 500 if (i == bu || buf[i] == '\n') { 501 buf[i] = '\0'; 502 if (parse_netblock(buf + start, 503 bl + *blc, bl + *blc + 1, white)) 504 *blc += 2; 505 start = i + 1; 506 } 507 } 508 if (bu == 0) 509 errno = EIO; 510 bldone: 511 if (buf) 512 free(buf); 513 if (serrno) 514 errno = serrno; 515 return (bl); 516 } 517 518 int 519 cmpbl(const void *a, const void *b) 520 { 521 if (((struct bl *)a)->addr > ((struct bl *) b)->addr) 522 return (1); 523 if (((struct bl *)a)->addr < ((struct bl *) b)->addr) 524 return (-1); 525 return (0); 526 } 527 528 /* 529 * collapse_blacklist takes blacklist/whitelist entries sorts, removes 530 * overlaps and whitelist portions, and returns netblocks to blacklist 531 * as lists of nonoverlapping cidr blocks suitable for feeding in 532 * printable form to pfctl or spamd. 533 */ 534 struct cidr ** 535 collapse_blacklist(struct bl *bl, size_t blc) 536 { 537 int bs = 0, ws = 0, state=0, cli, i; 538 u_int32_t bstart = 0; 539 struct cidr **cl; 540 int laststate; 541 u_int32_t addr; 542 543 if (blc == 0) 544 return (NULL); 545 cl = malloc((blc / 2) * sizeof(struct cidr)); 546 if (cl == NULL) { 547 return (NULL); 548 } 549 qsort(bl, blc, sizeof(struct bl), cmpbl); 550 cli = 0; 551 cl[cli] = NULL; 552 for (i = 0; i < blc;) { 553 laststate = state; 554 addr = bl[i].addr; 555 556 do { 557 bs += bl[i].b; 558 ws += bl[i].w; 559 i++; 560 } while (bl[i].addr == addr); 561 if (state == 1 && bs == 0) 562 state = 0; 563 else if (state == 0 && bs > 0) 564 state = 1; 565 if (ws > 0) 566 state = 0; 567 if (laststate == 0 && state == 1) { 568 /* start blacklist */ 569 bstart = addr; 570 } 571 if (laststate == 1 && state == 0) { 572 /* end blacklist */ 573 cl[cli++] = range2cidrlist(bstart, addr - 1); 574 cl[cli] = NULL; 575 } 576 laststate = state; 577 } 578 return (cl); 579 } 580 581 int 582 configure_spamd(u_short dport, char *name, char *message, 583 struct cidr **blacklists) 584 { 585 int lport = IPPORT_RESERVED - 1, s; 586 struct sockaddr_in sin; 587 FILE* sdc; 588 589 s = rresvport(&lport); 590 if (s == -1) 591 return (-1); 592 memset(&sin, 0, sizeof sin); 593 sin.sin_len = sizeof(sin); 594 sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); 595 sin.sin_family = AF_INET; 596 sin.sin_port = htons(dport); 597 if (connect(s, (struct sockaddr *)&sin, sizeof sin) == -1) 598 return (-1); 599 sdc = fdopen(s, "w"); 600 if (sdc == NULL) { 601 close(s); 602 return (-1); 603 } 604 fprintf(sdc, "%s", name); 605 do_message(sdc, message); 606 while (*blacklists != NULL) { 607 struct cidr *b = *blacklists; 608 while (b->addr != 0) { 609 fprintf(sdc, ";%s/%u", atop(b->addr), (b->bits)); 610 b++; 611 } 612 blacklists++; 613 } 614 fputc('\n', sdc); 615 fclose(sdc); 616 close(s); 617 return (0); 618 } 619 620 621 int 622 configure_pf(struct cidr **blacklists) 623 { 624 char *argv[9]= {"pfctl", "-q", "-t", "spamd", "-T", "replace", 625 "-f" "-", NULL}; 626 static FILE *pf = NULL; 627 int pdes[2]; 628 629 if (pf == NULL) { 630 if (pipe(pdes) != 0) 631 return (-1); 632 switch (fork()) { 633 case -1: 634 close(pdes[0]); 635 close(pdes[1]); 636 return (-1); 637 case 0: 638 /* child */ 639 close(pdes[1]); 640 if (pdes[0] != STDIN_FILENO) { 641 dup2(pdes[0], STDIN_FILENO); 642 close(pdes[0]); 643 } 644 execvp(PATH_PFCTL, argv); 645 _exit(1); 646 } 647 648 /* parent */ 649 close(pdes[0]); 650 pf = fdopen(pdes[1], "w"); 651 if (pf == NULL) { 652 close(pdes[1]); 653 return (-1); 654 } 655 } 656 while (*blacklists != NULL) { 657 struct cidr *b = *blacklists; 658 659 while (b->addr != 0) { 660 fprintf(pf, "%s/%u\n", atop(b->addr), (b->bits)); 661 b++; 662 } 663 blacklists++; 664 } 665 return (0); 666 } 667 668 int 669 getlist(char ** db_array, char *name, struct blacklist *blist, 670 struct blacklist *blistnew) 671 { 672 char *buf, *method, *file, *message; 673 int fd, black = 0; 674 size_t blc, bls; 675 struct bl *bl = NULL; 676 gzFile gzf; 677 678 if (cgetent(&buf, db_array, name) != 0) 679 err(1, "Can't find \"%s\" in spamd config", name); 680 buf = fix_quoted_colons(buf); 681 if (cgetcap(buf, "black", ':') != NULL) { 682 /* use new list */ 683 black = 1; 684 blc = blistnew->blc; 685 bls = blistnew->bls; 686 bl = blistnew->bl; 687 } else if (cgetcap(buf, "white", ':') != NULL) { 688 /* apply to most recent blacklist */ 689 black = 0; 690 blc = blist->blc; 691 bls = blist->bls; 692 bl = blist->bl; 693 } else 694 errx(1, "Must have \"black\" or \"white\" in %s", name); 695 696 switch (cgetstr(buf, "msg", &message)) { 697 case -1: 698 if (black) 699 errx(1, "No msg for blacklist \"%s\"", name); 700 break; 701 case -2: 702 errx(1, "malloc failed"); 703 } 704 705 switch (cgetstr(buf, "method", &method)) { 706 case -1: 707 method = NULL; 708 break; 709 case -2: 710 errx(1, "malloc failed"); 711 } 712 713 switch (cgetstr(buf, "file", &file)) { 714 case -1: 715 errx(1, "No file given for %slist %s", 716 black ? "black" : "white", name); 717 case -2: 718 errx(1, "malloc failed"); 719 default: 720 fd = open_file(method, file); 721 if (fd == -1) 722 err(1, "Can't open %s by %s method", 723 file, method ? method : "file"); 724 free(method); 725 free(file); 726 gzf = gzdopen(fd, "r"); 727 if (gzf == NULL) 728 errx(1, "gzdopen"); 729 } 730 bl = add_blacklist(bl, &blc, &bls, gzf, !black); 731 gzclose(gzf); 732 if (bl == NULL) { 733 warn("Could not add %slist %s", black ? "black" : "white", 734 name); 735 return (0); 736 } 737 if (black) { 738 blistnew->message = message; 739 blistnew->name = name; 740 blistnew->black = black; 741 blistnew->bl = bl; 742 blistnew->blc = blc; 743 blistnew->bls = bls; 744 } else { 745 /* whitelist applied to last active blacklist */ 746 blist->bl = bl; 747 blist->blc = blc; 748 blist->bls = bls; 749 } 750 if (debug) 751 fprintf(stderr, "%slist %s %zu entries\n", 752 black ? "black" : "white", name, blc / 2); 753 return (black); 754 } 755 756 int 757 main(int argc, char *argv[]) 758 { 759 size_t dbs, dbc, blc, bls, black, white; 760 char **db_array, *buf, *name; 761 struct blacklist *blists; 762 struct servent *ent; 763 int i, ch; 764 765 while ((ch = getopt(argc, argv, "nd")) != -1) { 766 switch (ch) { 767 case 'n': 768 dryrun = 1; 769 break; 770 case 'd': 771 debug = 1; 772 break; 773 default: 774 break; 775 } 776 } 777 778 if ((ent = getservbyname("spamd-cfg", "tcp")) == NULL) 779 errx(1, "cannot find service \"spamd-cfg\" in /etc/services"); 780 ent->s_port = ntohs(ent->s_port); 781 782 dbs = argc + 2; 783 dbc = 0; 784 db_array = calloc(dbs, sizeof(char *)); 785 if (db_array == NULL) 786 errx(1, "malloc failed"); 787 788 db_array[dbc]= PATH_SPAMD_CONF; 789 dbc++; 790 for (i = 1; i < argc; i++) 791 db_array[dbc++] = argv[i]; 792 793 blists = NULL; 794 blc = bls = 0; 795 if (cgetent(&buf, db_array, "all") != 0) 796 err(1, "Can't find \"all\" in spamd config"); 797 name = strsep(&buf, ": \t"); /* skip "all" at start */ 798 blc = 0; 799 while ((name = strsep(&buf, ": \t")) != NULL) { 800 if (*name) { 801 /* extract config in order specified in "all" tag */ 802 if (blc == bls) { 803 struct blacklist *tmp; 804 805 bls += 1024; 806 tmp = realloc(blists, 807 bls * sizeof(struct blacklist)); 808 if (tmp == NULL) 809 errx(1, "malloc failed"); 810 blists = tmp; 811 } 812 if (blc == 0) 813 black = white = 0; 814 else { 815 white = blc - 1; 816 black = blc; 817 } 818 memset(&blists[black], 0, sizeof(struct blacklist)); 819 blc += getlist(db_array, name, &blists[white], 820 &blists[black]); 821 } 822 } 823 for (i = 0; i < blc; i++) { 824 struct cidr **cidrs, **tmp; 825 826 if (blists[i].blc > 0) { 827 cidrs = collapse_blacklist(blists[i].bl, 828 blists[i].blc); 829 if (cidrs == NULL) 830 errx(1, "malloc failed"); 831 if (dryrun) 832 continue; 833 834 if (configure_spamd(ent->s_port, blists[i].name, 835 blists[i].message, cidrs) == -1) 836 err(1, "Can't connect to spamd on port %d", 837 ent->s_port); 838 if (configure_pf(cidrs) == -1) 839 err(1, "pfctl failed"); 840 tmp = cidrs; 841 while (*tmp != NULL) 842 free(*tmp++); 843 free(cidrs); 844 free(blists[i].bl); 845 } 846 } 847 return (0); 848 } 849