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