1 /* $NetBSD: test-milter.c,v 1.1.1.3 2011/03/02 19:32:22 tron Exp $ */ 2 3 /*++ 4 /* NAME 5 /* test-milter 1 6 /* SUMMARY 7 /* Simple test mail filter program. 8 /* SYNOPSIS 9 /* .fi 10 /* \fBtest-milter\fR [\fIoptions\fR] -p \fBinet:\fIport\fB@\fIhost\fR 11 /* 12 /* \fBtest-milter\fR [\fIoptions\fR] -p \fBunix:\fIpathname\fR 13 /* DESCRIPTION 14 /* \fBtest-milter\fR is a Milter (mail filter) application that 15 /* exercises selected features. 16 /* 17 /* Note: this is an unsupported test program. No attempt is made 18 /* to maintain compatibility between successive versions. 19 /* 20 /* Arguments (multiple alternatives are separated by "\fB|\fR"): 21 /* .IP "\fB-a accept|tempfail|reject|discard|skip|\fIddd x.y.z text\fR" 22 /* Specifies a non-default reply for the MTA command specified 23 /* with \fB-c\fR. The default is \fBtempfail\fR. 24 /* .IP "\fB-A address\fR" 25 /* Add the specified recipient address. Multiple -A options 26 /* are supported. 27 /* .IP "\fB-b pathname 28 /* Replace the message body by the content of the specified file. 29 /* .IP "\fB-c connect|helo|mail|rcpt|data|header|eoh|body|eom|unknown|close|abort\fR" 30 /* When to send the non-default reply specified with \fB-a\fR. 31 /* The default protocol stage is \fBconnect\fR. 32 /* .IP "\fB-C\fI count\fR" 33 /* Terminate after \fIcount\fR connections. 34 /* .IP "\fB-d\fI level\fR" 35 /* Enable libmilter debugging at the specified level. 36 /* .IP "\fB-f \fIsender\fR 37 /* Replace the sender by the specified address. 38 /* .IP "\fB-h \fI'index header-label header-value'\fR" 39 /* Replace the message header at the specified position. 40 /* .IP "\fB-i \fI'index header-label header-value'\fR" 41 /* Insert header at specified position. 42 /* .IP "\fB-l\fR" 43 /* Header values include leading space. Specify this option 44 /* before \fB-i\fR or \fB-h\fR. 45 /* .IP "\fB-m connect|helo|mail|rcpt|data|eoh|eom\fR" 46 /* The protocol stage that receives the list of macros specified 47 /* with \fB-M\fR. The default protocol stage is \fBconnect\fR. 48 /* .IP "\fB-M \fIset_macro_list\fR" 49 /* A non-default list of macros that the MTA should send at 50 /* the protocol stage specified with \fB-m\fR. 51 /* .IP "\fB-n connect|helo|mail|rcpt|data|header|eoh|body|eom|unknown\fR" 52 /* The event that the MTA should not send. 53 /* .IP "\fB-N connect|helo|mail|rcpt|data|header|eoh|body|eom|unknown\fR" 54 /* The event for which the filter will not reply. 55 /* .IP "\fB-p inet:\fIport\fB@\fIhost\fB|unix:\fIpathname\fR" 56 /* The mail filter listen endpoint. 57 /* .IP "\fB-r\fR" 58 /* Request rejected recipients from the MTA. 59 /* .IP "\fB-v\fR" 60 /* Make the program more verbose. 61 /* LICENSE 62 /* .ad 63 /* .fi 64 /* The Secure Mailer license must be distributed with this software. 65 /* AUTHOR(S) 66 /* Wietse Venema 67 /* IBM T.J. Watson Research 68 /* P.O. Box 704 69 /* Yorktown Heights, NY 10598, USA 70 /*--*/ 71 72 #include <sys/types.h> 73 #include <sys/socket.h> 74 #include <netinet/in.h> 75 #include <sys/un.h> 76 #include <arpa/inet.h> 77 #include <errno.h> 78 #include <stdio.h> 79 #include <stdlib.h> 80 #include <unistd.h> 81 #include <string.h> 82 83 #include "libmilter/mfapi.h" 84 #include "libmilter/mfdef.h" 85 86 static int conn_count; 87 static int verbose; 88 89 static int test_connect_reply = SMFIS_CONTINUE; 90 static int test_helo_reply = SMFIS_CONTINUE; 91 static int test_mail_reply = SMFIS_CONTINUE; 92 static int test_rcpt_reply = SMFIS_CONTINUE; 93 94 #if SMFI_VERSION > 3 95 static int test_data_reply = SMFIS_CONTINUE; 96 97 #endif 98 static int test_header_reply = SMFIS_CONTINUE; 99 static int test_eoh_reply = SMFIS_CONTINUE; 100 static int test_body_reply = SMFIS_CONTINUE; 101 static int test_eom_reply = SMFIS_CONTINUE; 102 103 #if SMFI_VERSION > 2 104 static int test_unknown_reply = SMFIS_CONTINUE; 105 106 #endif 107 static int test_close_reply = SMFIS_CONTINUE; 108 static int test_abort_reply = SMFIS_CONTINUE; 109 110 struct command_map { 111 const char *name; 112 int *reply; 113 }; 114 115 static const struct command_map command_map[] = { 116 "connect", &test_connect_reply, 117 "helo", &test_helo_reply, 118 "mail", &test_mail_reply, 119 "rcpt", &test_rcpt_reply, 120 "header", &test_header_reply, 121 "eoh", &test_eoh_reply, 122 "body", &test_body_reply, 123 "eom", &test_eom_reply, 124 "abort", &test_abort_reply, 125 "close", &test_close_reply, 126 #if SMFI_VERSION > 2 127 "unknown", &test_unknown_reply, 128 #endif 129 #if SMFI_VERSION > 3 130 "data", &test_data_reply, 131 #endif 132 0, 0, 133 }; 134 135 static char *reply_code; 136 static char *reply_dsn; 137 static char *reply_message; 138 139 #ifdef SMFIR_CHGFROM 140 static char *chg_from; 141 142 #endif 143 144 #ifdef SMFIR_INSHEADER 145 static char *ins_hdr; 146 static int ins_idx; 147 static char *ins_val; 148 149 #endif 150 151 #ifdef SMFIR_CHGHEADER 152 static char *chg_hdr; 153 static int chg_idx; 154 static char *chg_val; 155 156 #endif 157 158 #ifdef SMFIR_REPLBODY 159 static char *body_file; 160 161 #endif 162 163 #define MAX_RCPT 10 164 int rcpt_count = 0; 165 char *rcpt_addr[MAX_RCPT]; 166 167 static const char *macro_names[] = { 168 "_", 169 "i", 170 "j", 171 "v", 172 "{auth_authen}", 173 "{auth_author}", 174 "{auth_type}", 175 "{cert_issuer}", 176 "{cert_subject}", 177 "{cipher}", 178 "{cipher_bits}", 179 "{client_addr}", 180 "{client_connections}", 181 "{client_name}", 182 "{client_port}", 183 "{client_ptr}", 184 "{client_resolve}", 185 "{daemon_name}", 186 "{if_addr}", 187 "{if_name}", 188 "{mail_addr}", 189 "{mail_host}", 190 "{mail_mailer}", 191 "{rcpt_addr}", 192 "{rcpt_host}", 193 "{rcpt_mailer}", 194 "{tls_version}", 195 0, 196 }; 197 198 static int test_reply(SMFICTX *ctx, int code) 199 { 200 const char **cpp; 201 const char *symval; 202 203 for (cpp = macro_names; *cpp; cpp++) 204 if ((symval = smfi_getsymval(ctx, (char *) *cpp)) != 0) 205 printf("macro: %s=\"%s\"\n", *cpp, symval); 206 (void) fflush(stdout); /* In case output redirected. */ 207 208 if (code == SMFIR_REPLYCODE) { 209 if (smfi_setmlreply(ctx, reply_code, reply_dsn, reply_message, reply_message, (char *) 0) == MI_FAILURE) 210 fprintf(stderr, "smfi_setmlreply failed\n"); 211 printf("test_reply %s\n", reply_code); 212 return (reply_code[0] == '4' ? SMFIS_TEMPFAIL : SMFIS_REJECT); 213 } else { 214 printf("test_reply %d\n\n", code); 215 return (code); 216 } 217 } 218 219 static sfsistat test_connect(SMFICTX *ctx, char *name, struct sockaddr * sa) 220 { 221 const char *print_addr; 222 char buf[BUFSIZ]; 223 224 printf("test_connect %s ", name); 225 switch (sa->sa_family) { 226 case AF_INET: 227 { 228 struct sockaddr_in *sin = (struct sockaddr_in *) sa; 229 230 print_addr = inet_ntop(AF_INET, &sin->sin_addr, buf, sizeof(buf)); 231 if (print_addr == 0) 232 print_addr = strerror(errno); 233 printf("AF_INET (%s:%d)\n", print_addr, ntohs(sin->sin_port)); 234 } 235 break; 236 #ifdef HAS_IPV6 237 case AF_INET6: 238 { 239 struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) sa; 240 241 print_addr = inet_ntop(AF_INET, &sin6->sin6_addr, buf, sizeof(buf)); 242 if (print_addr == 0) 243 print_addr = strerror(errno); 244 printf("AF_INET6 (%s:%d)\n", print_addr, ntohs(sin6->sin6_port)); 245 } 246 break; 247 #endif 248 case AF_UNIX: 249 { 250 #undef sun 251 struct sockaddr_un *sun = (struct sockaddr_un *) sa; 252 253 printf("AF_UNIX (%s)\n", sun->sun_path); 254 } 255 break; 256 default: 257 printf(" [unknown address family]\n"); 258 break; 259 } 260 return (test_reply(ctx, test_connect_reply)); 261 } 262 263 static sfsistat test_helo(SMFICTX *ctx, char *arg) 264 { 265 printf("test_helo \"%s\"\n", arg ? arg : "NULL"); 266 return (test_reply(ctx, test_helo_reply)); 267 } 268 269 static sfsistat test_mail(SMFICTX *ctx, char **argv) 270 { 271 char **cpp; 272 273 printf("test_mail"); 274 for (cpp = argv; *cpp; cpp++) 275 printf(" \"%s\"", *cpp); 276 printf("\n"); 277 return (test_reply(ctx, test_mail_reply)); 278 } 279 280 static sfsistat test_rcpt(SMFICTX *ctx, char **argv) 281 { 282 char **cpp; 283 284 printf("test_rcpt"); 285 for (cpp = argv; *cpp; cpp++) 286 printf(" \"%s\"", *cpp); 287 printf("\n"); 288 return (test_reply(ctx, test_rcpt_reply)); 289 } 290 291 292 sfsistat test_header(SMFICTX *ctx, char *name, char *value) 293 { 294 printf("test_header \"%s\" \"%s\"\n", name, value); 295 return (test_reply(ctx, test_header_reply)); 296 } 297 298 static sfsistat test_eoh(SMFICTX *ctx) 299 { 300 printf("test_eoh\n"); 301 return (test_reply(ctx, test_eoh_reply)); 302 } 303 304 static sfsistat test_body(SMFICTX *ctx, unsigned char *data, size_t data_len) 305 { 306 if (verbose == 0) 307 printf("test_body %ld bytes\n", (long) data_len); 308 else 309 printf("%.*s", (int) data_len, data); 310 return (test_reply(ctx, test_body_reply)); 311 } 312 313 static sfsistat test_eom(SMFICTX *ctx) 314 { 315 printf("test_eom\n"); 316 #ifdef SMFIR_REPLBODY 317 if (body_file) { 318 char buf[BUFSIZ + 2]; 319 FILE *fp; 320 size_t len; 321 int count; 322 323 if ((fp = fopen(body_file, "r")) == 0) { 324 perror(body_file); 325 } else { 326 printf("replace body with content of %s\n", body_file); 327 for (count = 0; fgets(buf, BUFSIZ, fp) != 0; count++) { 328 len = strcspn(buf, "\n"); 329 buf[len + 0] = '\r'; 330 buf[len + 1] = '\n'; 331 if (smfi_replacebody(ctx, buf, len + 2) == MI_FAILURE) { 332 fprintf(stderr, "body replace failure\n"); 333 exit(1); 334 } 335 if (verbose) 336 printf("%.*s\n", (int) len, buf); 337 } 338 if (count == 0) 339 perror("fgets"); 340 (void) fclose(fp); 341 } 342 } 343 #endif 344 #ifdef SMFIR_CHGFROM 345 if (chg_from != 0 && smfi_chgfrom(ctx, chg_from, "whatever") == MI_FAILURE) 346 fprintf(stderr, "smfi_chgfrom failed\n"); 347 #endif 348 #ifdef SMFIR_INSHEADER 349 if (ins_hdr && smfi_insheader(ctx, ins_idx, ins_hdr, ins_val) == MI_FAILURE) 350 fprintf(stderr, "smfi_insheader failed\n"); 351 #endif 352 #ifdef SMFIR_CHGHEADER 353 if (chg_hdr && smfi_chgheader(ctx, chg_hdr, chg_idx, chg_val) == MI_FAILURE) 354 fprintf(stderr, "smfi_chgheader failed\n"); 355 #endif 356 { 357 int count; 358 359 for (count = 0; count < rcpt_count; count++) 360 if (smfi_addrcpt(ctx, rcpt_addr[count]) == MI_FAILURE) 361 fprintf(stderr, "smfi_addrcpt `%s' failed\n", rcpt_addr[count]); 362 } 363 return (test_reply(ctx, test_eom_reply)); 364 } 365 366 static sfsistat test_abort(SMFICTX *ctx) 367 { 368 printf("test_abort\n"); 369 return (test_reply(ctx, test_abort_reply)); 370 } 371 372 static sfsistat test_close(SMFICTX *ctx) 373 { 374 printf("test_close\n"); 375 if (verbose) 376 printf("conn_count %d\n", conn_count); 377 if (conn_count > 0 && --conn_count == 0) 378 exit(0); 379 return (test_reply(ctx, test_close_reply)); 380 } 381 382 #if SMFI_VERSION > 3 383 384 static sfsistat test_data(SMFICTX *ctx) 385 { 386 printf("test_data\n"); 387 return (test_reply(ctx, test_data_reply)); 388 } 389 390 #endif 391 392 #if SMFI_VERSION > 2 393 394 static sfsistat test_unknown(SMFICTX *ctx, const char *what) 395 { 396 printf("test_unknown %s\n", what); 397 return (test_reply(ctx, test_unknown_reply)); 398 } 399 400 #endif 401 402 #if SMFI_VERSION > 5 403 404 static sfsistat test_negotiate(SMFICTX *, unsigned long, unsigned long, 405 unsigned long, unsigned long, 406 unsigned long *, unsigned long *, 407 unsigned long *, unsigned long *); 408 409 #endif 410 411 #ifndef SMFIF_CHGFROM 412 #define SMFIF_CHGFROM 0 413 #endif 414 #ifndef SMFIP_HDR_LEADSPC 415 #define SMFIP_HDR_LEADSPC 0 416 #define misc_mask 0 417 #endif 418 419 static struct smfiDesc smfilter = 420 { 421 "test-milter", 422 SMFI_VERSION, 423 SMFIF_ADDRCPT | SMFIF_DELRCPT | SMFIF_ADDHDRS | SMFIF_CHGHDRS | SMFIF_CHGBODY | SMFIF_CHGFROM, 424 test_connect, 425 test_helo, 426 test_mail, 427 test_rcpt, 428 test_header, 429 test_eoh, 430 test_body, 431 test_eom, 432 test_abort, 433 test_close, 434 #if SMFI_VERSION > 2 435 test_unknown, 436 #endif 437 #if SMFI_VERSION > 3 438 test_data, 439 #endif 440 #if SMFI_VERSION > 5 441 test_negotiate, 442 #endif 443 }; 444 445 #if SMFI_VERSION > 5 446 447 static const char *macro_states[] = { 448 "connect", /* SMFIM_CONNECT */ 449 "helo", /* SMFIM_HELO */ 450 "mail", /* SMFIM_ENVFROM */ 451 "rcpt", /* SMFIM_ENVRCPT */ 452 "data", /* SMFIM_DATA */ 453 "eom", /* SMFIM_EOM < SMFIM_EOH */ 454 "eoh", /* SMFIM_EOH > SMFIM_EOM */ 455 0, 456 }; 457 458 static int set_macro_state; 459 static char *set_macro_list; 460 461 typedef sfsistat (*FILTER_ACTION) (); 462 463 struct noproto_map { 464 const char *name; 465 int send_mask; 466 int reply_mask; 467 int *reply; 468 FILTER_ACTION *action; 469 }; 470 471 static const struct noproto_map noproto_map[] = { 472 "connect", SMFIP_NOCONNECT, SMFIP_NR_CONN, &test_connect_reply, &smfilter.xxfi_connect, 473 "helo", SMFIP_NOHELO, SMFIP_NR_HELO, &test_helo_reply, &smfilter.xxfi_helo, 474 "mail", SMFIP_NOMAIL, SMFIP_NR_MAIL, &test_mail_reply, &smfilter.xxfi_envfrom, 475 "rcpt", SMFIP_NORCPT, SMFIP_NR_RCPT, &test_rcpt_reply, &smfilter.xxfi_envrcpt, 476 "data", SMFIP_NODATA, SMFIP_NR_DATA, &test_data_reply, &smfilter.xxfi_data, 477 "header", SMFIP_NOHDRS, SMFIP_NR_HDR, &test_header_reply, &smfilter.xxfi_header, 478 "eoh", SMFIP_NOEOH, SMFIP_NR_EOH, &test_eoh_reply, &smfilter.xxfi_eoh, 479 "body", SMFIP_NOBODY, SMFIP_NR_BODY, &test_body_reply, &smfilter.xxfi_body, 480 "unknown", SMFIP_NOUNKNOWN, SMFIP_NR_UNKN, &test_connect_reply, &smfilter.xxfi_unknown, 481 0, 482 }; 483 484 static int nosend_mask; 485 static int noreply_mask; 486 static int misc_mask; 487 488 static sfsistat test_negotiate(SMFICTX *ctx, 489 unsigned long f0, 490 unsigned long f1, 491 unsigned long f2, 492 unsigned long f3, 493 unsigned long *pf0, 494 unsigned long *pf1, 495 unsigned long *pf2, 496 unsigned long *pf3) 497 { 498 if (set_macro_list) { 499 if (verbose) 500 printf("set symbol list %s to \"%s\"\n", 501 macro_states[set_macro_state], set_macro_list); 502 smfi_setsymlist(ctx, set_macro_state, set_macro_list); 503 } 504 if (verbose) 505 printf("negotiate f0=%lx *pf0 = %lx f1=%lx *pf1=%lx nosend=%lx noreply=%lx misc=%lx\n", 506 f0, *pf0, f1, *pf1, (long) nosend_mask, (long) noreply_mask, (long) misc_mask); 507 *pf0 = f0; 508 *pf1 = f1 & (nosend_mask | noreply_mask | misc_mask); 509 return (SMFIS_CONTINUE); 510 } 511 512 #endif 513 514 static void parse_hdr_info(const char *optarg, int *idx, 515 char **hdr, char **value) 516 { 517 int len; 518 519 len = strlen(optarg) + 1; 520 if ((*hdr = malloc(len)) == 0 || (*value = malloc(len)) == 0) { 521 fprintf(stderr, "out of memory\n"); 522 exit(1); 523 } 524 if ((misc_mask & SMFIP_HDR_LEADSPC) == 0 ? 525 sscanf(optarg, "%d %s %[^\n]", idx, *hdr, *value) != 3 : 526 sscanf(optarg, "%d %[^ ]%[^\n]", idx, *hdr, *value) != 3) { 527 fprintf(stderr, "bad header info: %s\n", optarg); 528 exit(1); 529 } 530 } 531 532 int main(int argc, char **argv) 533 { 534 char *action = 0; 535 char *command = 0; 536 const struct command_map *cp; 537 int ch; 538 int code; 539 const char **cpp; 540 char *set_macro_state_arg = 0; 541 char *nosend = 0; 542 char *noreply = 0; 543 const struct noproto_map *np; 544 545 while ((ch = getopt(argc, argv, "a:A:b:c:C:d:f:h:i:lm:M:n:N:p:rv")) > 0) { 546 switch (ch) { 547 case 'a': 548 action = optarg; 549 break; 550 case 'A': 551 if (rcpt_count >= MAX_RCPT) { 552 fprintf(stderr, "too many -A options\n"); 553 exit(1); 554 } 555 rcpt_addr[rcpt_count++] = optarg; 556 break; 557 case 'b': 558 #ifdef SMFIR_REPLBODY 559 if (body_file) { 560 fprintf(stderr, "too many -b options\n"); 561 exit(1); 562 } 563 body_file = optarg; 564 #else 565 fprintf(stderr, "no libmilter support to replace body\n"); 566 #endif 567 break; 568 case 'c': 569 command = optarg; 570 break; 571 case 'd': 572 if (smfi_setdbg(atoi(optarg)) == MI_FAILURE) { 573 fprintf(stderr, "smfi_setdbg failed\n"); 574 exit(1); 575 } 576 break; 577 case 'f': 578 #ifdef SMFIR_CHGFROM 579 if (chg_from) { 580 fprintf(stderr, "too many -f options\n"); 581 exit(1); 582 } 583 chg_from = optarg; 584 #else 585 fprintf(stderr, "no libmilter support to change sender\n"); 586 exit(1); 587 #endif 588 break; 589 case 'h': 590 #ifdef SMFIR_CHGHEADER 591 if (chg_hdr) { 592 fprintf(stderr, "too many -h options\n"); 593 exit(1); 594 } 595 parse_hdr_info(optarg, &chg_idx, &chg_hdr, &chg_val); 596 #else 597 fprintf(stderr, "no libmilter support to change header\n"); 598 exit(1); 599 #endif 600 break; 601 case 'i': 602 #ifdef SMFIR_INSHEADER 603 if (ins_hdr) { 604 fprintf(stderr, "too many -i options\n"); 605 exit(1); 606 } 607 parse_hdr_info(optarg, &ins_idx, &ins_hdr, &ins_val); 608 #else 609 fprintf(stderr, "no libmilter support to insert header\n"); 610 exit(1); 611 #endif 612 break; 613 case 'l': 614 #if SMFI_VERSION > 5 615 if (ins_hdr || chg_hdr) { 616 fprintf(stderr, "specify -l before -i or -r\n"); 617 exit(1); 618 } 619 misc_mask |= SMFIP_HDR_LEADSPC; 620 #else 621 fprintf(stderr, "no libmilter support for leading space\n"); 622 exit(1); 623 #endif 624 break; 625 case 'm': 626 #if SMFI_VERSION > 5 627 if (set_macro_state_arg) { 628 fprintf(stderr, "too many -m options\n"); 629 exit(1); 630 } 631 set_macro_state_arg = optarg; 632 #else 633 fprintf(stderr, "no libmilter support to specify macro list\n"); 634 exit(1); 635 #endif 636 break; 637 case 'M': 638 #if SMFI_VERSION > 5 639 if (set_macro_list) { 640 fprintf(stderr, "too many -M options\n"); 641 exit(1); 642 } 643 set_macro_list = optarg; 644 #else 645 fprintf(stderr, "no libmilter support to specify macro list\n"); 646 #endif 647 break; 648 case 'n': 649 #if SMFI_VERSION > 5 650 if (nosend) { 651 fprintf(stderr, "too many -n options\n"); 652 exit(1); 653 } 654 nosend = optarg; 655 #else 656 fprintf(stderr, "no libmilter support for negotiate callback\n"); 657 #endif 658 break; 659 case 'N': 660 #if SMFI_VERSION > 5 661 if (noreply) { 662 fprintf(stderr, "too many -n options\n"); 663 exit(1); 664 } 665 noreply = optarg; 666 #else 667 fprintf(stderr, "no libmilter support for negotiate callback\n"); 668 #endif 669 break; 670 case 'p': 671 if (smfi_setconn(optarg) == MI_FAILURE) { 672 fprintf(stderr, "smfi_setconn failed\n"); 673 exit(1); 674 } 675 break; 676 case 'r': 677 #ifdef SMFIP_RCPT_REJ 678 misc_mask |= SMFIP_RCPT_REJ; 679 #else 680 fprintf(stderr, "no libmilter support for rejected recipients\n"); 681 #endif 682 break; 683 case 'v': 684 verbose++; 685 break; 686 case 'C': 687 conn_count = atoi(optarg); 688 break; 689 default: 690 fprintf(stderr, 691 "usage: %s [-dv] \n" 692 "\t[-a action] non-default action\n" 693 "\t[-b body_text] replace body\n", 694 "\t[-c command] non-default action trigger\n" 695 "\t[-h 'index label value'] replace header\n" 696 "\t[-i 'index label value'] insert header\n" 697 "\t[-m macro_state] non-default macro state\n" 698 "\t[-M macro_list] non-default macro list\n" 699 "\t[-n events] don't receive these events\n" 700 "\t[-N events] don't reply to these events\n" 701 "\t-p port milter application\n" 702 "\t-r request rejected recipients\n" 703 "\t[-C conn_count] when to exit\n", 704 argv[0]); 705 exit(1); 706 } 707 } 708 if (command) { 709 for (cp = command_map; /* see below */ ; cp++) { 710 if (cp->name == 0) { 711 fprintf(stderr, "bad -c argument: %s\n", command); 712 exit(1); 713 } 714 if (strcmp(command, cp->name) == 0) 715 break; 716 } 717 } 718 if (action) { 719 if (command == 0) 720 cp = command_map; 721 if (strcmp(action, "tempfail") == 0) { 722 cp->reply[0] = SMFIS_TEMPFAIL; 723 } else if (strcmp(action, "reject") == 0) { 724 cp->reply[0] = SMFIS_REJECT; 725 } else if (strcmp(action, "accept") == 0) { 726 cp->reply[0] = SMFIS_ACCEPT; 727 } else if (strcmp(action, "discard") == 0) { 728 cp->reply[0] = SMFIS_DISCARD; 729 #ifdef SMFIS_SKIP 730 } else if (strcmp(action, "skip") == 0) { 731 cp->reply[0] = SMFIS_SKIP; 732 #endif 733 } else if ((code = atoi(action)) >= 400 734 && code <= 599 735 && action[3] == ' ') { 736 cp->reply[0] = SMFIR_REPLYCODE; 737 reply_code = action; 738 reply_dsn = action + 3; 739 if (*reply_dsn != 0) { 740 *reply_dsn++ = 0; 741 reply_dsn += strspn(reply_dsn, " "); 742 } 743 if (*reply_dsn == 0) { 744 reply_dsn = reply_message = 0; 745 } else { 746 reply_message = reply_dsn + strcspn(reply_dsn, " "); 747 if (*reply_message != 0) { 748 *reply_message++ = 0; 749 reply_message += strspn(reply_message, " "); 750 } 751 if (*reply_message == 0) 752 reply_message = 0; 753 } 754 } else { 755 fprintf(stderr, "bad -a argument: %s\n", action); 756 exit(1); 757 } 758 if (verbose) { 759 printf("command %s action %d\n", cp->name, cp->reply[0]); 760 if (reply_code) 761 printf("reply code %s dsn %s message %s\n", 762 reply_code, reply_dsn ? reply_dsn : "(null)", 763 reply_message ? reply_message : "(null)"); 764 } 765 } 766 #if SMFI_VERSION > 5 767 if (set_macro_state_arg) { 768 for (cpp = macro_states; /* see below */ ; cpp++) { 769 if (*cpp == 0) { 770 fprintf(stderr, "bad -m argument: %s\n", set_macro_state_arg); 771 exit(1); 772 } 773 if (strcmp(set_macro_state_arg, *cpp) == 0) 774 break; 775 } 776 set_macro_state = cpp - macro_states; 777 } 778 if (nosend) { 779 for (np = noproto_map; /* see below */ ; np++) { 780 if (np->name == 0) { 781 fprintf(stderr, "bad -n argument: %s\n", nosend); 782 exit(1); 783 } 784 if (strcmp(nosend, np->name) == 0) 785 break; 786 } 787 nosend_mask = np->send_mask; 788 np->action[0] = 0; 789 } 790 if (noreply) { 791 for (np = noproto_map; /* see below */ ; np++) { 792 if (np->name == 0) { 793 fprintf(stderr, "bad -N argument: %s\n", noreply); 794 exit(1); 795 } 796 if (strcmp(noreply, np->name) == 0) 797 break; 798 } 799 noreply_mask = np->reply_mask; 800 *np->reply = SMFIS_NOREPLY; 801 } 802 #endif 803 if (smfi_register(smfilter) == MI_FAILURE) { 804 fprintf(stderr, "smfi_register failed\n"); 805 exit(1); 806 } 807 return (smfi_main()); 808 } 809