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