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