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