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