1 /* $NetBSD: milter.c,v 1.1.1.4 2015/01/24 18:08:26 tron Exp $ */ 2 3 /*++ 4 /* NAME 5 /* milter 3 6 /* SUMMARY 7 /* generic MTA-side mail filter interface 8 /* SYNOPSIS 9 /* #include <milter.h> 10 /* 11 /* MILTERS *milter_create(milter_names, conn_timeout, cmd_timeout, 12 /* msg_timeout, protocol, def_action, 13 /* conn_macros, helo_macros, 14 /* mail_macros, rcpt_macros, 15 /* data_macros, eoh_macros, 16 /* eod_macros, unk_macros) 17 /* const char *milter_names; 18 /* int conn_timeout; 19 /* int cmd_timeout; 20 /* int msg_timeout; 21 /* const char *protocol; 22 /* const char *def_action; 23 /* const char *conn_macros; 24 /* const char *helo_macros; 25 /* const char *mail_macros; 26 /* const char *rcpt_macrps; 27 /* const char *data_macros; 28 /* const char *eoh_macros; 29 /* const char *eod_macros; 30 /* const char *unk_macros; 31 /* 32 /* void milter_free(milters) 33 /* MILTERS *milters; 34 /* 35 /* void milter_macro_callback(milters, mac_lookup, mac_context) 36 /* const char *(*mac_lookup)(const char *name, void *context); 37 /* void *mac_context; 38 /* 39 /* void milter_edit_callback(milters, add_header, upd_header, 40 /* ins_header, del_header, chg_from, 41 /* add_rcpt, add_rcpt_par, del_rcpt, 42 /* repl_body, context) 43 /* MILTERS *milters; 44 /* MILTER_ADD_HEADER_FN add_header; 45 /* MILTER_EDIT_HEADER_FN upd_header; 46 /* MILTER_EDIT_HEADER_FN ins_header; 47 /* MILTER_DEL_HEADER_FN del_header; 48 /* MILTER_EDIT_FROM_FN chg_from; 49 /* MILTER_EDIT_RCPT_FN add_rcpt; 50 /* MILTER_EDIT_RCPT_PAR_FN add_rcpt_par; 51 /* MILTER_EDIT_RCPT_FN del_rcpt; 52 /* MILTER_EDIT_BODY_FN repl_body; 53 /* void *context; 54 /* 55 /* const char *milter_conn_event(milters, client_name, client_addr, 56 /* client_port, addr_family) 57 /* MILTERS *milters; 58 /* const char *client_name; 59 /* const char *client_addr; 60 /* const char *client_port; 61 /* int addr_family; 62 /* 63 /* const char *milter_disc_event(milters) 64 /* MILTERS *milters; 65 /* 66 /* const char *milter_helo_event(milters, helo_name, esmtp_flag) 67 /* MILTERS *milters; 68 /* const char *helo_name; 69 /* int esmtp_flag; 70 /* 71 /* const char *milter_mail_event(milters, argv) 72 /* MILTERS *milters; 73 /* const char **argv; 74 /* 75 /* const char *milter_rcpt_event(milters, flags, argv) 76 /* MILTERS *milters; 77 /* int flags; 78 /* const char **argv; 79 /* 80 /* const char *milter_data_event(milters) 81 /* MILTERS *milters; 82 /* 83 /* const char *milter_unknown_event(milters, command) 84 /* MILTERS *milters; 85 /* const char *command; 86 /* 87 /* const char *milter_other_event(milters) 88 /* MILTERS *milters; 89 /* 90 /* const char *milter_message(milters, qfile, data_offset, auto_hdrs) 91 /* MILTERS *milters; 92 /* VSTREAM *qfile; 93 /* off_t data_offset; 94 /* ARGV *auto_hdrs; 95 /* 96 /* const char *milter_abort(milters) 97 /* MILTERS *milters; 98 /* 99 /* int milter_send(milters, fp) 100 /* MILTERS *milters; 101 /* VSTREAM *fp; 102 /* 103 /* MILTERS *milter_receive(fp, count) 104 /* VSTREAM *fp; 105 /* int count; 106 /* 107 /* int milter_dummy(milters, fp) 108 /* MILTERS *milters; 109 /* VSTREAM *fp; 110 /* DESCRIPTION 111 /* The functions in this module manage one or more milter (mail 112 /* filter) clients. Currently, only the Sendmail 8 filter 113 /* protocol is supported. 114 /* 115 /* The functions that inspect content or envelope commands 116 /* return either an SMTP reply ([45]XX followed by enhanced 117 /* status code and text), "D" (discard), "H" (quarantine), 118 /* "S" (shutdown connection), or a null pointer, which means 119 /* "no news is good news". 120 /* 121 /* milter_create() instantiates the milter clients specified 122 /* with the milter_names argument. The conn_macros etc. 123 /* arguments specify the names of macros that are sent to the 124 /* mail filter applications upon a connect etc. event. This 125 /* function should be called during process initialization, 126 /* before entering a chroot jail. The timeout parameters specify 127 /* time limits for the completion of the specified request 128 /* classes. The protocol parameter specifies a protocol version 129 /* and optional extensions. When the milter application is 130 /* unavailable, the milter client will go into a suitable error 131 /* state as specified with the def_action parameter (i.e. 132 /* reject, tempfail or accept all subsequent events). 133 /* 134 /* milter_free() disconnects from the milter instances that 135 /* are still opened, and destroys the data structures created 136 /* by milter_create(). This function is safe to call at any 137 /* point after milter_create(). 138 /* 139 /* milter_macro_callback() specifies a call-back function and 140 /* context for macro lookup. This function must be called 141 /* before milter_conn_event(). 142 /* 143 /* milter_edit_callback() specifies call-back functions and 144 /* context for editing the queue file after the end-of-data 145 /* is received. This function must be called before milter_message(); 146 /* 147 /* milter_conn_event() reports an SMTP client connection event 148 /* to the specified milter instances, after sending the macros 149 /* specified with the milter_create() conn_macros argument. 150 /* This function must be called before reporting any other 151 /* events. 152 /* 153 /* milter_disc_event() reports an SMTP client disconnection 154 /* event to the specified milter instances. No events can 155 /* reported after this call. To simplify usage, redundant calls 156 /* of this function are NO-OPs and don't raise a run-time 157 /* error. 158 /* 159 /* milter_helo_event() reports a HELO or EHLO event to the 160 /* specified milter instances, after sending the macros that 161 /* were specified with the milter_create() helo_macros argument. 162 /* 163 /* milter_mail_event() reports a MAIL FROM event to the specified 164 /* milter instances, after sending the macros that were specified 165 /* with the milter_create() mail_macros argument. 166 /* 167 /* milter_rcpt_event() reports an RCPT TO event to the specified 168 /* milter instances, after sending the macros that were specified 169 /* with the milter_create() rcpt_macros argument. The flags 170 /* argument supports the following: 171 /* .IP MILTER_FLAG_WANT_RCPT_REJ 172 /* When this flag is cleared, invoke all milters. When this 173 /* flag is set, invoke only milters that want to receive 174 /* rejected recipients; with Sendmail V8 Milters, {rcpt_mailer} 175 /* is set to "error", {rcpt_host} is set to an enhanced status 176 /* code, and {rcpt_addr} is set to descriptive text. 177 /* .PP 178 /* milter_data_event() reports a DATA event to the specified 179 /* milter instances, after sending the macros that were specified 180 /* with the milter_create() data_macros argument. 181 /* 182 /* milter_unknown_event() reports an unknown command event to 183 /* the specified milter instances, after sending the macros 184 /* that were specified with the milter_create() unk_macros 185 /* argument. 186 /* 187 /* milter_other_event() returns the current default mail filter 188 /* reply for the current SMTP connection state; it does not 189 /* change milter states. A null pointer result means that all 190 /* is well. This function can be used for SMTP commands such 191 /* as AUTH, STARTTLS that don't have their own milter event 192 /* routine. 193 /* 194 /* milter_message() sends the message header and body to the 195 /* to the specified milter instances, and sends the macros 196 /* specified with the milter_create() eoh_macros after the 197 /* message header, and with the eod_macros argument at 198 /* the end. Each milter sees the result of any changes made 199 /* by a preceding milter. This function must be called with 200 /* as argument an open Postfix queue file. 201 /* 202 /* milter_abort() cancels a mail transaction in progress. To 203 /* simplify usage, redundant calls of this function are NO-OPs 204 /* and don't raise a run-time error. 205 /* 206 /* milter_send() sends a list of mail filters over the specified 207 /* stream. When given a null list pointer, a "no filter" 208 /* indication is sent. The result is non-zero in case of 209 /* error. 210 /* 211 /* milter_receive() receives the specified number of mail 212 /* filters over the specified stream. The result is a null 213 /* pointer when no milters were sent, or when an error happened. 214 /* 215 /* milter_dummy() is like milter_send(), except that it sends 216 /* a dummy, but entirely valid, mail filter list. 217 /* SEE ALSO 218 /* milter8(3) Sendmail 8 Milter protocol 219 /* DIAGNOSTICS 220 /* Panic: interface violation. 221 /* Fatal errors: memory allocation problem. 222 /* LICENSE 223 /* .ad 224 /* .fi 225 /* The Secure Mailer license must be distributed with this software. 226 /* AUTHOR(S) 227 /* Wietse Venema 228 /* IBM T.J. Watson Research 229 /* P.O. Box 704 230 /* Yorktown Heights, NY 10598, USA 231 /*--*/ 232 233 /* System library. */ 234 235 #include <sys_defs.h> 236 237 /* Utility library. */ 238 239 #include <msg.h> 240 #include <mymalloc.h> 241 #include <stringops.h> 242 #include <argv.h> 243 #include <attr.h> 244 245 /* Global library. */ 246 247 #include <mail_proto.h> 248 #include <record.h> 249 #include <rec_type.h> 250 251 /* Postfix Milter library. */ 252 253 #include <milter.h> 254 255 /* Application-specific. */ 256 257 /* 258 * SLMs. 259 */ 260 #define STR(x) vstring_str(x) 261 262 /* milter_macro_lookup - look up macros */ 263 264 static ARGV *milter_macro_lookup(MILTERS *milters, const char *macro_names) 265 { 266 const char *myname = "milter_macro_lookup"; 267 char *saved_names = mystrdup(macro_names); 268 char *cp = saved_names; 269 ARGV *argv = argv_alloc(10); 270 const char *value; 271 const char *name; 272 273 while ((name = mystrtok(&cp, ", \t\r\n")) != 0) { 274 if (msg_verbose) 275 msg_info("%s: \"%s\"", myname, name); 276 if ((value = milters->mac_lookup(name, milters->mac_context)) != 0) { 277 if (msg_verbose) 278 msg_info("%s: result \"%s\"", myname, value); 279 argv_add(argv, name, value, (char *) 0); 280 } 281 } 282 myfree(saved_names); 283 return (argv); 284 } 285 286 /* milter_macro_callback - specify macro lookup */ 287 288 void milter_macro_callback(MILTERS *milters, 289 const char *(*mac_lookup) (const char *, void *), 290 void *mac_context) 291 { 292 milters->mac_lookup = mac_lookup; 293 milters->mac_context = mac_context; 294 } 295 296 /* milter_edit_callback - specify queue file edit call-back information */ 297 298 void milter_edit_callback(MILTERS *milters, 299 MILTER_ADD_HEADER_FN add_header, 300 MILTER_EDIT_HEADER_FN upd_header, 301 MILTER_EDIT_HEADER_FN ins_header, 302 MILTER_DEL_HEADER_FN del_header, 303 MILTER_EDIT_FROM_FN chg_from, 304 MILTER_EDIT_RCPT_FN add_rcpt, 305 MILTER_EDIT_RCPT_PAR_FN add_rcpt_par, 306 MILTER_EDIT_RCPT_FN del_rcpt, 307 MILTER_EDIT_BODY_FN repl_body, 308 void *chg_context) 309 { 310 milters->add_header = add_header; 311 milters->upd_header = upd_header; 312 milters->ins_header = ins_header; 313 milters->del_header = del_header; 314 milters->chg_from = chg_from; 315 milters->add_rcpt = add_rcpt; 316 milters->add_rcpt_par = add_rcpt_par; 317 milters->del_rcpt = del_rcpt; 318 milters->repl_body = repl_body; 319 milters->chg_context = chg_context; 320 } 321 322 /* milter_conn_event - report connect event */ 323 324 const char *milter_conn_event(MILTERS *milters, 325 const char *client_name, 326 const char *client_addr, 327 const char *client_port, 328 unsigned addr_family) 329 { 330 const char *resp; 331 MILTER *m; 332 ARGV *global_macros = 0; 333 ARGV *any_macros; 334 335 #define MILTER_MACRO_EVAL(global_macros, m, milters, member) \ 336 ((m->macros && m->macros->member[0]) ? \ 337 milter_macro_lookup(milters, m->macros->member) : \ 338 global_macros ? global_macros : \ 339 (global_macros = \ 340 milter_macro_lookup(milters, milters->macros->member))) 341 342 if (msg_verbose) 343 msg_info("report connect to all milters"); 344 for (resp = 0, m = milters->milter_list; resp == 0 && m != 0; m = m->next) { 345 any_macros = MILTER_MACRO_EVAL(global_macros, m, milters, conn_macros); 346 resp = m->conn_event(m, client_name, client_addr, client_port, 347 addr_family, any_macros); 348 if (any_macros != global_macros) 349 argv_free(any_macros); 350 } 351 if (global_macros) 352 argv_free(global_macros); 353 return (resp); 354 } 355 356 /* milter_helo_event - report helo event */ 357 358 const char *milter_helo_event(MILTERS *milters, const char *helo_name, 359 int esmtp_flag) 360 { 361 const char *resp; 362 MILTER *m; 363 ARGV *global_macros = 0; 364 ARGV *any_macros; 365 366 if (msg_verbose) 367 msg_info("report helo to all milters"); 368 for (resp = 0, m = milters->milter_list; resp == 0 && m != 0; m = m->next) { 369 any_macros = MILTER_MACRO_EVAL(global_macros, m, milters, helo_macros); 370 resp = m->helo_event(m, helo_name, esmtp_flag, any_macros); 371 if (any_macros != global_macros) 372 argv_free(any_macros); 373 } 374 if (global_macros) 375 argv_free(global_macros); 376 return (resp); 377 } 378 379 /* milter_mail_event - report mail from event */ 380 381 const char *milter_mail_event(MILTERS *milters, const char **argv) 382 { 383 const char *resp; 384 MILTER *m; 385 ARGV *global_macros = 0; 386 ARGV *any_macros; 387 388 if (msg_verbose) 389 msg_info("report sender to all milters"); 390 for (resp = 0, m = milters->milter_list; resp == 0 && m != 0; m = m->next) { 391 any_macros = MILTER_MACRO_EVAL(global_macros, m, milters, mail_macros); 392 resp = m->mail_event(m, argv, any_macros); 393 if (any_macros != global_macros) 394 argv_free(any_macros); 395 } 396 if (global_macros) 397 argv_free(global_macros); 398 return (resp); 399 } 400 401 /* milter_rcpt_event - report rcpt to event */ 402 403 const char *milter_rcpt_event(MILTERS *milters, int flags, const char **argv) 404 { 405 const char *resp; 406 MILTER *m; 407 ARGV *global_macros = 0; 408 ARGV *any_macros; 409 410 if (msg_verbose) 411 msg_info("report recipient to all milters (flags=0x%x)", flags); 412 for (resp = 0, m = milters->milter_list; resp == 0 && m != 0; m = m->next) { 413 if ((flags & MILTER_FLAG_WANT_RCPT_REJ) == 0 414 || (m->flags & MILTER_FLAG_WANT_RCPT_REJ) != 0) { 415 any_macros = 416 MILTER_MACRO_EVAL(global_macros, m, milters, rcpt_macros); 417 resp = m->rcpt_event(m, argv, any_macros); 418 if (any_macros != global_macros) 419 argv_free(any_macros); 420 } 421 } 422 if (global_macros) 423 argv_free(global_macros); 424 return (resp); 425 } 426 427 /* milter_data_event - report data event */ 428 429 const char *milter_data_event(MILTERS *milters) 430 { 431 const char *resp; 432 MILTER *m; 433 ARGV *global_macros = 0; 434 ARGV *any_macros; 435 436 if (msg_verbose) 437 msg_info("report data to all milters"); 438 for (resp = 0, m = milters->milter_list; resp == 0 && m != 0; m = m->next) { 439 any_macros = MILTER_MACRO_EVAL(global_macros, m, milters, data_macros); 440 resp = m->data_event(m, any_macros); 441 if (any_macros != global_macros) 442 argv_free(any_macros); 443 } 444 if (global_macros) 445 argv_free(global_macros); 446 return (resp); 447 } 448 449 /* milter_unknown_event - report unknown command */ 450 451 const char *milter_unknown_event(MILTERS *milters, const char *command) 452 { 453 const char *resp; 454 MILTER *m; 455 ARGV *global_macros = 0; 456 ARGV *any_macros; 457 458 if (msg_verbose) 459 msg_info("report unknown command to all milters"); 460 for (resp = 0, m = milters->milter_list; resp == 0 && m != 0; m = m->next) { 461 any_macros = MILTER_MACRO_EVAL(global_macros, m, milters, unk_macros); 462 resp = m->unknown_event(m, command, any_macros); 463 if (any_macros != global_macros) 464 argv_free(any_macros); 465 } 466 if (global_macros) 467 argv_free(global_macros); 468 return (resp); 469 } 470 471 /* milter_other_event - other SMTP event */ 472 473 const char *milter_other_event(MILTERS *milters) 474 { 475 const char *resp; 476 MILTER *m; 477 478 if (msg_verbose) 479 msg_info("query milter states for other event"); 480 for (resp = 0, m = milters->milter_list; resp == 0 && m != 0; m = m->next) 481 resp = m->other_event(m); 482 return (resp); 483 } 484 485 /* milter_message - inspect message content */ 486 487 const char *milter_message(MILTERS *milters, VSTREAM *fp, off_t data_offset, 488 ARGV *auto_hdrs) 489 { 490 const char *resp; 491 MILTER *m; 492 ARGV *global_eoh_macros = 0; 493 ARGV *global_eod_macros = 0; 494 ARGV *any_eoh_macros; 495 ARGV *any_eod_macros; 496 497 if (msg_verbose) 498 msg_info("inspect content by all milters"); 499 for (resp = 0, m = milters->milter_list; resp == 0 && m != 0; m = m->next) { 500 any_eoh_macros = MILTER_MACRO_EVAL(global_eoh_macros, m, milters, eoh_macros); 501 any_eod_macros = MILTER_MACRO_EVAL(global_eod_macros, m, milters, eod_macros); 502 resp = m->message(m, fp, data_offset, any_eoh_macros, any_eod_macros, 503 auto_hdrs); 504 if (any_eoh_macros != global_eoh_macros) 505 argv_free(any_eoh_macros); 506 if (any_eod_macros != global_eod_macros) 507 argv_free(any_eod_macros); 508 } 509 if (global_eoh_macros) 510 argv_free(global_eoh_macros); 511 if (global_eod_macros) 512 argv_free(global_eod_macros); 513 return (resp); 514 } 515 516 /* milter_abort - cancel message receiving state, all milters */ 517 518 void milter_abort(MILTERS *milters) 519 { 520 MILTER *m; 521 522 if (msg_verbose) 523 msg_info("abort all milters"); 524 for (m = milters->milter_list; m != 0; m = m->next) 525 m->abort(m); 526 } 527 528 /* milter_disc_event - report client disconnect event to all milters */ 529 530 void milter_disc_event(MILTERS *milters) 531 { 532 MILTER *m; 533 534 if (msg_verbose) 535 msg_info("disconnect event to all milters"); 536 for (m = milters->milter_list; m != 0; m = m->next) 537 m->disc_event(m); 538 } 539 540 /* milter_new - create milter list */ 541 542 MILTERS *milter_new(const char *names, 543 int conn_timeout, 544 int cmd_timeout, 545 int msg_timeout, 546 const char *protocol, 547 const char *def_action, 548 MILTER_MACROS *macros) 549 { 550 MILTERS *milters; 551 MILTER *head = 0; 552 MILTER *tail = 0; 553 char *name; 554 MILTER *milter; 555 const char *sep = ", \t\r\n"; 556 557 /* 558 * Parse the milter list. 559 */ 560 milters = (MILTERS *) mymalloc(sizeof(*milters)); 561 if (names != 0) { 562 char *saved_names = mystrdup(names); 563 char *cp = saved_names; 564 565 while ((name = mystrtok(&cp, sep)) != 0) { 566 milter = milter8_create(name, conn_timeout, cmd_timeout, 567 msg_timeout, protocol, def_action, 568 milters); 569 if (head == 0) { 570 head = milter; 571 } else { 572 tail->next = milter; 573 } 574 tail = milter; 575 } 576 myfree(saved_names); 577 } 578 milters->milter_list = head; 579 milters->mac_lookup = 0; 580 milters->mac_context = 0; 581 milters->macros = macros; 582 milters->add_header = 0; 583 milters->upd_header = milters->ins_header = 0; 584 milters->del_header = 0; 585 milters->add_rcpt = milters->del_rcpt = 0; 586 milters->repl_body = 0; 587 milters->chg_context = 0; 588 return (milters); 589 } 590 591 /* milter_free - destroy all milters */ 592 593 void milter_free(MILTERS *milters) 594 { 595 MILTER *m; 596 MILTER *next; 597 598 if (msg_verbose) 599 msg_info("free all milters"); 600 for (m = milters->milter_list; m != 0; m = next) 601 next = m->next, m->free(m); 602 if (milters->macros) 603 milter_macros_free(milters->macros); 604 myfree((char *) milters); 605 } 606 607 /* milter_dummy - send empty milter list */ 608 609 int milter_dummy(MILTERS *milters, VSTREAM *stream) 610 { 611 MILTERS dummy = *milters; 612 613 dummy.milter_list = 0; 614 return (milter_send(&dummy, stream)); 615 } 616 617 /* milter_send - send Milter instances over stream */ 618 619 int milter_send(MILTERS *milters, VSTREAM *stream) 620 { 621 MILTER *m; 622 int status = 0; 623 int count = 0; 624 625 /* 626 * XXX Optimization: send only the filters that are actually used in the 627 * remote process. No point sending a filter that looks at HELO commands 628 * to a cleanup server. For now we skip only the filters that are known 629 * to be disabled (either in global error state or in global accept 630 * state). 631 * 632 * XXX We must send *some* information, even when there are no active 633 * filters, otherwise the cleanup server would try to apply its own 634 * non_smtpd_milters settings. 635 */ 636 if (milters != 0) 637 for (m = milters->milter_list; m != 0; m = m->next) 638 if (m->active(m)) 639 count++; 640 (void) rec_fprintf(stream, REC_TYPE_MILT_COUNT, "%d", count); 641 642 if (msg_verbose) 643 msg_info("send %d milters", count); 644 645 /* 646 * XXX Optimization: don't send or receive further information when there 647 * aren't any active filters. 648 */ 649 if (count <= 0) 650 return (0); 651 652 /* 653 * Send the filter macro name lists. 654 */ 655 (void) attr_print(stream, ATTR_FLAG_MORE, 656 ATTR_TYPE_FUNC, milter_macros_print, 657 (void *) milters->macros, 658 ATTR_TYPE_END); 659 660 /* 661 * Send the filter instances. 662 */ 663 for (m = milters->milter_list; m != 0; m = m->next) 664 if (m->active(m) && (status = m->send(m, stream)) != 0) 665 break; 666 667 /* 668 * Over to you. 669 */ 670 if (status != 0 671 || attr_scan(stream, ATTR_FLAG_STRICT, 672 ATTR_TYPE_INT, MAIL_ATTR_STATUS, &status, 673 ATTR_TYPE_END) != 1 674 || status != 0) { 675 msg_warn("cannot send milters to service %s", VSTREAM_PATH(stream)); 676 return (-1); 677 } 678 return (0); 679 } 680 681 /* milter_receive - receive milters from stream */ 682 683 MILTERS *milter_receive(VSTREAM *stream, int count) 684 { 685 MILTERS *milters; 686 MILTER *head = 0; 687 MILTER *tail = 0; 688 MILTER *milter = 0; 689 690 if (msg_verbose) 691 msg_info("receive %d milters", count); 692 693 /* 694 * XXX We must instantiate a MILTERS structure even when the sender has 695 * no active filters, otherwise the cleanup server would try to use its 696 * own non_smtpd_milters settings. 697 */ 698 #define NO_MILTERS ((char *) 0) 699 #define NO_TIMEOUTS 0, 0, 0 700 #define NO_PROTOCOL ((char *) 0) 701 #define NO_ACTION ((char *) 0) 702 #define NO_MACROS ((MILTER_MACROS *) 0) 703 704 milters = milter_new(NO_MILTERS, NO_TIMEOUTS, NO_PROTOCOL, NO_ACTION, 705 NO_MACROS); 706 707 /* 708 * XXX Optimization: don't send or receive further information when there 709 * aren't any active filters. 710 */ 711 if (count <= 0) 712 return (milters); 713 714 /* 715 * Receive the global macro name lists. 716 */ 717 milters->macros = milter_macros_alloc(MILTER_MACROS_ALLOC_ZERO); 718 if (attr_scan(stream, ATTR_FLAG_STRICT | ATTR_FLAG_MORE, 719 ATTR_TYPE_FUNC, milter_macros_scan, 720 (void *) milters->macros, 721 ATTR_TYPE_END) != 1) { 722 milter_free(milters); 723 return (0); 724 } 725 726 /* 727 * Receive the filters. 728 */ 729 for (; count > 0; count--) { 730 if ((milter = milter8_receive(stream, milters)) == 0) { 731 msg_warn("cannot receive milters via service %s socket", 732 VSTREAM_PATH(stream)); 733 milter_free(milters); 734 return (0); 735 } 736 if (head == 0) { 737 /* Coverity: milter_free() depends on milters->milter_list. */ 738 milters->milter_list = head = milter; 739 } else { 740 tail->next = milter; 741 } 742 tail = milter; 743 } 744 745 /* 746 * Over to you. 747 */ 748 (void) attr_print(stream, ATTR_FLAG_NONE, 749 ATTR_TYPE_INT, MAIL_ATTR_STATUS, 0, 750 ATTR_TYPE_END); 751 return (milters); 752 } 753 754 #ifdef TEST 755 756 /* 757 * Proof-of-concept test program. This can be used interactively, but is 758 * typically used for automated regression tests from a script. 759 */ 760 761 /* System library. */ 762 763 #include <sys/socket.h> 764 #include <stdlib.h> 765 #include <string.h> 766 767 /* Utility library. */ 768 769 #include "msg_vstream.h" 770 #include "vstring_vstream.h" 771 772 /* Global library. */ 773 774 #include <mail_params.h> 775 776 int var_milt_conn_time = 10; 777 int var_milt_cmd_time = 10; 778 int var_milt_msg_time = 100; 779 char *var_milt_protocol = DEF_MILT_PROTOCOL; 780 char *var_milt_def_action = DEF_MILT_DEF_ACTION; 781 782 static void usage(void) 783 { 784 vstream_fprintf(VSTREAM_ERR, "usage: \n" 785 " create names... create and connect\n" 786 #if 0 787 " conn_macros names... define connect macros\n" 788 " helo_macros names... define helo command macros\n" 789 " mail_macros names... define mail command macros\n" 790 " rcpt_macros names... define rcpt command macros\n" 791 " data_macros names... define data command macros\n" 792 " unk_macros names... unknown command macros\n" 793 " message_macros names... define message macros\n" 794 #endif 795 " free disconnect and destroy\n" 796 " connect name addr port family\n" 797 " helo hostname\n" 798 " ehlo hostname\n" 799 " mail from sender...\n" 800 " rcpt to recipient...\n" 801 " data\n" 802 " disconnect\n" 803 " unknown command\n"); 804 vstream_fflush(VSTREAM_ERR); 805 } 806 807 int main(int argc, char **argv) 808 { 809 MILTERS *milters = 0; 810 char *conn_macros, *helo_macros, *mail_macros, *rcpt_macros; 811 char *data_macros, *eoh_macros, *eod_macros, *unk_macros; 812 VSTRING *inbuf = vstring_alloc(100); 813 char *bufp; 814 char *cmd; 815 int ch; 816 int istty = isatty(vstream_fileno(VSTREAM_IN)); 817 818 conn_macros = helo_macros = mail_macros = rcpt_macros = data_macros 819 = eoh_macros = eod_macros = unk_macros = ""; 820 821 msg_vstream_init(argv[0], VSTREAM_ERR); 822 while ((ch = GETOPT(argc, argv, "V:v")) > 0) { 823 switch (ch) { 824 default: 825 msg_fatal("usage: %s [-a action] [-p protocol] [-v]", argv[0]); 826 case 'a': 827 var_milt_def_action = optarg; 828 break; 829 case 'p': 830 var_milt_protocol = optarg; 831 break; 832 case 'v': 833 msg_verbose++; 834 break; 835 } 836 } 837 optind = OPTIND; 838 839 for (;;) { 840 const char *resp = 0; 841 ARGV *argv; 842 char **args; 843 844 if (istty) { 845 vstream_printf("- "); 846 vstream_fflush(VSTREAM_OUT); 847 } 848 if (vstring_fgets_nonl(inbuf, VSTREAM_IN) <= 0) 849 break; 850 bufp = vstring_str(inbuf); 851 if (!istty) { 852 vstream_printf("> %s\n", bufp); 853 vstream_fflush(VSTREAM_OUT); 854 } 855 if (*bufp == '#') 856 continue; 857 cmd = mystrtok(&bufp, " "); 858 if (cmd == 0) { 859 usage(); 860 continue; 861 } 862 argv = argv_split(bufp, " "); 863 args = argv->argv; 864 if (strcmp(cmd, "create") == 0 && argv->argc == 1) { 865 if (milters != 0) { 866 msg_warn("deleting existing milters"); 867 milter_free(milters); 868 } 869 milters = milter_create(args[0], var_milt_conn_time, 870 var_milt_cmd_time, var_milt_msg_time, 871 var_milt_protocol, var_milt_def_action, 872 conn_macros, helo_macros, mail_macros, 873 rcpt_macros, data_macros, eoh_macros, 874 eod_macros, unk_macros); 875 } else if (strcmp(cmd, "free") == 0 && argv->argc == 0) { 876 if (milters == 0) { 877 msg_warn("no milters"); 878 continue; 879 } 880 milter_free(milters); 881 milters = 0; 882 } else if (strcmp(cmd, "connect") == 0 && argv->argc == 4) { 883 if (milters == 0) { 884 msg_warn("no milters"); 885 continue; 886 } 887 resp = milter_conn_event(milters, args[0], args[1], args[2], 888 strcmp(args[3], "AF_INET") == 0 ? AF_INET : 889 strcmp(args[3], "AF_INET6") == 0 ? AF_INET6 : 890 strcmp(args[3], "AF_UNIX") == 0 ? AF_UNIX : 891 AF_UNSPEC); 892 } else if (strcmp(cmd, "helo") == 0 && argv->argc == 1) { 893 if (milters == 0) { 894 msg_warn("no milters"); 895 continue; 896 } 897 resp = milter_helo_event(milters, args[0], 0); 898 } else if (strcmp(cmd, "ehlo") == 0 && argv->argc == 1) { 899 if (milters == 0) { 900 msg_warn("no milters"); 901 continue; 902 } 903 resp = milter_helo_event(milters, args[0], 1); 904 } else if (strcmp(cmd, "mail") == 0 && argv->argc > 0) { 905 if (milters == 0) { 906 msg_warn("no milters"); 907 continue; 908 } 909 resp = milter_mail_event(milters, (const char **) args); 910 } else if (strcmp(cmd, "rcpt") == 0 && argv->argc > 0) { 911 if (milters == 0) { 912 msg_warn("no milters"); 913 continue; 914 } 915 resp = milter_rcpt_event(milters, 0, (const char **) args); 916 } else if (strcmp(cmd, "unknown") == 0 && argv->argc > 0) { 917 if (milters == 0) { 918 msg_warn("no milters"); 919 continue; 920 } 921 resp = milter_unknown_event(milters, args[0]); 922 } else if (strcmp(cmd, "data") == 0 && argv->argc == 0) { 923 if (milters == 0) { 924 msg_warn("no milters"); 925 continue; 926 } 927 resp = milter_data_event(milters); 928 } else if (strcmp(cmd, "disconnect") == 0 && argv->argc == 0) { 929 if (milters == 0) { 930 msg_warn("no milters"); 931 continue; 932 } 933 milter_disc_event(milters); 934 } else { 935 usage(); 936 } 937 if (resp != 0) 938 msg_info("%s", resp); 939 argv_free(argv); 940 } 941 if (milters != 0) 942 milter_free(milters); 943 vstring_free(inbuf); 944 return (0); 945 } 946 947 #endif 948