1 /* $NetBSD: test-milter.c,v 1.1.1.1 2009/06/23 10:08:50 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-r\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", 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 else 348 printf("smfi_chgfrom OK\n"); 349 #endif 350 #ifdef SMFIR_INSHEADER 351 if (ins_hdr && smfi_insheader(ctx, ins_idx, ins_hdr, ins_val) == MI_FAILURE) 352 fprintf(stderr, "smfi_insheader failed\n"); 353 #endif 354 #ifdef SMFIR_CHGHEADER 355 if (chg_hdr && smfi_chgheader(ctx, chg_hdr, chg_idx, chg_val) == MI_FAILURE) 356 fprintf(stderr, "smfi_chgheader failed\n"); 357 #endif 358 { 359 int count; 360 361 for (count = 0; count < rcpt_count; count++) 362 if (smfi_addrcpt(ctx, rcpt_addr[count]) == MI_FAILURE) 363 fprintf(stderr, "smfi_addrcpt `%s' failed\n", rcpt_addr[count]); 364 } 365 return (test_reply(ctx, test_eom_reply)); 366 } 367 368 static sfsistat test_abort(SMFICTX *ctx) 369 { 370 printf("test_abort\n"); 371 return (test_reply(ctx, test_abort_reply)); 372 } 373 374 static sfsistat test_close(SMFICTX *ctx) 375 { 376 printf("test_close\n"); 377 if (verbose) 378 printf("conn_count %d\n", conn_count); 379 if (conn_count > 0 && --conn_count == 0) 380 exit(0); 381 return (test_reply(ctx, test_close_reply)); 382 } 383 384 #if SMFI_VERSION > 3 385 386 static sfsistat test_data(SMFICTX *ctx) 387 { 388 printf("test_data\n"); 389 return (test_reply(ctx, test_data_reply)); 390 } 391 392 #endif 393 394 #if SMFI_VERSION > 2 395 396 static sfsistat test_unknown(SMFICTX *ctx, const char *what) 397 { 398 printf("test_unknown %s\n", what); 399 return (test_reply(ctx, test_unknown_reply)); 400 } 401 402 #endif 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 static struct smfiDesc smfilter = 410 { 411 "test-milter", 412 SMFI_VERSION, 413 SMFIF_ADDRCPT | SMFIF_DELRCPT | SMFIF_ADDHDRS | SMFIF_CHGHDRS | SMFIF_CHGBODY | SMFIF_CHGFROM, 414 test_connect, 415 test_helo, 416 test_mail, 417 test_rcpt, 418 test_header, 419 test_eoh, 420 test_body, 421 test_eom, 422 test_abort, 423 test_close, 424 #if SMFI_VERSION > 2 425 test_unknown, 426 #endif 427 #if SMFI_VERSION > 3 428 test_data, 429 #endif 430 #if SMFI_VERSION > 5 431 test_negotiate, 432 #endif 433 }; 434 435 #if SMFI_VERSION > 5 436 437 static const char *macro_states[] = { 438 "connect", /* SMFIM_CONNECT */ 439 "helo", /* SMFIM_HELO */ 440 "mail", /* SMFIM_ENVFROM */ 441 "rcpt", /* SMFIM_ENVRCPT */ 442 "data", /* SMFIM_DATA */ 443 "eom", /* SMFIM_EOM < SMFIM_EOH */ 444 "eoh", /* SMFIM_EOH > SMFIM_EOM */ 445 0, 446 }; 447 448 static int set_macro_state; 449 static char *set_macro_list; 450 451 typedef sfsistat (*FILTER_ACTION) (); 452 453 struct noproto_map { 454 const char *name; 455 int send_mask; 456 int reply_mask; 457 int *reply; 458 FILTER_ACTION *action; 459 }; 460 461 static const struct noproto_map noproto_map[] = { 462 "connect", SMFIP_NOCONNECT, SMFIP_NR_CONN, &test_connect_reply, &smfilter.xxfi_connect, 463 "helo", SMFIP_NOHELO, SMFIP_NR_HELO, &test_helo_reply, &smfilter.xxfi_helo, 464 "mail", SMFIP_NOMAIL, SMFIP_NR_MAIL, &test_mail_reply, &smfilter.xxfi_envfrom, 465 "rcpt", SMFIP_NORCPT, SMFIP_NR_RCPT, &test_rcpt_reply, &smfilter.xxfi_envrcpt, 466 "data", SMFIP_NODATA, SMFIP_NR_DATA, &test_data_reply, &smfilter.xxfi_data, 467 "header", SMFIP_NOHDRS, SMFIP_NR_HDR, &test_header_reply, &smfilter.xxfi_header, 468 "eoh", SMFIP_NOEOH, SMFIP_NR_EOH, &test_eoh_reply, &smfilter.xxfi_eoh, 469 "body", SMFIP_NOBODY, SMFIP_NR_BODY, &test_body_reply, &smfilter.xxfi_body, 470 "unknown", SMFIP_NOUNKNOWN, SMFIP_NR_UNKN, &test_connect_reply, &smfilter.xxfi_unknown, 471 0, 472 }; 473 474 static int nosend_mask; 475 static int noreply_mask; 476 static int misc_mask; 477 478 static sfsistat test_negotiate(SMFICTX *ctx, 479 unsigned long f0, 480 unsigned long f1, 481 unsigned long f2, 482 unsigned long f3, 483 unsigned long *pf0, 484 unsigned long *pf1, 485 unsigned long *pf2, 486 unsigned long *pf3) 487 { 488 if (set_macro_list) { 489 if (verbose) 490 printf("set symbol list %s to \"%s\"\n", 491 macro_states[set_macro_state], set_macro_list); 492 smfi_setsymlist(ctx, set_macro_state, set_macro_list); 493 } 494 if (verbose) 495 printf("negotiate f0=%lx *pf0 = %lx f1=%lx *pf1=%lx nosend=%lx noreply=%lx misc=%lx\n", 496 f0, *pf0, f1, *pf1, (long) nosend_mask, (long) noreply_mask, (long) misc_mask); 497 *pf0 = f0; 498 *pf1 = f1 & (nosend_mask | noreply_mask | misc_mask); 499 return (SMFIS_CONTINUE); 500 } 501 502 #endif 503 504 static void parse_hdr_info(const char *optarg, int *idx, 505 char **hdr, char **value) 506 { 507 int len; 508 509 len = strlen(optarg) + 1; 510 if ((*hdr = malloc(len)) == 0 || (*value = malloc(len)) == 0) { 511 fprintf(stderr, "out of memory\n"); 512 exit(1); 513 } 514 if ((misc_mask & SMFIP_HDR_LEADSPC) == 0 ? 515 sscanf(optarg, "%d %s %[^\n]", idx, *hdr, *value) != 3 : 516 sscanf(optarg, "%d %[^ ]%[^\n]", idx, *hdr, *value) != 3) { 517 fprintf(stderr, "bad header info: %s\n", optarg); 518 exit(1); 519 } 520 } 521 522 int main(int argc, char **argv) 523 { 524 char *action = 0; 525 char *command = 0; 526 const struct command_map *cp; 527 int ch; 528 int code; 529 const char **cpp; 530 char *set_macro_state_arg = 0; 531 char *nosend = 0; 532 char *noreply = 0; 533 const struct noproto_map *np; 534 535 while ((ch = getopt(argc, argv, "a:A:b:c:C:d:f:h:i:lm:M:n:N:p:rv")) > 0) { 536 switch (ch) { 537 case 'a': 538 action = optarg; 539 break; 540 case 'A': 541 if (rcpt_count >= MAX_RCPT) { 542 fprintf(stderr, "too many -A options\n"); 543 exit(1); 544 } 545 rcpt_addr[rcpt_count++] = optarg; 546 break; 547 case 'b': 548 #ifdef SMFIR_REPLBODY 549 if (body_file) { 550 fprintf(stderr, "too many -b options\n"); 551 exit(1); 552 } 553 body_file = optarg; 554 #else 555 fprintf(stderr, "no libmilter support to replace body\n"); 556 #endif 557 break; 558 case 'c': 559 command = optarg; 560 break; 561 case 'd': 562 if (smfi_setdbg(atoi(optarg)) == MI_FAILURE) { 563 fprintf(stderr, "smfi_setdbg failed\n"); 564 exit(1); 565 } 566 break; 567 case 'f': 568 #ifdef SMFIR_CHGFROM 569 if (chg_from) { 570 fprintf(stderr, "too many -f options\n"); 571 exit(1); 572 } 573 chg_from = optarg; 574 #else 575 fprintf(stderr, "no libmilter support to change sender\n"); 576 exit(1); 577 #endif 578 break; 579 case 'h': 580 #ifdef SMFIR_CHGHEADER 581 if (chg_hdr) { 582 fprintf(stderr, "too many -h options\n"); 583 exit(1); 584 } 585 parse_hdr_info(optarg, &chg_idx, &chg_hdr, &chg_val); 586 #else 587 fprintf(stderr, "no libmilter support to change header\n"); 588 exit(1); 589 #endif 590 break; 591 case 'i': 592 #ifdef SMFIR_INSHEADER 593 if (ins_hdr) { 594 fprintf(stderr, "too many -i options\n"); 595 exit(1); 596 } 597 parse_hdr_info(optarg, &ins_idx, &ins_hdr, &ins_val); 598 #else 599 fprintf(stderr, "no libmilter support to insert header\n"); 600 exit(1); 601 #endif 602 break; 603 case 'l': 604 #if SMFI_VERSION > 5 605 if (ins_hdr || chg_hdr) { 606 fprintf(stderr, "specify -l before -i or -r\n"); 607 exit(1); 608 } 609 misc_mask |= SMFIP_HDR_LEADSPC; 610 #else 611 fprintf(stderr, "no libmilter support for leading space\n"); 612 exit(1); 613 #endif 614 break; 615 case 'm': 616 #if SMFI_VERSION > 5 617 if (set_macro_state_arg) { 618 fprintf(stderr, "too many -m options\n"); 619 exit(1); 620 } 621 set_macro_state_arg = optarg; 622 #else 623 fprintf(stderr, "no libmilter support to specify macro list\n"); 624 exit(1); 625 #endif 626 break; 627 case 'M': 628 #if SMFI_VERSION > 5 629 if (set_macro_list) { 630 fprintf(stderr, "too many -M options\n"); 631 exit(1); 632 } 633 set_macro_list = optarg; 634 #else 635 fprintf(stderr, "no libmilter support to specify macro list\n"); 636 #endif 637 break; 638 case 'n': 639 #if SMFI_VERSION > 5 640 if (nosend) { 641 fprintf(stderr, "too many -n options\n"); 642 exit(1); 643 } 644 nosend = optarg; 645 #else 646 fprintf(stderr, "no libmilter support for negotiate callback\n"); 647 #endif 648 break; 649 case 'N': 650 #if SMFI_VERSION > 5 651 if (noreply) { 652 fprintf(stderr, "too many -n options\n"); 653 exit(1); 654 } 655 noreply = optarg; 656 #else 657 fprintf(stderr, "no libmilter support for negotiate callback\n"); 658 #endif 659 break; 660 case 'p': 661 if (smfi_setconn(optarg) == MI_FAILURE) { 662 fprintf(stderr, "smfi_setconn failed\n"); 663 exit(1); 664 } 665 break; 666 case 'r': 667 #ifdef SMFIP_RCPT_REJ 668 misc_mask |= SMFIP_RCPT_REJ; 669 #else 670 fprintf(stderr, "no libmilter support for rejected recipients\n"); 671 #endif 672 break; 673 case 'v': 674 verbose++; 675 break; 676 case 'C': 677 conn_count = atoi(optarg); 678 break; 679 default: 680 fprintf(stderr, 681 "usage: %s [-dv] \n" 682 "\t[-a action] non-default action\n" 683 "\t[-b body_text] replace body\n", 684 "\t[-c command] non-default action trigger\n" 685 "\t[-h 'index label value'] replace header\n" 686 "\t[-i 'index label value'] insert header\n" 687 "\t[-m macro_state] non-default macro state\n" 688 "\t[-M macro_list] non-default macro list\n" 689 "\t[-n events] don't receive these events\n" 690 "\t[-N events] don't reply to these events\n" 691 "\t-p port milter application\n" 692 "\t-r request rejected recipients\n" 693 "\t[-C conn_count] when to exit\n", 694 argv[0]); 695 exit(1); 696 } 697 } 698 if (command) { 699 for (cp = command_map; /* see below */ ; cp++) { 700 if (cp->name == 0) { 701 fprintf(stderr, "bad -c argument: %s\n", command); 702 exit(1); 703 } 704 if (strcmp(command, cp->name) == 0) 705 break; 706 } 707 } 708 if (action) { 709 if (command == 0) 710 cp = command_map; 711 if (strcmp(action, "tempfail") == 0) { 712 cp->reply[0] = SMFIS_TEMPFAIL; 713 } else if (strcmp(action, "reject") == 0) { 714 cp->reply[0] = SMFIS_REJECT; 715 } else if (strcmp(action, "accept") == 0) { 716 cp->reply[0] = SMFIS_ACCEPT; 717 } else if (strcmp(action, "discard") == 0) { 718 cp->reply[0] = SMFIS_DISCARD; 719 #ifdef SMFIS_SKIP 720 } else if (strcmp(action, "skip") == 0) { 721 cp->reply[0] = SMFIS_SKIP; 722 #endif 723 } else if ((code = atoi(action)) >= 400 724 && code <= 599 725 && action[3] == ' ') { 726 cp->reply[0] = SMFIR_REPLYCODE; 727 reply_code = action; 728 reply_dsn = action + 3; 729 if (*reply_dsn != 0) { 730 *reply_dsn++ = 0; 731 reply_dsn += strspn(reply_dsn, " "); 732 } 733 if (*reply_dsn == 0) { 734 reply_dsn = reply_message = 0; 735 } else { 736 reply_message = reply_dsn + strcspn(reply_dsn, " "); 737 if (*reply_message != 0) { 738 *reply_message++ = 0; 739 reply_message += strspn(reply_message, " "); 740 } 741 if (*reply_message == 0) 742 reply_message = 0; 743 } 744 } else { 745 fprintf(stderr, "bad -a argument: %s\n", action); 746 exit(1); 747 } 748 if (verbose) { 749 printf("command %s action %d\n", cp->name, cp->reply[0]); 750 if (reply_code) 751 printf("reply code %s dsn %s message %s\n", 752 reply_code, reply_dsn ? reply_dsn : "(null)", 753 reply_message ? reply_message : "(null)"); 754 } 755 } 756 #if SMFI_VERSION > 5 757 if (set_macro_state_arg) { 758 for (cpp = macro_states; /* see below */ ; cpp++) { 759 if (*cpp == 0) { 760 fprintf(stderr, "bad -m argument: %s\n", set_macro_state_arg); 761 exit(1); 762 } 763 if (strcmp(set_macro_state_arg, *cpp) == 0) 764 break; 765 } 766 set_macro_state = cpp - macro_states; 767 } 768 if (nosend) { 769 for (np = noproto_map; /* see below */ ; np++) { 770 if (np->name == 0) { 771 fprintf(stderr, "bad -n argument: %s\n", nosend); 772 exit(1); 773 } 774 if (strcmp(nosend, np->name) == 0) 775 break; 776 } 777 nosend_mask = np->send_mask; 778 np->action[0] = 0; 779 } 780 if (noreply) { 781 for (np = noproto_map; /* see below */ ; np++) { 782 if (np->name == 0) { 783 fprintf(stderr, "bad -N argument: %s\n", noreply); 784 exit(1); 785 } 786 if (strcmp(noreply, np->name) == 0) 787 break; 788 } 789 noreply_mask = np->reply_mask; 790 *np->reply = SMFIS_NOREPLY; 791 } 792 #endif 793 if (smfi_register(smfilter) == MI_FAILURE) { 794 fprintf(stderr, "smfi_register failed\n"); 795 exit(1); 796 } 797 return (smfi_main()); 798 } 799