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