164562Sgshapiro/* 2261370Sgshapiro * Copyright (c) 1999-2009, 2012, 2013 Proofpoint, Inc. and its suppliers. 364562Sgshapiro * All rights reserved. 464562Sgshapiro * 564562Sgshapiro * By using this file, you agree to the terms and conditions set 664562Sgshapiro * forth in the LICENSE file which can be found at the top level of 764562Sgshapiro * the sendmail distribution. 864562Sgshapiro * 964562Sgshapiro */ 1064562Sgshapiro 1190792Sgshapiro#include <sendmail.h> 1264562Sgshapiro 13266711SgshapiroSM_RCSID("@(#)$Id: milter.c,v 8.281 2013-11-22 20:51:56 ca Exp $") 1464562Sgshapiro 1590792Sgshapiro#if MILTER 16168515Sgshapiro# include <sm/sendmail.h> 1790792Sgshapiro# include <libmilter/mfapi.h> 1890792Sgshapiro# include <libmilter/mfdef.h> 1990792Sgshapiro 2064562Sgshapiro# include <errno.h> 21157001Sgshapiro# include <sm/time.h> 22132943Sgshapiro# include <sys/uio.h> 2364562Sgshapiro 2464562Sgshapiro# if NETINET || NETINET6 2564562Sgshapiro# include <arpa/inet.h> 26168515Sgshapiro# if MILTER_NO_NAGLE 27132943Sgshapiro# include <netinet/tcp.h> 28168515Sgshapiro# endif /* MILTER_NO_NAGLE */ 2964562Sgshapiro# endif /* NETINET || NETINET6 */ 3064562Sgshapiro 3190792Sgshapiro# include <sm/fdset.h> 3264562Sgshapiro 33141858Sgshapirostatic void milter_connect_timeout __P((int)); 3490792Sgshapirostatic void milter_error __P((struct milter *, ENVELOPE *)); 3564562Sgshapirostatic int milter_open __P((struct milter *, bool, ENVELOPE *)); 3664562Sgshapirostatic void milter_parse_timeouts __P((char *, struct milter *)); 37168515Sgshapirostatic char *milter_sysread __P((struct milter *, char *, ssize_t, time_t, 38168515Sgshapiro ENVELOPE *, const char *)); 39168515Sgshapirostatic char *milter_read __P((struct milter *, char *, ssize_t *, time_t, 40168515Sgshapiro ENVELOPE *, const char *)); 41168515Sgshapirostatic char *milter_write __P((struct milter *, int, char *, ssize_t, 42168515Sgshapiro time_t, ENVELOPE *, const char *)); 43168515Sgshapirostatic char *milter_send_command __P((struct milter *, int, void *, 44168515Sgshapiro ssize_t, ENVELOPE *, char *, const char *)); 45244928Sgshapirostatic char *milter_command __P((int, void *, ssize_t, int, 46168515Sgshapiro ENVELOPE *, char *, const char *, bool)); 47168515Sgshapirostatic char *milter_body __P((struct milter *, ENVELOPE *, char *)); 48168515Sgshapirostatic int milter_reopen_df __P((ENVELOPE *)); 49168515Sgshapirostatic int milter_reset_df __P((ENVELOPE *)); 50168515Sgshapirostatic void milter_quit_filter __P((struct milter *, ENVELOPE *)); 51168515Sgshapirostatic void milter_abort_filter __P((struct milter *, ENVELOPE *)); 52168515Sgshapirostatic void milter_send_macros __P((struct milter *, char **, int, 53168515Sgshapiro ENVELOPE *)); 54173340Sgshapirostatic int milter_negotiate __P((struct milter *, ENVELOPE *, 55173340Sgshapiro milters_T *)); 56168515Sgshapirostatic void milter_per_connection_check __P((ENVELOPE *)); 57168515Sgshapirostatic char *milter_headers __P((struct milter *, ENVELOPE *, char *)); 58168515Sgshapirostatic void milter_addheader __P((struct milter *, char *, ssize_t, 59168515Sgshapiro ENVELOPE *)); 60168515Sgshapirostatic void milter_insheader __P((struct milter *, char *, ssize_t, 61168515Sgshapiro ENVELOPE *)); 62168515Sgshapirostatic void milter_changeheader __P((struct milter *, char *, ssize_t, 63168515Sgshapiro ENVELOPE *)); 64168515Sgshapirostatic void milter_chgfrom __P((char *, ssize_t, ENVELOPE *)); 65168515Sgshapirostatic void milter_addrcpt __P((char *, ssize_t, ENVELOPE *)); 66168515Sgshapirostatic void milter_addrcpt_par __P((char *, ssize_t, ENVELOPE *)); 67168515Sgshapirostatic void milter_delrcpt __P((char *, ssize_t, ENVELOPE *)); 68168515Sgshapirostatic int milter_replbody __P((char *, ssize_t, bool, ENVELOPE *)); 69168515Sgshapirostatic int milter_set_macros __P((char *, char **, char *, int)); 7064562Sgshapiro 71168515Sgshapiro 72168515Sgshapiro/* milter states */ 73168515Sgshapiro# define SMFS_CLOSED 'C' /* closed for all further actions */ 74168515Sgshapiro# define SMFS_OPEN 'O' /* connected to remote milter filter */ 75168515Sgshapiro# define SMFS_INMSG 'M' /* currently servicing a message */ 76168515Sgshapiro# define SMFS_DONE 'D' /* done with current message */ 77168515Sgshapiro# define SMFS_CLOSABLE 'Q' /* done with current connection */ 78168515Sgshapiro# define SMFS_ERROR 'E' /* error state */ 79168515Sgshapiro# define SMFS_READY 'R' /* ready for action */ 80168515Sgshapiro# define SMFS_SKIP 'S' /* skip body */ 81168515Sgshapiro 82244928Sgshapiro/* 83244928Sgshapiro** MilterMacros contains the milter macros for each milter and each stage. 84244928Sgshapiro** indices are (in order): stages, milter-index, macro 85244928Sgshapiro** milter-index == 0: "global" macros (not for a specific milter). 86244928Sgshapiro*/ 87244928Sgshapiro 88244928Sgshapirostatic char *MilterMacros[SMFIM_LAST + 1][MAXFILTERS + 1][MAXFILTERMACROS + 1]; 89132943Sgshapirostatic size_t MilterMaxDataSize = MILTER_MAX_DATA_SIZE; 9064562Sgshapiro 9164562Sgshapiro# define MILTER_CHECK_DONE_MSG() \ 9264562Sgshapiro if (*state == SMFIR_REPLYCODE || \ 9364562Sgshapiro *state == SMFIR_REJECT || \ 9464562Sgshapiro *state == SMFIR_DISCARD || \ 9564562Sgshapiro *state == SMFIR_TEMPFAIL) \ 9664562Sgshapiro { \ 9764562Sgshapiro /* Abort the filters to let them know we are done with msg */ \ 9864562Sgshapiro milter_abort(e); \ 9964562Sgshapiro } 10064562Sgshapiro 101244928Sgshapiro/* set state in case of an error */ 102244928Sgshapiro# define MILTER_SET_STATE \ 103244928Sgshapiro if (bitnset(SMF_TEMPFAIL, m->mf_flags)) \ 104244928Sgshapiro *state = SMFIR_TEMPFAIL; \ 105244928Sgshapiro else if (bitnset(SMF_TEMPDROP, m->mf_flags)) \ 106244928Sgshapiro *state = SMFIR_SHUTDOWN; \ 107244928Sgshapiro else if (bitnset(SMF_REJECT, m->mf_flags)) \ 108244928Sgshapiro *state = SMFIR_REJECT 109244928Sgshapiro 110244928Sgshapiro/* flow through code maybe using continue; don't wrap in do {} while */ 111132943Sgshapiro# define MILTER_CHECK_ERROR(initial, action) \ 112112810Sgshapiro if (!initial && tTd(71, 100)) \ 113102528Sgshapiro { \ 114102528Sgshapiro if (e->e_quarmsg == NULL) \ 115102528Sgshapiro { \ 116102528Sgshapiro e->e_quarmsg = sm_rpool_strdup_x(e->e_rpool, \ 117102528Sgshapiro "filter failure"); \ 118102528Sgshapiro macdefine(&e->e_macro, A_PERM, macid("{quarantine}"), \ 119102528Sgshapiro e->e_quarmsg); \ 120102528Sgshapiro } \ 121102528Sgshapiro } \ 122112810Sgshapiro else if (tTd(71, 101)) \ 123112810Sgshapiro { \ 124112810Sgshapiro if (e->e_quarmsg == NULL) \ 125112810Sgshapiro { \ 126112810Sgshapiro e->e_quarmsg = sm_rpool_strdup_x(e->e_rpool, \ 127112810Sgshapiro "filter failure"); \ 128112810Sgshapiro macdefine(&e->e_macro, A_PERM, macid("{quarantine}"), \ 129112810Sgshapiro e->e_quarmsg); \ 130112810Sgshapiro } \ 131112810Sgshapiro } \ 132244928Sgshapiro else MILTER_SET_STATE; \ 133102528Sgshapiro else \ 134102528Sgshapiro action; 13564562Sgshapiro 13664562Sgshapiro# define MILTER_CHECK_REPLYCODE(default) \ 13764562Sgshapiro if (response == NULL || \ 13864562Sgshapiro strlen(response) + 1 != (size_t) rlen || \ 13964562Sgshapiro rlen < 3 || \ 14064562Sgshapiro (response[0] != '4' && response[0] != '5') || \ 14164562Sgshapiro !isascii(response[1]) || !isdigit(response[1]) || \ 14264562Sgshapiro !isascii(response[2]) || !isdigit(response[2])) \ 14364562Sgshapiro { \ 14464562Sgshapiro if (response != NULL) \ 14590792Sgshapiro sm_free(response); /* XXX */ \ 14664562Sgshapiro response = newstr(default); \ 14764562Sgshapiro } \ 14864562Sgshapiro else \ 14964562Sgshapiro { \ 15064562Sgshapiro char *ptr = response; \ 15164562Sgshapiro \ 15264562Sgshapiro /* Check for unprotected %'s in the string */ \ 15364562Sgshapiro while (*ptr != '\0') \ 15464562Sgshapiro { \ 15564562Sgshapiro if (*ptr == '%' && *++ptr != '%') \ 15664562Sgshapiro { \ 15790792Sgshapiro sm_free(response); /* XXX */ \ 15864562Sgshapiro response = newstr(default); \ 15964562Sgshapiro break; \ 16064562Sgshapiro } \ 16164562Sgshapiro ptr++; \ 16264562Sgshapiro } \ 16364562Sgshapiro } 16464562Sgshapiro 16564562Sgshapiro# define MILTER_DF_ERROR(msg) \ 16664562Sgshapiro{ \ 16764562Sgshapiro int save_errno = errno; \ 16864562Sgshapiro \ 16964562Sgshapiro if (tTd(64, 5)) \ 17064562Sgshapiro { \ 17190792Sgshapiro sm_dprintf(msg, dfname, sm_errstring(save_errno)); \ 17290792Sgshapiro sm_dprintf("\n"); \ 17364562Sgshapiro } \ 17490792Sgshapiro if (MilterLogLevel > 0) \ 17590792Sgshapiro sm_syslog(LOG_ERR, e->e_id, msg, dfname, sm_errstring(save_errno)); \ 17690792Sgshapiro if (SuperSafe == SAFE_REALLY) \ 17764562Sgshapiro { \ 17864562Sgshapiro if (e->e_dfp != NULL) \ 17964562Sgshapiro { \ 18090792Sgshapiro (void) sm_io_close(e->e_dfp, SM_TIME_DEFAULT); \ 18164562Sgshapiro e->e_dfp = NULL; \ 18264562Sgshapiro } \ 18364562Sgshapiro e->e_flags &= ~EF_HAS_DF; \ 18464562Sgshapiro } \ 18564562Sgshapiro errno = save_errno; \ 18664562Sgshapiro} 18764562Sgshapiro 18864562Sgshapiro/* 18964562Sgshapiro** MILTER_TIMEOUT -- make sure socket is ready in time 19064562Sgshapiro** 19164562Sgshapiro** Parameters: 19264562Sgshapiro** routine -- routine name for debug/logging 19364562Sgshapiro** secs -- number of seconds in timeout 19464562Sgshapiro** write -- waiting to read or write? 19590792Sgshapiro** started -- whether this is part of a previous sequence 19664562Sgshapiro** 19764562Sgshapiro** Assumes 'm' is a milter structure for the current socket. 19864562Sgshapiro*/ 19964562Sgshapiro 200168515Sgshapiro# define MILTER_TIMEOUT(routine, secs, write, started, function) \ 20164562Sgshapiro{ \ 20264562Sgshapiro int ret; \ 20364562Sgshapiro int save_errno; \ 20464562Sgshapiro fd_set fds; \ 20564562Sgshapiro struct timeval tv; \ 20664562Sgshapiro \ 20790792Sgshapiro if (SM_FD_SETSIZE > 0 && m->mf_sock >= SM_FD_SETSIZE) \ 20864562Sgshapiro { \ 20964562Sgshapiro if (tTd(64, 5)) \ 21090792Sgshapiro sm_dprintf("milter_%s(%s): socket %d is larger than FD_SETSIZE %d\n", \ 21190792Sgshapiro (routine), m->mf_name, m->mf_sock, \ 21290792Sgshapiro SM_FD_SETSIZE); \ 21390792Sgshapiro if (MilterLogLevel > 0) \ 21464562Sgshapiro sm_syslog(LOG_ERR, e->e_id, \ 21590792Sgshapiro "Milter (%s): socket(%s) %d is larger than FD_SETSIZE %d", \ 21690792Sgshapiro m->mf_name, (routine), m->mf_sock, \ 21790792Sgshapiro SM_FD_SETSIZE); \ 21890792Sgshapiro milter_error(m, e); \ 21964562Sgshapiro return NULL; \ 22064562Sgshapiro } \ 22164562Sgshapiro \ 22294334Sgshapiro do \ 22394334Sgshapiro { \ 22494334Sgshapiro FD_ZERO(&fds); \ 22594334Sgshapiro SM_FD_SET(m->mf_sock, &fds); \ 22694334Sgshapiro tv.tv_sec = (secs); \ 22794334Sgshapiro tv.tv_usec = 0; \ 22894334Sgshapiro ret = select(m->mf_sock + 1, \ 22994334Sgshapiro (write) ? NULL : &fds, \ 23094334Sgshapiro (write) ? &fds : NULL, \ 23194334Sgshapiro NULL, &tv); \ 23294334Sgshapiro } while (ret < 0 && errno == EINTR); \ 23364562Sgshapiro \ 23464562Sgshapiro switch (ret) \ 23564562Sgshapiro { \ 23664562Sgshapiro case 0: \ 23764562Sgshapiro if (tTd(64, 5)) \ 238168515Sgshapiro sm_dprintf("milter_%s(%s): timeout, where=%s\n", \ 239168515Sgshapiro (routine), m->mf_name, (function)); \ 24090792Sgshapiro if (MilterLogLevel > 0) \ 24190792Sgshapiro sm_syslog(LOG_ERR, e->e_id, \ 242168515Sgshapiro "Milter (%s): timeout %s data %s, where=%s", \ 243168515Sgshapiro m->mf_name, \ 24490792Sgshapiro started ? "during" : "before", \ 245168515Sgshapiro (routine), (function)); \ 24690792Sgshapiro milter_error(m, e); \ 24764562Sgshapiro return NULL; \ 24864562Sgshapiro \ 24964562Sgshapiro case -1: \ 25064562Sgshapiro save_errno = errno; \ 25164562Sgshapiro if (tTd(64, 5)) \ 25290792Sgshapiro sm_dprintf("milter_%s(%s): select: %s\n", (routine), \ 25390792Sgshapiro m->mf_name, sm_errstring(save_errno)); \ 25490792Sgshapiro if (MilterLogLevel > 0) \ 25590792Sgshapiro { \ 25664562Sgshapiro sm_syslog(LOG_ERR, e->e_id, \ 25790792Sgshapiro "Milter (%s): select(%s): %s", \ 25890792Sgshapiro m->mf_name, (routine), \ 25990792Sgshapiro sm_errstring(save_errno)); \ 26090792Sgshapiro } \ 26190792Sgshapiro milter_error(m, e); \ 26264562Sgshapiro return NULL; \ 26364562Sgshapiro \ 26464562Sgshapiro default: \ 26571345Sgshapiro if (SM_FD_ISSET(m->mf_sock, &fds)) \ 26664562Sgshapiro break; \ 26764562Sgshapiro if (tTd(64, 5)) \ 26890792Sgshapiro sm_dprintf("milter_%s(%s): socket not ready\n", \ 26990792Sgshapiro (routine), m->mf_name); \ 27090792Sgshapiro if (MilterLogLevel > 0) \ 27190792Sgshapiro { \ 27264562Sgshapiro sm_syslog(LOG_ERR, e->e_id, \ 27390792Sgshapiro "Milter (%s): socket(%s) not ready", \ 27490792Sgshapiro m->mf_name, (routine)); \ 27590792Sgshapiro } \ 27690792Sgshapiro milter_error(m, e); \ 27764562Sgshapiro return NULL; \ 27864562Sgshapiro } \ 27964562Sgshapiro} 28064562Sgshapiro 28164562Sgshapiro/* 28264562Sgshapiro** Low level functions 28364562Sgshapiro*/ 28464562Sgshapiro 28590792Sgshapiro/* 28664562Sgshapiro** MILTER_READ -- read from a remote milter filter 28764562Sgshapiro** 28864562Sgshapiro** Parameters: 28964562Sgshapiro** m -- milter to read from. 29064562Sgshapiro** cmd -- return param for command read. 29164562Sgshapiro** rlen -- return length of response string. 29264562Sgshapiro** to -- timeout in seconds. 29364562Sgshapiro** e -- current envelope. 29464562Sgshapiro** 29564562Sgshapiro** Returns: 29664562Sgshapiro** response string (may be NULL) 29764562Sgshapiro*/ 29864562Sgshapiro 29964562Sgshapirostatic char * 300168515Sgshapiromilter_sysread(m, buf, sz, to, e, where) 30164562Sgshapiro struct milter *m; 30264562Sgshapiro char *buf; 30364562Sgshapiro ssize_t sz; 30464562Sgshapiro time_t to; 30564562Sgshapiro ENVELOPE *e; 306168515Sgshapiro const char *where; 30764562Sgshapiro{ 30866494Sgshapiro time_t readstart = 0; 30964562Sgshapiro ssize_t len, curl; 31090792Sgshapiro bool started = false; 31164562Sgshapiro 31264562Sgshapiro curl = 0; 31364562Sgshapiro 31464562Sgshapiro if (to > 0) 31564562Sgshapiro readstart = curtime(); 31664562Sgshapiro 31764562Sgshapiro for (;;) 31864562Sgshapiro { 31964562Sgshapiro if (to > 0) 32064562Sgshapiro { 32164562Sgshapiro time_t now; 32264562Sgshapiro 32364562Sgshapiro now = curtime(); 32464562Sgshapiro if (now - readstart >= to) 32564562Sgshapiro { 32664562Sgshapiro if (tTd(64, 5)) 327168515Sgshapiro sm_dprintf("milter_sys_read (%s): timeout %s data read in %s", 328168515Sgshapiro m->mf_name, 32990792Sgshapiro started ? "during" : "before", 330168515Sgshapiro where); 33190792Sgshapiro if (MilterLogLevel > 0) 33264562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 333168515Sgshapiro "Milter (%s): timeout %s data read in %s", 334168515Sgshapiro m->mf_name, 33590792Sgshapiro started ? "during" : "before", 336168515Sgshapiro where); 33790792Sgshapiro milter_error(m, e); 33864562Sgshapiro return NULL; 33964562Sgshapiro } 34064562Sgshapiro to -= now - readstart; 34164562Sgshapiro readstart = now; 342168515Sgshapiro MILTER_TIMEOUT("read", to, false, started, where); 34364562Sgshapiro } 34464562Sgshapiro 34564562Sgshapiro len = read(m->mf_sock, buf + curl, sz - curl); 34664562Sgshapiro 34764562Sgshapiro if (len < 0) 34864562Sgshapiro { 34964562Sgshapiro int save_errno = errno; 35064562Sgshapiro 35164562Sgshapiro if (tTd(64, 5)) 352168515Sgshapiro sm_dprintf("milter_sys_read(%s): read returned %ld: %s\n", 35364562Sgshapiro m->mf_name, (long) len, 35490792Sgshapiro sm_errstring(save_errno)); 35590792Sgshapiro if (MilterLogLevel > 0) 35664562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 35790792Sgshapiro "Milter (%s): read returned %ld: %s", 35864562Sgshapiro m->mf_name, (long) len, 35990792Sgshapiro sm_errstring(save_errno)); 36090792Sgshapiro milter_error(m, e); 36164562Sgshapiro return NULL; 36264562Sgshapiro } 36364562Sgshapiro 36490792Sgshapiro started = true; 36564562Sgshapiro curl += len; 36673188Sgshapiro if (len == 0 || curl >= sz) 36764562Sgshapiro break; 36864562Sgshapiro 36964562Sgshapiro } 37064562Sgshapiro 37164562Sgshapiro if (curl != sz) 37264562Sgshapiro { 37364562Sgshapiro if (tTd(64, 5)) 374168515Sgshapiro sm_dprintf("milter_sys_read(%s): cmd read returned %ld, expecting %ld\n", 37564562Sgshapiro m->mf_name, (long) curl, (long) sz); 37690792Sgshapiro if (MilterLogLevel > 0) 37764562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 378168515Sgshapiro "milter_sys_read(%s): cmd read returned %ld, expecting %ld", 37964562Sgshapiro m->mf_name, (long) curl, (long) sz); 38090792Sgshapiro milter_error(m, e); 38164562Sgshapiro return NULL; 38264562Sgshapiro } 38364562Sgshapiro return buf; 38464562Sgshapiro} 38564562Sgshapiro 38664562Sgshapirostatic char * 387168515Sgshapiromilter_read(m, cmd, rlen, to, e, where) 38864562Sgshapiro struct milter *m; 38964562Sgshapiro char *cmd; 39064562Sgshapiro ssize_t *rlen; 39164562Sgshapiro time_t to; 39264562Sgshapiro ENVELOPE *e; 393168515Sgshapiro const char *where; 39464562Sgshapiro{ 39566494Sgshapiro time_t readstart = 0; 39664562Sgshapiro ssize_t expl; 39764562Sgshapiro mi_int32 i; 398168515Sgshapiro# if MILTER_NO_NAGLE && defined(TCP_CORK) 399132943Sgshapiro int cork = 0; 400168515Sgshapiro# endif /* MILTER_NO_NAGLE && defined(TCP_CORK) */ 40164562Sgshapiro char *buf; 40264562Sgshapiro char data[MILTER_LEN_BYTES + 1]; 40364562Sgshapiro 404147078Sgshapiro if (m->mf_sock < 0) 405147078Sgshapiro { 406147078Sgshapiro if (MilterLogLevel > 0) 407147078Sgshapiro sm_syslog(LOG_ERR, e->e_id, 408168515Sgshapiro "milter_read(%s): socket closed, where=%s", 409168515Sgshapiro m->mf_name, where); 410147078Sgshapiro milter_error(m, e); 411147078Sgshapiro return NULL; 412147078Sgshapiro } 413147078Sgshapiro 41464562Sgshapiro *rlen = 0; 41564562Sgshapiro *cmd = '\0'; 41664562Sgshapiro 41764562Sgshapiro if (to > 0) 41864562Sgshapiro readstart = curtime(); 41964562Sgshapiro 420168515Sgshapiro# if MILTER_NO_NAGLE && defined(TCP_CORK) 421132943Sgshapiro setsockopt(m->mf_sock, IPPROTO_TCP, TCP_CORK, (char *)&cork, 422132943Sgshapiro sizeof(cork)); 423168515Sgshapiro# endif /* MILTER_NO_NAGLE && defined(TCP_CORK) */ 424132943Sgshapiro 425168515Sgshapiro if (milter_sysread(m, data, sizeof(data), to, e, where) == NULL) 42664562Sgshapiro return NULL; 42764562Sgshapiro 428168515Sgshapiro# if MILTER_NO_NAGLE && defined(TCP_CORK) 429132943Sgshapiro cork = 1; 430132943Sgshapiro setsockopt(m->mf_sock, IPPROTO_TCP, TCP_CORK, (char *)&cork, 431132943Sgshapiro sizeof(cork)); 432168515Sgshapiro# endif /* MILTER_NO_NAGLE && defined(TCP_CORK) */ 433132943Sgshapiro 43464562Sgshapiro /* reset timeout */ 43564562Sgshapiro if (to > 0) 43664562Sgshapiro { 43764562Sgshapiro time_t now; 43864562Sgshapiro 43964562Sgshapiro now = curtime(); 44064562Sgshapiro if (now - readstart >= to) 44164562Sgshapiro { 44264562Sgshapiro if (tTd(64, 5)) 443168515Sgshapiro sm_dprintf("milter_read(%s): timeout before data read, where=%s\n", 444168515Sgshapiro m->mf_name, where); 44590792Sgshapiro if (MilterLogLevel > 0) 44664562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 447168515Sgshapiro "Milter read(%s): timeout before data read, where=%s", 448168515Sgshapiro m->mf_name, where); 44990792Sgshapiro milter_error(m, e); 45064562Sgshapiro return NULL; 45164562Sgshapiro } 45264562Sgshapiro to -= now - readstart; 45364562Sgshapiro } 45464562Sgshapiro 45564562Sgshapiro *cmd = data[MILTER_LEN_BYTES]; 45664562Sgshapiro data[MILTER_LEN_BYTES] = '\0'; 45764562Sgshapiro (void) memcpy(&i, data, MILTER_LEN_BYTES); 45864562Sgshapiro expl = ntohl(i) - 1; 45964562Sgshapiro 46064562Sgshapiro if (tTd(64, 25)) 46190792Sgshapiro sm_dprintf("milter_read(%s): expecting %ld bytes\n", 46264562Sgshapiro m->mf_name, (long) expl); 46364562Sgshapiro 46464562Sgshapiro if (expl < 0) 46564562Sgshapiro { 46664562Sgshapiro if (tTd(64, 5)) 467168515Sgshapiro sm_dprintf("milter_read(%s): read size %ld out of range, where=%s\n", 468168515Sgshapiro m->mf_name, (long) expl, where); 46990792Sgshapiro if (MilterLogLevel > 0) 47064562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 471168515Sgshapiro "milter_read(%s): read size %ld out of range, where=%s", 472168515Sgshapiro m->mf_name, (long) expl, where); 47390792Sgshapiro milter_error(m, e); 47464562Sgshapiro return NULL; 47564562Sgshapiro } 47664562Sgshapiro 47764562Sgshapiro if (expl == 0) 47864562Sgshapiro return NULL; 47964562Sgshapiro 48090792Sgshapiro buf = (char *) xalloc(expl); 48164562Sgshapiro 482168515Sgshapiro if (milter_sysread(m, buf, expl, to, e, where) == NULL) 48364562Sgshapiro { 48490792Sgshapiro sm_free(buf); /* XXX */ 48564562Sgshapiro return NULL; 48664562Sgshapiro } 48764562Sgshapiro 48864562Sgshapiro if (tTd(64, 50)) 48990792Sgshapiro sm_dprintf("milter_read(%s): Returning %*s\n", 49064562Sgshapiro m->mf_name, (int) expl, buf); 49164562Sgshapiro *rlen = expl; 49264562Sgshapiro return buf; 49364562Sgshapiro} 494132943Sgshapiro 49590792Sgshapiro/* 49664562Sgshapiro** MILTER_WRITE -- write to a remote milter filter 49764562Sgshapiro** 49864562Sgshapiro** Parameters: 49964562Sgshapiro** m -- milter to read from. 50064562Sgshapiro** cmd -- command to send. 50164562Sgshapiro** buf -- optional command data. 50264562Sgshapiro** len -- length of buf. 50364562Sgshapiro** to -- timeout in seconds. 50464562Sgshapiro** e -- current envelope. 50564562Sgshapiro** 50664562Sgshapiro** Returns: 50764562Sgshapiro** buf if successful, NULL otherwise 50864562Sgshapiro** Not actually used anywhere but function prototype 50964562Sgshapiro** must match milter_read() 51064562Sgshapiro*/ 51164562Sgshapiro 51264562Sgshapirostatic char * 513168515Sgshapiromilter_write(m, cmd, buf, len, to, e, where) 51464562Sgshapiro struct milter *m; 515168515Sgshapiro int cmd; 51664562Sgshapiro char *buf; 51764562Sgshapiro ssize_t len; 51864562Sgshapiro time_t to; 51964562Sgshapiro ENVELOPE *e; 520168515Sgshapiro const char *where; 52164562Sgshapiro{ 52264562Sgshapiro ssize_t sl, i; 523132943Sgshapiro int num_vectors; 52464562Sgshapiro mi_int32 nl; 525168515Sgshapiro char command = (char) cmd; 52664562Sgshapiro char data[MILTER_LEN_BYTES + 1]; 52790792Sgshapiro bool started = false; 528132943Sgshapiro struct iovec vector[2]; 52964562Sgshapiro 530132943Sgshapiro /* 531132943Sgshapiro ** At most two buffers will be written, though 532132943Sgshapiro ** only one may actually be used (see num_vectors). 533132943Sgshapiro ** The first is the size/command and the second is the command data. 534132943Sgshapiro */ 535132943Sgshapiro 536132943Sgshapiro if (len < 0 || len > MilterMaxDataSize) 53764562Sgshapiro { 53864562Sgshapiro if (tTd(64, 5)) 539203004Sgshapiro { 540203004Sgshapiro sm_dprintf("milter_write(%s): length %ld out of range, cmd=%c\n", 541203004Sgshapiro m->mf_name, (long) len, command); 542203004Sgshapiro sm_dprintf("milter_write(%s): buf=%s\n", 543203004Sgshapiro m->mf_name, str2prt(buf)); 544203004Sgshapiro } 54590792Sgshapiro if (MilterLogLevel > 0) 54664562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 547203004Sgshapiro "milter_write(%s): length %ld out of range, cmd=%c", 548203004Sgshapiro m->mf_name, (long) len, command); 54990792Sgshapiro milter_error(m, e); 55064562Sgshapiro return NULL; 55164562Sgshapiro } 552147078Sgshapiro if (m->mf_sock < 0) 553147078Sgshapiro { 554147078Sgshapiro if (MilterLogLevel > 0) 555147078Sgshapiro sm_syslog(LOG_ERR, e->e_id, 556147078Sgshapiro "milter_write(%s): socket closed", 557147078Sgshapiro m->mf_name); 558147078Sgshapiro milter_error(m, e); 559147078Sgshapiro return NULL; 560147078Sgshapiro } 56164562Sgshapiro 56264562Sgshapiro if (tTd(64, 20)) 56390792Sgshapiro sm_dprintf("milter_write(%s): cmd %c, len %ld\n", 564168515Sgshapiro m->mf_name, command, (long) len); 56564562Sgshapiro 566168515Sgshapiro nl = htonl(len + 1); /* add 1 for the command char */ 56764562Sgshapiro (void) memcpy(data, (char *) &nl, MILTER_LEN_BYTES); 568168515Sgshapiro data[MILTER_LEN_BYTES] = command; 56964562Sgshapiro sl = MILTER_LEN_BYTES + 1; 57064562Sgshapiro 571132943Sgshapiro /* set up the vector for the size / command */ 572132943Sgshapiro vector[0].iov_base = (void *) data; 573132943Sgshapiro vector[0].iov_len = sl; 574132943Sgshapiro 575132943Sgshapiro /* 576132943Sgshapiro ** Determine if there is command data. If so, there will be two 577132943Sgshapiro ** vectors. If not, there will be only one. The vectors are set 578132943Sgshapiro ** up here and 'num_vectors' and 'sl' are set appropriately. 579132943Sgshapiro */ 580132943Sgshapiro 581132943Sgshapiro /* NOTE: len<0 has already been checked for. Pedantic */ 582132943Sgshapiro if (len <= 0 || buf == NULL) 58364562Sgshapiro { 584132943Sgshapiro /* There is no command data -- only a size / command data */ 585132943Sgshapiro num_vectors = 1; 58664562Sgshapiro } 587132943Sgshapiro else 58864562Sgshapiro { 589132943Sgshapiro /* 590132943Sgshapiro ** There is both size / command and command data. 591132943Sgshapiro ** Set up the vector for the command data. 592132943Sgshapiro */ 59364562Sgshapiro 594132943Sgshapiro num_vectors = 2; 595132943Sgshapiro sl += len; 596132943Sgshapiro vector[1].iov_base = (void *) buf; 597132943Sgshapiro vector[1].iov_len = len; 598132943Sgshapiro 599132943Sgshapiro if (tTd(64, 50)) 600132943Sgshapiro sm_dprintf("milter_write(%s): Sending %*s\n", 601132943Sgshapiro m->mf_name, (int) len, buf); 60264562Sgshapiro } 60364562Sgshapiro 60464562Sgshapiro if (to > 0) 605168515Sgshapiro MILTER_TIMEOUT("write", to, true, started, where); 60664562Sgshapiro 607132943Sgshapiro /* write the vector(s) */ 608132943Sgshapiro i = writev(m->mf_sock, vector, num_vectors); 609132943Sgshapiro if (i != sl) 61064562Sgshapiro { 61164562Sgshapiro int save_errno = errno; 61264562Sgshapiro 61364562Sgshapiro if (tTd(64, 5)) 61490792Sgshapiro sm_dprintf("milter_write(%s): write(%c) returned %ld, expected %ld: %s\n", 615168515Sgshapiro m->mf_name, command, (long) i, (long) sl, 61690792Sgshapiro sm_errstring(save_errno)); 61790792Sgshapiro if (MilterLogLevel > 0) 61864562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 61990792Sgshapiro "Milter (%s): write(%c) returned %ld, expected %ld: %s", 620168515Sgshapiro m->mf_name, command, (long) i, (long) sl, 62190792Sgshapiro sm_errstring(save_errno)); 62290792Sgshapiro milter_error(m, e); 62364562Sgshapiro return NULL; 62464562Sgshapiro } 62564562Sgshapiro return buf; 62664562Sgshapiro} 62764562Sgshapiro 62864562Sgshapiro/* 62964562Sgshapiro** Utility functions 63064562Sgshapiro*/ 63164562Sgshapiro 63290792Sgshapiro/* 63364562Sgshapiro** MILTER_OPEN -- connect to remote milter filter 63464562Sgshapiro** 63564562Sgshapiro** Parameters: 63664562Sgshapiro** m -- milter to connect to. 63764562Sgshapiro** parseonly -- parse but don't connect. 63864562Sgshapiro** e -- current envelope. 63964562Sgshapiro** 64064562Sgshapiro** Returns: 641111823Sgshapiro** connected socket if successful && !parseonly, 64264562Sgshapiro** 0 upon parse success if parseonly, 64364562Sgshapiro** -1 otherwise. 64464562Sgshapiro*/ 64564562Sgshapiro 64680785Sgshapirostatic jmp_buf MilterConnectTimeout; 64780785Sgshapiro 64864562Sgshapirostatic int 64964562Sgshapiromilter_open(m, parseonly, e) 65064562Sgshapiro struct milter *m; 65164562Sgshapiro bool parseonly; 65264562Sgshapiro ENVELOPE *e; 65364562Sgshapiro{ 65464562Sgshapiro int sock = 0; 65564562Sgshapiro SOCKADDR_LEN_T addrlen = 0; 65664562Sgshapiro int addrno = 0; 65764562Sgshapiro int save_errno; 65864562Sgshapiro char *p; 65964562Sgshapiro char *colon; 66064562Sgshapiro char *at; 66164562Sgshapiro struct hostent *hp = NULL; 66264562Sgshapiro SOCKADDR addr; 66364562Sgshapiro 66464562Sgshapiro if (m->mf_conn == NULL || m->mf_conn[0] == '\0') 66564562Sgshapiro { 66664562Sgshapiro if (tTd(64, 5)) 66790792Sgshapiro sm_dprintf("X%s: empty or missing socket information\n", 66890792Sgshapiro m->mf_name); 66964562Sgshapiro if (parseonly) 67064562Sgshapiro syserr("X%s: empty or missing socket information", 67164562Sgshapiro m->mf_name); 672110560Sgshapiro else if (MilterLogLevel > 0) 67364562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 67490792Sgshapiro "Milter (%s): empty or missing socket information", 67564562Sgshapiro m->mf_name); 67690792Sgshapiro milter_error(m, e); 67764562Sgshapiro return -1; 67864562Sgshapiro } 67964562Sgshapiro 68064562Sgshapiro /* protocol:filename or protocol:port@host */ 681168515Sgshapiro memset(&addr, '\0', sizeof(addr)); 68264562Sgshapiro p = m->mf_conn; 68364562Sgshapiro colon = strchr(p, ':'); 68464562Sgshapiro if (colon != NULL) 68564562Sgshapiro { 68664562Sgshapiro *colon = '\0'; 68764562Sgshapiro 68864562Sgshapiro if (*p == '\0') 68964562Sgshapiro { 69064562Sgshapiro# if NETUNIX 69164562Sgshapiro /* default to AF_UNIX */ 69264562Sgshapiro addr.sa.sa_family = AF_UNIX; 69364562Sgshapiro# else /* NETUNIX */ 69464562Sgshapiro# if NETINET 69564562Sgshapiro /* default to AF_INET */ 69664562Sgshapiro addr.sa.sa_family = AF_INET; 69764562Sgshapiro# else /* NETINET */ 69864562Sgshapiro# if NETINET6 69964562Sgshapiro /* default to AF_INET6 */ 70064562Sgshapiro addr.sa.sa_family = AF_INET6; 70164562Sgshapiro# else /* NETINET6 */ 70264562Sgshapiro /* no protocols available */ 703110560Sgshapiro if (MilterLogLevel > 0) 704110560Sgshapiro sm_syslog(LOG_ERR, e->e_id, 705110560Sgshapiro "Milter (%s): no valid socket protocols available", 706110560Sgshapiro m->mf_name); 70790792Sgshapiro milter_error(m, e); 70864562Sgshapiro return -1; 70964562Sgshapiro# endif /* NETINET6 */ 71064562Sgshapiro# endif /* NETINET */ 71164562Sgshapiro# endif /* NETUNIX */ 71264562Sgshapiro } 71364562Sgshapiro# if NETUNIX 71490792Sgshapiro else if (sm_strcasecmp(p, "unix") == 0 || 71590792Sgshapiro sm_strcasecmp(p, "local") == 0) 71664562Sgshapiro addr.sa.sa_family = AF_UNIX; 71764562Sgshapiro# endif /* NETUNIX */ 71864562Sgshapiro# if NETINET 71990792Sgshapiro else if (sm_strcasecmp(p, "inet") == 0) 72064562Sgshapiro addr.sa.sa_family = AF_INET; 72164562Sgshapiro# endif /* NETINET */ 72264562Sgshapiro# if NETINET6 72390792Sgshapiro else if (sm_strcasecmp(p, "inet6") == 0) 72464562Sgshapiro addr.sa.sa_family = AF_INET6; 72564562Sgshapiro# endif /* NETINET6 */ 72664562Sgshapiro else 72764562Sgshapiro { 72864562Sgshapiro# ifdef EPROTONOSUPPORT 72964562Sgshapiro errno = EPROTONOSUPPORT; 73064562Sgshapiro# else /* EPROTONOSUPPORT */ 73164562Sgshapiro errno = EINVAL; 73264562Sgshapiro# endif /* EPROTONOSUPPORT */ 73364562Sgshapiro if (tTd(64, 5)) 73490792Sgshapiro sm_dprintf("X%s: unknown socket type %s\n", 73564562Sgshapiro m->mf_name, p); 73664562Sgshapiro if (parseonly) 73764562Sgshapiro syserr("X%s: unknown socket type %s", 73864562Sgshapiro m->mf_name, p); 739110560Sgshapiro else if (MilterLogLevel > 0) 74064562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 74190792Sgshapiro "Milter (%s): unknown socket type %s", 74264562Sgshapiro m->mf_name, p); 74390792Sgshapiro milter_error(m, e); 74464562Sgshapiro return -1; 74564562Sgshapiro } 74664562Sgshapiro *colon++ = ':'; 74764562Sgshapiro } 74864562Sgshapiro else 74964562Sgshapiro { 75064562Sgshapiro /* default to AF_UNIX */ 75164562Sgshapiro addr.sa.sa_family = AF_UNIX; 75264562Sgshapiro colon = p; 75364562Sgshapiro } 75464562Sgshapiro 75564562Sgshapiro# if NETUNIX 75664562Sgshapiro if (addr.sa.sa_family == AF_UNIX) 75764562Sgshapiro { 75864562Sgshapiro long sff = SFF_SAFEDIRPATH|SFF_OPENASROOT|SFF_NOLINK|SFF_EXECOK; 75964562Sgshapiro 76064562Sgshapiro at = colon; 761168515Sgshapiro if (strlen(colon) >= sizeof(addr.sunix.sun_path)) 76264562Sgshapiro { 76364562Sgshapiro if (tTd(64, 5)) 76490792Sgshapiro sm_dprintf("X%s: local socket name %s too long\n", 76564562Sgshapiro m->mf_name, colon); 76664562Sgshapiro errno = EINVAL; 76764562Sgshapiro if (parseonly) 76864562Sgshapiro syserr("X%s: local socket name %s too long", 76964562Sgshapiro m->mf_name, colon); 770110560Sgshapiro else if (MilterLogLevel > 0) 77164562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 77290792Sgshapiro "Milter (%s): local socket name %s too long", 77364562Sgshapiro m->mf_name, colon); 77490792Sgshapiro milter_error(m, e); 77564562Sgshapiro return -1; 77664562Sgshapiro } 77764562Sgshapiro errno = safefile(colon, RunAsUid, RunAsGid, RunAsUserName, sff, 77864562Sgshapiro S_IRUSR|S_IWUSR, NULL); 77964562Sgshapiro 78064562Sgshapiro /* if just parsing .cf file, socket doesn't need to exist */ 78164562Sgshapiro if (parseonly && errno == ENOENT) 78264562Sgshapiro { 78364562Sgshapiro if (OpMode == MD_DAEMON || 78464562Sgshapiro OpMode == MD_FGDAEMON) 78590792Sgshapiro (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, 78690792Sgshapiro "WARNING: X%s: local socket name %s missing\n", 78790792Sgshapiro m->mf_name, colon); 78864562Sgshapiro } 78964562Sgshapiro else if (errno != 0) 79064562Sgshapiro { 79164562Sgshapiro /* if not safe, don't create */ 79264562Sgshapiro save_errno = errno; 79364562Sgshapiro if (tTd(64, 5)) 79490792Sgshapiro sm_dprintf("X%s: local socket name %s unsafe\n", 79564562Sgshapiro m->mf_name, colon); 79664562Sgshapiro errno = save_errno; 79764562Sgshapiro if (parseonly) 79864562Sgshapiro { 79964562Sgshapiro if (OpMode == MD_DAEMON || 80064562Sgshapiro OpMode == MD_FGDAEMON || 80164562Sgshapiro OpMode == MD_SMTP) 80264562Sgshapiro syserr("X%s: local socket name %s unsafe", 80364562Sgshapiro m->mf_name, colon); 80464562Sgshapiro } 805110560Sgshapiro else if (MilterLogLevel > 0) 80664562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 80790792Sgshapiro "Milter (%s): local socket name %s unsafe", 80864562Sgshapiro m->mf_name, colon); 80990792Sgshapiro milter_error(m, e); 81064562Sgshapiro return -1; 81164562Sgshapiro } 81264562Sgshapiro 81390792Sgshapiro (void) sm_strlcpy(addr.sunix.sun_path, colon, 814168515Sgshapiro sizeof(addr.sunix.sun_path)); 815168515Sgshapiro addrlen = sizeof(struct sockaddr_un); 81664562Sgshapiro } 81764562Sgshapiro else 81864562Sgshapiro# endif /* NETUNIX */ 81964562Sgshapiro# if NETINET || NETINET6 82090792Sgshapiro if (false 82164562Sgshapiro# if NETINET 82264562Sgshapiro || addr.sa.sa_family == AF_INET 82364562Sgshapiro# endif /* NETINET */ 82464562Sgshapiro# if NETINET6 82564562Sgshapiro || addr.sa.sa_family == AF_INET6 82664562Sgshapiro# endif /* NETINET6 */ 82764562Sgshapiro ) 82864562Sgshapiro { 82990792Sgshapiro unsigned short port; 83064562Sgshapiro 83164562Sgshapiro /* Parse port@host */ 83264562Sgshapiro at = strchr(colon, '@'); 83364562Sgshapiro if (at == NULL) 83464562Sgshapiro { 83564562Sgshapiro if (tTd(64, 5)) 83690792Sgshapiro sm_dprintf("X%s: bad address %s (expected port@host)\n", 83764562Sgshapiro m->mf_name, colon); 83864562Sgshapiro if (parseonly) 83964562Sgshapiro syserr("X%s: bad address %s (expected port@host)", 84064562Sgshapiro m->mf_name, colon); 841110560Sgshapiro else if (MilterLogLevel > 0) 84264562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 84390792Sgshapiro "Milter (%s): bad address %s (expected port@host)", 84464562Sgshapiro m->mf_name, colon); 84590792Sgshapiro milter_error(m, e); 84664562Sgshapiro return -1; 84764562Sgshapiro } 84864562Sgshapiro *at = '\0'; 84964562Sgshapiro if (isascii(*colon) && isdigit(*colon)) 85090792Sgshapiro port = htons((unsigned short) atoi(colon)); 85164562Sgshapiro else 85264562Sgshapiro { 85364562Sgshapiro# ifdef NO_GETSERVBYNAME 85464562Sgshapiro if (tTd(64, 5)) 85590792Sgshapiro sm_dprintf("X%s: invalid port number %s\n", 85664562Sgshapiro m->mf_name, colon); 85764562Sgshapiro if (parseonly) 85864562Sgshapiro syserr("X%s: invalid port number %s", 85964562Sgshapiro m->mf_name, colon); 860110560Sgshapiro else if (MilterLogLevel > 0) 86164562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 86290792Sgshapiro "Milter (%s): invalid port number %s", 86364562Sgshapiro m->mf_name, colon); 86490792Sgshapiro milter_error(m, e); 86564562Sgshapiro return -1; 86664562Sgshapiro# else /* NO_GETSERVBYNAME */ 867168515Sgshapiro struct servent *sp; 86864562Sgshapiro 86964562Sgshapiro sp = getservbyname(colon, "tcp"); 87064562Sgshapiro if (sp == NULL) 87164562Sgshapiro { 87264562Sgshapiro save_errno = errno; 87364562Sgshapiro if (tTd(64, 5)) 87490792Sgshapiro sm_dprintf("X%s: unknown port name %s\n", 87564562Sgshapiro m->mf_name, colon); 87664562Sgshapiro errno = save_errno; 87764562Sgshapiro if (parseonly) 87864562Sgshapiro syserr("X%s: unknown port name %s", 87964562Sgshapiro m->mf_name, colon); 880110560Sgshapiro else if (MilterLogLevel > 0) 88164562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 88290792Sgshapiro "Milter (%s): unknown port name %s", 88364562Sgshapiro m->mf_name, colon); 88490792Sgshapiro milter_error(m, e); 88564562Sgshapiro return -1; 88664562Sgshapiro } 88764562Sgshapiro port = sp->s_port; 88864562Sgshapiro# endif /* NO_GETSERVBYNAME */ 88964562Sgshapiro } 89064562Sgshapiro *at++ = '@'; 89164562Sgshapiro if (*at == '[') 89264562Sgshapiro { 89364562Sgshapiro char *end; 89464562Sgshapiro 89564562Sgshapiro end = strchr(at, ']'); 89664562Sgshapiro if (end != NULL) 89764562Sgshapiro { 89890792Sgshapiro bool found = false; 89964562Sgshapiro# if NETINET 90064562Sgshapiro unsigned long hid = INADDR_NONE; 90164562Sgshapiro# endif /* NETINET */ 90264562Sgshapiro# if NETINET6 90364562Sgshapiro struct sockaddr_in6 hid6; 90464562Sgshapiro# endif /* NETINET6 */ 90564562Sgshapiro 90664562Sgshapiro *end = '\0'; 90764562Sgshapiro# if NETINET 90864562Sgshapiro if (addr.sa.sa_family == AF_INET && 90964562Sgshapiro (hid = inet_addr(&at[1])) != INADDR_NONE) 91064562Sgshapiro { 91164562Sgshapiro addr.sin.sin_addr.s_addr = hid; 91264562Sgshapiro addr.sin.sin_port = port; 91390792Sgshapiro found = true; 91464562Sgshapiro } 91564562Sgshapiro# endif /* NETINET */ 91664562Sgshapiro# if NETINET6 917168515Sgshapiro (void) memset(&hid6, '\0', sizeof(hid6)); 91864562Sgshapiro if (addr.sa.sa_family == AF_INET6 && 91990792Sgshapiro anynet_pton(AF_INET6, &at[1], 92090792Sgshapiro &hid6.sin6_addr) == 1) 92164562Sgshapiro { 92264562Sgshapiro addr.sin6.sin6_addr = hid6.sin6_addr; 92364562Sgshapiro addr.sin6.sin6_port = port; 92490792Sgshapiro found = true; 92564562Sgshapiro } 92664562Sgshapiro# endif /* NETINET6 */ 92764562Sgshapiro *end = ']'; 92864562Sgshapiro if (!found) 92964562Sgshapiro { 93064562Sgshapiro if (tTd(64, 5)) 93190792Sgshapiro sm_dprintf("X%s: Invalid numeric domain spec \"%s\"\n", 93264562Sgshapiro m->mf_name, at); 93364562Sgshapiro if (parseonly) 93464562Sgshapiro syserr("X%s: Invalid numeric domain spec \"%s\"", 93564562Sgshapiro m->mf_name, at); 936110560Sgshapiro else if (MilterLogLevel > 0) 93764562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 93890792Sgshapiro "Milter (%s): Invalid numeric domain spec \"%s\"", 93964562Sgshapiro m->mf_name, at); 94090792Sgshapiro milter_error(m, e); 94164562Sgshapiro return -1; 94264562Sgshapiro } 94364562Sgshapiro } 94464562Sgshapiro else 94564562Sgshapiro { 94664562Sgshapiro if (tTd(64, 5)) 94790792Sgshapiro sm_dprintf("X%s: Invalid numeric domain spec \"%s\"\n", 94864562Sgshapiro m->mf_name, at); 94964562Sgshapiro if (parseonly) 95064562Sgshapiro syserr("X%s: Invalid numeric domain spec \"%s\"", 95164562Sgshapiro m->mf_name, at); 952110560Sgshapiro else if (MilterLogLevel > 0) 95364562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 95490792Sgshapiro "Milter (%s): Invalid numeric domain spec \"%s\"", 95564562Sgshapiro m->mf_name, at); 95690792Sgshapiro milter_error(m, e); 95764562Sgshapiro return -1; 95864562Sgshapiro } 95964562Sgshapiro } 96064562Sgshapiro else 96164562Sgshapiro { 96264562Sgshapiro hp = sm_gethostbyname(at, addr.sa.sa_family); 96364562Sgshapiro if (hp == NULL) 96464562Sgshapiro { 96564562Sgshapiro save_errno = errno; 96664562Sgshapiro if (tTd(64, 5)) 96790792Sgshapiro sm_dprintf("X%s: Unknown host name %s\n", 96890792Sgshapiro m->mf_name, at); 96964562Sgshapiro errno = save_errno; 97064562Sgshapiro if (parseonly) 97164562Sgshapiro syserr("X%s: Unknown host name %s", 97264562Sgshapiro m->mf_name, at); 973110560Sgshapiro else if (MilterLogLevel > 0) 97464562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 97590792Sgshapiro "Milter (%s): Unknown host name %s", 97664562Sgshapiro m->mf_name, at); 97790792Sgshapiro milter_error(m, e); 97864562Sgshapiro return -1; 97964562Sgshapiro } 98064562Sgshapiro addr.sa.sa_family = hp->h_addrtype; 98164562Sgshapiro switch (hp->h_addrtype) 98264562Sgshapiro { 98364562Sgshapiro# if NETINET 98464562Sgshapiro case AF_INET: 98564562Sgshapiro memmove(&addr.sin.sin_addr, 98690792Sgshapiro hp->h_addr, INADDRSZ); 98764562Sgshapiro addr.sin.sin_port = port; 988168515Sgshapiro addrlen = sizeof(struct sockaddr_in); 98964562Sgshapiro addrno = 1; 99064562Sgshapiro break; 99164562Sgshapiro# endif /* NETINET */ 99264562Sgshapiro 99364562Sgshapiro# if NETINET6 99464562Sgshapiro case AF_INET6: 99564562Sgshapiro memmove(&addr.sin6.sin6_addr, 99690792Sgshapiro hp->h_addr, IN6ADDRSZ); 99764562Sgshapiro addr.sin6.sin6_port = port; 998168515Sgshapiro addrlen = sizeof(struct sockaddr_in6); 99964562Sgshapiro addrno = 1; 100064562Sgshapiro break; 100164562Sgshapiro# endif /* NETINET6 */ 100264562Sgshapiro 100364562Sgshapiro default: 100464562Sgshapiro if (tTd(64, 5)) 100590792Sgshapiro sm_dprintf("X%s: Unknown protocol for %s (%d)\n", 100690792Sgshapiro m->mf_name, at, 100790792Sgshapiro hp->h_addrtype); 100864562Sgshapiro if (parseonly) 100964562Sgshapiro syserr("X%s: Unknown protocol for %s (%d)", 101064562Sgshapiro m->mf_name, at, hp->h_addrtype); 1011110560Sgshapiro else if (MilterLogLevel > 0) 101264562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 101390792Sgshapiro "Milter (%s): Unknown protocol for %s (%d)", 101464562Sgshapiro m->mf_name, at, 101564562Sgshapiro hp->h_addrtype); 101690792Sgshapiro milter_error(m, e); 101790792Sgshapiro# if NETINET6 101871345Sgshapiro freehostent(hp); 101990792Sgshapiro# endif /* NETINET6 */ 102064562Sgshapiro return -1; 102164562Sgshapiro } 102264562Sgshapiro } 102364562Sgshapiro } 102464562Sgshapiro else 102564562Sgshapiro# endif /* NETINET || NETINET6 */ 102664562Sgshapiro { 102764562Sgshapiro if (tTd(64, 5)) 102890792Sgshapiro sm_dprintf("X%s: unknown socket protocol\n", 102990792Sgshapiro m->mf_name); 103064562Sgshapiro if (parseonly) 103164562Sgshapiro syserr("X%s: unknown socket protocol", m->mf_name); 1032110560Sgshapiro else if (MilterLogLevel > 0) 103364562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 103490792Sgshapiro "Milter (%s): unknown socket protocol", 103590792Sgshapiro m->mf_name); 103690792Sgshapiro milter_error(m, e); 103764562Sgshapiro return -1; 103864562Sgshapiro } 103964562Sgshapiro 104064562Sgshapiro /* just parsing through? */ 104164562Sgshapiro if (parseonly) 104264562Sgshapiro { 104364562Sgshapiro m->mf_state = SMFS_READY; 104490792Sgshapiro# if NETINET6 104571345Sgshapiro if (hp != NULL) 104671345Sgshapiro freehostent(hp); 104790792Sgshapiro# endif /* NETINET6 */ 104864562Sgshapiro return 0; 104964562Sgshapiro } 105064562Sgshapiro 105164562Sgshapiro /* sanity check */ 105264562Sgshapiro if (m->mf_state != SMFS_READY && 105364562Sgshapiro m->mf_state != SMFS_CLOSED) 105464562Sgshapiro { 105564562Sgshapiro /* shouldn't happen */ 105664562Sgshapiro if (tTd(64, 1)) 105790792Sgshapiro sm_dprintf("Milter (%s): Trying to open filter in state %c\n", 105890792Sgshapiro m->mf_name, (char) m->mf_state); 105990792Sgshapiro milter_error(m, e); 106090792Sgshapiro# if NETINET6 106171345Sgshapiro if (hp != NULL) 106271345Sgshapiro freehostent(hp); 106390792Sgshapiro# endif /* NETINET6 */ 106464562Sgshapiro return -1; 106564562Sgshapiro } 106664562Sgshapiro 106764562Sgshapiro /* nope, actually connecting */ 106864562Sgshapiro for (;;) 106964562Sgshapiro { 107064562Sgshapiro sock = socket(addr.sa.sa_family, SOCK_STREAM, 0); 107164562Sgshapiro if (sock < 0) 107264562Sgshapiro { 107364562Sgshapiro save_errno = errno; 107464562Sgshapiro if (tTd(64, 5)) 107590792Sgshapiro sm_dprintf("Milter (%s): error creating socket: %s\n", 107690792Sgshapiro m->mf_name, 107790792Sgshapiro sm_errstring(save_errno)); 107890792Sgshapiro if (MilterLogLevel > 0) 107964562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 108090792Sgshapiro "Milter (%s): error creating socket: %s", 108190792Sgshapiro m->mf_name, sm_errstring(save_errno)); 108290792Sgshapiro milter_error(m, e); 108390792Sgshapiro# if NETINET6 108471345Sgshapiro if (hp != NULL) 108571345Sgshapiro freehostent(hp); 108690792Sgshapiro# endif /* NETINET6 */ 108764562Sgshapiro return -1; 108864562Sgshapiro } 108964562Sgshapiro 109080785Sgshapiro if (setjmp(MilterConnectTimeout) == 0) 109180785Sgshapiro { 109290792Sgshapiro SM_EVENT *ev = NULL; 109380785Sgshapiro int i; 109464562Sgshapiro 109580785Sgshapiro if (m->mf_timeout[SMFTO_CONNECT] > 0) 109690792Sgshapiro ev = sm_setevent(m->mf_timeout[SMFTO_CONNECT], 109790792Sgshapiro milter_connect_timeout, 0); 109880785Sgshapiro 109980785Sgshapiro i = connect(sock, (struct sockaddr *) &addr, addrlen); 110080785Sgshapiro save_errno = errno; 110180785Sgshapiro if (ev != NULL) 110290792Sgshapiro sm_clrevent(ev); 110380785Sgshapiro errno = save_errno; 110480785Sgshapiro if (i >= 0) 110580785Sgshapiro break; 110680785Sgshapiro } 110780785Sgshapiro 110864562Sgshapiro /* couldn't connect.... try next address */ 110964562Sgshapiro save_errno = errno; 111066494Sgshapiro p = CurHostName; 111166494Sgshapiro CurHostName = at; 111264562Sgshapiro if (tTd(64, 5)) 111390792Sgshapiro sm_dprintf("milter_open (%s): open %s failed: %s\n", 111490792Sgshapiro m->mf_name, at, sm_errstring(save_errno)); 111590792Sgshapiro if (MilterLogLevel > 13) 111664562Sgshapiro sm_syslog(LOG_INFO, e->e_id, 111790792Sgshapiro "Milter (%s): open %s failed: %s", 111890792Sgshapiro m->mf_name, at, sm_errstring(save_errno)); 111966494Sgshapiro CurHostName = p; 112064562Sgshapiro (void) close(sock); 112164562Sgshapiro 112264562Sgshapiro /* try next address */ 112364562Sgshapiro if (hp != NULL && hp->h_addr_list[addrno] != NULL) 112464562Sgshapiro { 112564562Sgshapiro switch (addr.sa.sa_family) 112664562Sgshapiro { 112764562Sgshapiro# if NETINET 112864562Sgshapiro case AF_INET: 112964562Sgshapiro memmove(&addr.sin.sin_addr, 113064562Sgshapiro hp->h_addr_list[addrno++], 113164562Sgshapiro INADDRSZ); 113264562Sgshapiro break; 113364562Sgshapiro# endif /* NETINET */ 113464562Sgshapiro 113564562Sgshapiro# if NETINET6 113664562Sgshapiro case AF_INET6: 113764562Sgshapiro memmove(&addr.sin6.sin6_addr, 113864562Sgshapiro hp->h_addr_list[addrno++], 113964562Sgshapiro IN6ADDRSZ); 114064562Sgshapiro break; 114164562Sgshapiro# endif /* NETINET6 */ 114264562Sgshapiro 114364562Sgshapiro default: 114464562Sgshapiro if (tTd(64, 5)) 114590792Sgshapiro sm_dprintf("X%s: Unknown protocol for %s (%d)\n", 114690792Sgshapiro m->mf_name, at, 114790792Sgshapiro hp->h_addrtype); 114890792Sgshapiro if (MilterLogLevel > 0) 114964562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 115090792Sgshapiro "Milter (%s): Unknown protocol for %s (%d)", 115164562Sgshapiro m->mf_name, at, 115264562Sgshapiro hp->h_addrtype); 115390792Sgshapiro milter_error(m, e); 115490792Sgshapiro# if NETINET6 115571345Sgshapiro freehostent(hp); 115690792Sgshapiro# endif /* NETINET6 */ 115764562Sgshapiro return -1; 115864562Sgshapiro } 115964562Sgshapiro continue; 116064562Sgshapiro } 116180785Sgshapiro p = CurHostName; 116280785Sgshapiro CurHostName = at; 116364562Sgshapiro if (tTd(64, 5)) 116490792Sgshapiro sm_dprintf("X%s: error connecting to filter: %s\n", 116590792Sgshapiro m->mf_name, sm_errstring(save_errno)); 116690792Sgshapiro if (MilterLogLevel > 0) 116764562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 116890792Sgshapiro "Milter (%s): error connecting to filter: %s", 116990792Sgshapiro m->mf_name, sm_errstring(save_errno)); 117080785Sgshapiro CurHostName = p; 117190792Sgshapiro milter_error(m, e); 117290792Sgshapiro# if NETINET6 117371345Sgshapiro if (hp != NULL) 117471345Sgshapiro freehostent(hp); 117590792Sgshapiro# endif /* NETINET6 */ 117664562Sgshapiro return -1; 117764562Sgshapiro } 117864562Sgshapiro m->mf_state = SMFS_OPEN; 117990792Sgshapiro# if NETINET6 118071345Sgshapiro if (hp != NULL) 118171345Sgshapiro { 118271345Sgshapiro freehostent(hp); 118371345Sgshapiro hp = NULL; 118471345Sgshapiro } 118590792Sgshapiro# endif /* NETINET6 */ 1186168515Sgshapiro# if MILTER_NO_NAGLE && !defined(TCP_CORK) 1187132943Sgshapiro { 1188132943Sgshapiro int nodelay = 1; 1189132943Sgshapiro 1190132943Sgshapiro setsockopt(m->mf_sock, IPPROTO_TCP, TCP_NODELAY, 1191132943Sgshapiro (char *)&nodelay, sizeof(nodelay)); 1192132943Sgshapiro } 1193168515Sgshapiro# endif /* MILTER_NO_NAGLE && !defined(TCP_CORK) */ 119464562Sgshapiro return sock; 119564562Sgshapiro} 119680785Sgshapiro 119780785Sgshapirostatic void 1198141858Sgshapiromilter_connect_timeout(ignore) 1199141858Sgshapiro int ignore; 120080785Sgshapiro{ 120180785Sgshapiro /* 120280785Sgshapiro ** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD 120380785Sgshapiro ** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE 120480785Sgshapiro ** DOING. 120580785Sgshapiro */ 120680785Sgshapiro 120780785Sgshapiro errno = ETIMEDOUT; 120880785Sgshapiro longjmp(MilterConnectTimeout, 1); 120980785Sgshapiro} 1210168515Sgshapiro 121190792Sgshapiro/* 121264562Sgshapiro** MILTER_SETUP -- setup structure for a mail filter 121364562Sgshapiro** 121464562Sgshapiro** Parameters: 121564562Sgshapiro** line -- the options line. 121664562Sgshapiro** 121764562Sgshapiro** Returns: 121864562Sgshapiro** none 121964562Sgshapiro*/ 122064562Sgshapiro 122164562Sgshapirovoid 122264562Sgshapiromilter_setup(line) 122364562Sgshapiro char *line; 122464562Sgshapiro{ 122564562Sgshapiro char fcode; 1226168515Sgshapiro char *p; 1227168515Sgshapiro struct milter *m; 122864562Sgshapiro STAB *s; 1229244928Sgshapiro static int idx = 0; 123064562Sgshapiro 123166494Sgshapiro /* collect the filter name */ 123264562Sgshapiro for (p = line; 123364562Sgshapiro *p != '\0' && *p != ',' && !(isascii(*p) && isspace(*p)); 123464562Sgshapiro p++) 123564562Sgshapiro continue; 123664562Sgshapiro if (*p != '\0') 123764562Sgshapiro *p++ = '\0'; 123864562Sgshapiro if (line[0] == '\0') 123964562Sgshapiro { 124064562Sgshapiro syserr("name required for mail filter"); 124164562Sgshapiro return; 124264562Sgshapiro } 1243168515Sgshapiro m = (struct milter *) xalloc(sizeof(*m)); 1244168515Sgshapiro memset((char *) m, '\0', sizeof(*m)); 124564562Sgshapiro m->mf_name = newstr(line); 124664562Sgshapiro m->mf_state = SMFS_READY; 124764562Sgshapiro m->mf_sock = -1; 124890792Sgshapiro m->mf_timeout[SMFTO_CONNECT] = (time_t) 300; 124964562Sgshapiro m->mf_timeout[SMFTO_WRITE] = (time_t) 10; 125064562Sgshapiro m->mf_timeout[SMFTO_READ] = (time_t) 10; 125164562Sgshapiro m->mf_timeout[SMFTO_EOM] = (time_t) 300; 1252168515Sgshapiro#if _FFR_MILTER_CHECK 1253168515Sgshapiro m->mf_mta_prot_version = SMFI_PROT_VERSION; 1254168515Sgshapiro m->mf_mta_prot_flags = SMFI_CURR_PROT; 1255168515Sgshapiro m->mf_mta_actions = SMFI_CURR_ACTS; 1256168515Sgshapiro#endif /* _FFR_MILTER_CHECK */ 125764562Sgshapiro 125864562Sgshapiro /* now scan through and assign info from the fields */ 125964562Sgshapiro while (*p != '\0') 126064562Sgshapiro { 126164562Sgshapiro char *delimptr; 126264562Sgshapiro 126364562Sgshapiro while (*p != '\0' && 126464562Sgshapiro (*p == ',' || (isascii(*p) && isspace(*p)))) 126564562Sgshapiro p++; 126664562Sgshapiro 126764562Sgshapiro /* p now points to field code */ 126864562Sgshapiro fcode = *p; 126964562Sgshapiro while (*p != '\0' && *p != '=' && *p != ',') 127064562Sgshapiro p++; 127164562Sgshapiro if (*p++ != '=') 127264562Sgshapiro { 127364562Sgshapiro syserr("X%s: `=' expected", m->mf_name); 127464562Sgshapiro return; 127564562Sgshapiro } 127664562Sgshapiro while (isascii(*p) && isspace(*p)) 127764562Sgshapiro p++; 127864562Sgshapiro 127964562Sgshapiro /* p now points to the field body */ 128064562Sgshapiro p = munchstring(p, &delimptr, ','); 128164562Sgshapiro 128266494Sgshapiro /* install the field into the filter struct */ 128364562Sgshapiro switch (fcode) 128464562Sgshapiro { 128564562Sgshapiro case 'S': /* socket */ 128664562Sgshapiro if (p == NULL) 128764562Sgshapiro m->mf_conn = NULL; 128864562Sgshapiro else 128964562Sgshapiro m->mf_conn = newstr(p); 129064562Sgshapiro break; 129164562Sgshapiro 129264562Sgshapiro case 'F': /* Milter flags configured on MTA */ 129364562Sgshapiro for (; *p != '\0'; p++) 129464562Sgshapiro { 129564562Sgshapiro if (!(isascii(*p) && isspace(*p))) 129671345Sgshapiro setbitn(bitidx(*p), m->mf_flags); 129764562Sgshapiro } 129864562Sgshapiro break; 129964562Sgshapiro 130064562Sgshapiro case 'T': /* timeouts */ 130164562Sgshapiro milter_parse_timeouts(p, m); 130264562Sgshapiro break; 130364562Sgshapiro 1304168515Sgshapiro#if _FFR_MILTER_CHECK 1305168515Sgshapiro case 'a': 1306168515Sgshapiro m->mf_mta_actions = strtoul(p, NULL, 0); 1307168515Sgshapiro break; 1308168515Sgshapiro case 'f': 1309168515Sgshapiro m->mf_mta_prot_flags = strtoul(p, NULL, 0); 1310168515Sgshapiro break; 1311168515Sgshapiro case 'v': 1312168515Sgshapiro m->mf_mta_prot_version = strtoul(p, NULL, 0); 1313168515Sgshapiro break; 1314168515Sgshapiro#endif /* _FFR_MILTER_CHECK */ 1315168515Sgshapiro 131664562Sgshapiro default: 131764562Sgshapiro syserr("X%s: unknown filter equate %c=", 131864562Sgshapiro m->mf_name, fcode); 131964562Sgshapiro break; 132064562Sgshapiro } 132164562Sgshapiro p = delimptr; 132264562Sgshapiro } 132364562Sgshapiro 132464562Sgshapiro /* early check for errors */ 132590792Sgshapiro (void) milter_open(m, true, CurEnv); 132664562Sgshapiro 132766494Sgshapiro /* enter the filter into the symbol table */ 132864562Sgshapiro s = stab(m->mf_name, ST_MILTER, ST_ENTER); 132964562Sgshapiro if (s->s_milter != NULL) 133064562Sgshapiro syserr("X%s: duplicate filter definition", m->mf_name); 133164562Sgshapiro else 1332244928Sgshapiro { 133364562Sgshapiro s->s_milter = m; 1334244928Sgshapiro m->mf_idx = ++idx; 1335244928Sgshapiro } 133664562Sgshapiro} 1337168515Sgshapiro 133890792Sgshapiro/* 133990792Sgshapiro** MILTER_CONFIG -- parse option list into an array and check config 134064562Sgshapiro** 134164562Sgshapiro** Called when reading configuration file. 134264562Sgshapiro** 134364562Sgshapiro** Parameters: 134464562Sgshapiro** spec -- the filter list. 134564562Sgshapiro** list -- the array to fill in. 134664562Sgshapiro** max -- the maximum number of entries in list. 134764562Sgshapiro** 134864562Sgshapiro** Returns: 134964562Sgshapiro** none 135064562Sgshapiro*/ 135164562Sgshapiro 135264562Sgshapirovoid 135390792Sgshapiromilter_config(spec, list, max) 135464562Sgshapiro char *spec; 135564562Sgshapiro struct milter **list; 135664562Sgshapiro int max; 135764562Sgshapiro{ 135864562Sgshapiro int numitems = 0; 1359168515Sgshapiro char *p; 136064562Sgshapiro 136164562Sgshapiro /* leave one for the NULL signifying the end of the list */ 136264562Sgshapiro max--; 136364562Sgshapiro 136464562Sgshapiro for (p = spec; p != NULL; ) 136564562Sgshapiro { 136664562Sgshapiro STAB *s; 136764562Sgshapiro 136864562Sgshapiro while (isascii(*p) && isspace(*p)) 136964562Sgshapiro p++; 137064562Sgshapiro if (*p == '\0') 137164562Sgshapiro break; 137264562Sgshapiro spec = p; 137364562Sgshapiro 137464562Sgshapiro if (numitems >= max) 137564562Sgshapiro { 137664562Sgshapiro syserr("Too many filters defined, %d max", max); 137764562Sgshapiro if (max > 0) 137864562Sgshapiro list[0] = NULL; 137964562Sgshapiro return; 138064562Sgshapiro } 138190792Sgshapiro p = strpbrk(p, ";,"); 138264562Sgshapiro if (p != NULL) 138364562Sgshapiro *p++ = '\0'; 138464562Sgshapiro 138564562Sgshapiro s = stab(spec, ST_MILTER, ST_FIND); 138664562Sgshapiro if (s == NULL) 138764562Sgshapiro { 138864562Sgshapiro syserr("InputFilter %s not defined", spec); 138964562Sgshapiro ExitStat = EX_CONFIG; 139064562Sgshapiro return; 139164562Sgshapiro } 139264562Sgshapiro list[numitems++] = s->s_milter; 139364562Sgshapiro } 139464562Sgshapiro list[numitems] = NULL; 139590792Sgshapiro 139690792Sgshapiro /* if not set, set to LogLevel */ 139790792Sgshapiro if (MilterLogLevel == -1) 139890792Sgshapiro MilterLogLevel = LogLevel; 139964562Sgshapiro} 1400168515Sgshapiro 140190792Sgshapiro/* 140264562Sgshapiro** MILTER_PARSE_TIMEOUTS -- parse timeout list 140364562Sgshapiro** 140464562Sgshapiro** Called when reading configuration file. 140564562Sgshapiro** 140664562Sgshapiro** Parameters: 140764562Sgshapiro** spec -- the timeout list. 140864562Sgshapiro** m -- milter to set. 140964562Sgshapiro** 141064562Sgshapiro** Returns: 141164562Sgshapiro** none 141264562Sgshapiro*/ 141364562Sgshapiro 141464562Sgshapirostatic void 141564562Sgshapiromilter_parse_timeouts(spec, m) 141664562Sgshapiro char *spec; 141764562Sgshapiro struct milter *m; 141864562Sgshapiro{ 141964562Sgshapiro char fcode; 1420132943Sgshapiro int tcode; 1421168515Sgshapiro char *p; 142264562Sgshapiro 142364562Sgshapiro p = spec; 142464562Sgshapiro 142564562Sgshapiro /* now scan through and assign info from the fields */ 142664562Sgshapiro while (*p != '\0') 142764562Sgshapiro { 142864562Sgshapiro char *delimptr; 142964562Sgshapiro 143064562Sgshapiro while (*p != '\0' && 143164562Sgshapiro (*p == ';' || (isascii(*p) && isspace(*p)))) 143264562Sgshapiro p++; 143364562Sgshapiro 143464562Sgshapiro /* p now points to field code */ 143564562Sgshapiro fcode = *p; 143664562Sgshapiro while (*p != '\0' && *p != ':') 143764562Sgshapiro p++; 143864562Sgshapiro if (*p++ != ':') 143964562Sgshapiro { 144064562Sgshapiro syserr("X%s, T=: `:' expected", m->mf_name); 144164562Sgshapiro return; 144264562Sgshapiro } 144364562Sgshapiro while (isascii(*p) && isspace(*p)) 144464562Sgshapiro p++; 144564562Sgshapiro 144664562Sgshapiro /* p now points to the field body */ 144764562Sgshapiro p = munchstring(p, &delimptr, ';'); 1448132943Sgshapiro tcode = -1; 144964562Sgshapiro 145066494Sgshapiro /* install the field into the filter struct */ 145164562Sgshapiro switch (fcode) 145264562Sgshapiro { 145382017Sgshapiro case 'C': 1454132943Sgshapiro tcode = SMFTO_CONNECT; 145582017Sgshapiro break; 145682017Sgshapiro 145764562Sgshapiro case 'S': 1458132943Sgshapiro tcode = SMFTO_WRITE; 145964562Sgshapiro break; 146064562Sgshapiro 146164562Sgshapiro case 'R': 1462132943Sgshapiro tcode = SMFTO_READ; 146364562Sgshapiro break; 146464562Sgshapiro 146564562Sgshapiro case 'E': 1466132943Sgshapiro tcode = SMFTO_EOM; 146764562Sgshapiro break; 146864562Sgshapiro 146964562Sgshapiro default: 147064562Sgshapiro if (tTd(64, 5)) 147190792Sgshapiro sm_dprintf("X%s: %c unknown\n", 147290792Sgshapiro m->mf_name, fcode); 147364562Sgshapiro syserr("X%s: unknown filter timeout %c", 147464562Sgshapiro m->mf_name, fcode); 147564562Sgshapiro break; 147664562Sgshapiro } 1477132943Sgshapiro if (tcode >= 0) 1478132943Sgshapiro { 1479132943Sgshapiro m->mf_timeout[tcode] = convtime(p, 's'); 1480132943Sgshapiro if (tTd(64, 5)) 1481132943Sgshapiro sm_dprintf("X%s: %c=%ld\n", 1482132943Sgshapiro m->mf_name, fcode, 1483132943Sgshapiro (u_long) m->mf_timeout[tcode]); 1484132943Sgshapiro } 148564562Sgshapiro p = delimptr; 148664562Sgshapiro } 148764562Sgshapiro} 1488168515Sgshapiro 148990792Sgshapiro/* 1490168515Sgshapiro** MILTER_SET_MACROS -- set milter macros 1491168515Sgshapiro** 1492168515Sgshapiro** Parameters: 1493168515Sgshapiro** name -- name of milter. 1494168515Sgshapiro** macros -- where to store macros. 1495168515Sgshapiro** val -- the value of the option. 1496168515Sgshapiro** nummac -- current number of macros 1497168515Sgshapiro** 1498168515Sgshapiro** Returns: 1499168515Sgshapiro** new number of macros 1500168515Sgshapiro*/ 1501168515Sgshapiro 1502168515Sgshapirostatic int 1503168515Sgshapiromilter_set_macros(name, macros, val, nummac) 1504168515Sgshapiro char *name; 1505168515Sgshapiro char **macros; 1506168515Sgshapiro char *val; 1507168515Sgshapiro int nummac; 1508168515Sgshapiro{ 1509168515Sgshapiro char *p; 1510168515Sgshapiro 1511168515Sgshapiro p = newstr(val); 1512168515Sgshapiro while (*p != '\0') 1513168515Sgshapiro { 1514168515Sgshapiro char *macro; 1515168515Sgshapiro 1516168515Sgshapiro /* Skip leading commas, spaces */ 1517168515Sgshapiro while (*p != '\0' && 1518168515Sgshapiro (*p == ',' || (isascii(*p) && isspace(*p)))) 1519168515Sgshapiro p++; 1520168515Sgshapiro 1521168515Sgshapiro if (*p == '\0') 1522168515Sgshapiro break; 1523168515Sgshapiro 1524168515Sgshapiro /* Find end of macro */ 1525168515Sgshapiro macro = p; 1526168515Sgshapiro while (*p != '\0' && *p != ',' && 1527168515Sgshapiro isascii(*p) && !isspace(*p)) 1528168515Sgshapiro p++; 1529168515Sgshapiro if (*p != '\0') 1530168515Sgshapiro *p++ = '\0'; 1531168515Sgshapiro 1532168515Sgshapiro if (nummac >= MAXFILTERMACROS) 1533168515Sgshapiro { 1534168515Sgshapiro syserr("milter_set_option: too many macros in Milter.%s (max %d)", 1535168515Sgshapiro name, MAXFILTERMACROS); 1536168515Sgshapiro macros[nummac] = NULL; 1537168515Sgshapiro return -1; 1538168515Sgshapiro } 1539168515Sgshapiro macros[nummac++] = macro; 1540168515Sgshapiro } 1541168515Sgshapiro macros[nummac] = NULL; 1542168515Sgshapiro return nummac; 1543168515Sgshapiro} 1544168515Sgshapiro 1545168515Sgshapiro/* 154664562Sgshapiro** MILTER_SET_OPTION -- set an individual milter option 154764562Sgshapiro** 154864562Sgshapiro** Parameters: 154964562Sgshapiro** name -- the name of the option. 155064562Sgshapiro** val -- the value of the option. 155164562Sgshapiro** sticky -- if set, don't let other setoptions override 155264562Sgshapiro** this value. 155364562Sgshapiro** 155464562Sgshapiro** Returns: 155564562Sgshapiro** none. 155664562Sgshapiro*/ 155764562Sgshapiro 155864562Sgshapiro/* set if Milter sub-option is stuck */ 155964562Sgshapirostatic BITMAP256 StickyMilterOpt; 156064562Sgshapiro 156164562Sgshapirostatic struct milteropt 156264562Sgshapiro{ 156390792Sgshapiro char *mo_name; /* long name of milter option */ 156490792Sgshapiro unsigned char mo_code; /* code for option */ 156564562Sgshapiro} MilterOptTab[] = 156664562Sgshapiro{ 1567244928Sgshapiro { "macros.connect", SMFIM_CONNECT }, 1568244928Sgshapiro { "macros.helo", SMFIM_HELO }, 1569244928Sgshapiro { "macros.envfrom", SMFIM_ENVFROM }, 1570244928Sgshapiro { "macros.envrcpt", SMFIM_ENVRCPT }, 1571244928Sgshapiro { "macros.data", SMFIM_DATA }, 1572244928Sgshapiro { "macros.eom", SMFIM_EOM }, 1573244928Sgshapiro { "macros.eoh", SMFIM_EOH }, 1574168515Sgshapiro 1575132943Sgshapiro# define MO_LOGLEVEL 0x07 1576132943Sgshapiro { "loglevel", MO_LOGLEVEL }, 1577203004Sgshapiro# if _FFR_MAXDATASIZE || _FFR_MDS_NEGOTIATE 1578168515Sgshapiro# define MO_MAXDATASIZE 0x08 1579132943Sgshapiro { "maxdatasize", MO_MAXDATASIZE }, 1580203004Sgshapiro# endif /* _FFR_MAXDATASIZE || _FFR_MDS_NEGOTIATE */ 1581168515Sgshapiro { NULL, (unsigned char)-1 }, 158264562Sgshapiro}; 158364562Sgshapiro 158464562Sgshapirovoid 158564562Sgshapiromilter_set_option(name, val, sticky) 158664562Sgshapiro char *name; 158764562Sgshapiro char *val; 158864562Sgshapiro bool sticky; 158964562Sgshapiro{ 1590168515Sgshapiro int nummac, r; 1591168515Sgshapiro struct milteropt *mo; 159264562Sgshapiro char **macros = NULL; 159364562Sgshapiro 1594168515Sgshapiro nummac = 0; 159564562Sgshapiro if (tTd(37, 2) || tTd(64, 5)) 159690792Sgshapiro sm_dprintf("milter_set_option(%s = %s)", name, val); 159764562Sgshapiro 159898841Sgshapiro if (name == NULL) 159998841Sgshapiro { 160098841Sgshapiro syserr("milter_set_option: invalid Milter option, must specify suboption"); 160198841Sgshapiro return; 160298841Sgshapiro } 160398841Sgshapiro 160464562Sgshapiro for (mo = MilterOptTab; mo->mo_name != NULL; mo++) 160564562Sgshapiro { 160690792Sgshapiro if (sm_strcasecmp(mo->mo_name, name) == 0) 160764562Sgshapiro break; 160864562Sgshapiro } 160964562Sgshapiro 161064562Sgshapiro if (mo->mo_name == NULL) 161190792Sgshapiro { 161264562Sgshapiro syserr("milter_set_option: invalid Milter option %s", name); 161390792Sgshapiro return; 161490792Sgshapiro } 161564562Sgshapiro 161664562Sgshapiro /* 161764562Sgshapiro ** See if this option is preset for us. 161864562Sgshapiro */ 161964562Sgshapiro 162064562Sgshapiro if (!sticky && bitnset(mo->mo_code, StickyMilterOpt)) 162164562Sgshapiro { 162264562Sgshapiro if (tTd(37, 2) || tTd(64,5)) 162390792Sgshapiro sm_dprintf(" (ignored)\n"); 162464562Sgshapiro return; 162564562Sgshapiro } 162664562Sgshapiro 162764562Sgshapiro if (tTd(37, 2) || tTd(64,5)) 162890792Sgshapiro sm_dprintf("\n"); 162964562Sgshapiro 163064562Sgshapiro switch (mo->mo_code) 163164562Sgshapiro { 163290792Sgshapiro case MO_LOGLEVEL: 163390792Sgshapiro MilterLogLevel = atoi(val); 163490792Sgshapiro break; 163590792Sgshapiro 1636203004Sgshapiro# if _FFR_MAXDATASIZE || _FFR_MDS_NEGOTIATE 1637132943Sgshapiro case MO_MAXDATASIZE: 1638203004Sgshapiro# if _FFR_MDS_NEGOTIATE 1639132943Sgshapiro MilterMaxDataSize = (size_t)atol(val); 1640203004Sgshapiro if (MilterMaxDataSize != MILTER_MDS_64K && 1641203004Sgshapiro MilterMaxDataSize != MILTER_MDS_256K && 1642203004Sgshapiro MilterMaxDataSize != MILTER_MDS_1M) 1643203004Sgshapiro { 1644203004Sgshapiro sm_syslog(LOG_WARNING, NOQID, 1645203004Sgshapiro "WARNING: Milter.%s=%d, allowed are only %d, %d, and %d", 1646203004Sgshapiro name, MilterMaxDataSize, 1647203004Sgshapiro MILTER_MDS_64K, MILTER_MDS_256K, 1648203004Sgshapiro MILTER_MDS_1M); 1649203004Sgshapiro if (MilterMaxDataSize < MILTER_MDS_64K) 1650203004Sgshapiro MilterMaxDataSize = MILTER_MDS_64K; 1651203004Sgshapiro else if (MilterMaxDataSize < MILTER_MDS_256K) 1652203004Sgshapiro MilterMaxDataSize = MILTER_MDS_256K; 1653203004Sgshapiro else 1654203004Sgshapiro MilterMaxDataSize = MILTER_MDS_1M; 1655203004Sgshapiro } 1656203004Sgshapiro# endif /* _FFR_MDS_NEGOTIATE */ 1657132943Sgshapiro break; 1658203004Sgshapiro# endif /* _FFR_MAXDATASIZE || _FFR_MDS_NEGOTIATE */ 1659132943Sgshapiro 1660244928Sgshapiro case SMFIM_CONNECT: 1661244928Sgshapiro case SMFIM_HELO: 1662244928Sgshapiro case SMFIM_ENVFROM: 1663244928Sgshapiro case SMFIM_ENVRCPT: 1664244928Sgshapiro case SMFIM_EOH: 1665244928Sgshapiro case SMFIM_EOM: 1666244928Sgshapiro case SMFIM_DATA: 1667244928Sgshapiro macros = MilterMacros[mo->mo_code][0]; 166864562Sgshapiro 1669168515Sgshapiro r = milter_set_macros(name, macros, val, nummac); 1670168515Sgshapiro if (r >= 0) 1671168515Sgshapiro nummac = r; 167264562Sgshapiro break; 167364562Sgshapiro 167464562Sgshapiro default: 167564562Sgshapiro syserr("milter_set_option: invalid Milter option %s", name); 167664562Sgshapiro break; 167764562Sgshapiro } 167864562Sgshapiro if (sticky) 167964562Sgshapiro setbitn(mo->mo_code, StickyMilterOpt); 168064562Sgshapiro} 1681168515Sgshapiro 168290792Sgshapiro/* 168390792Sgshapiro** MILTER_REOPEN_DF -- open & truncate the data file (for replbody) 168464562Sgshapiro** 168564562Sgshapiro** Parameters: 168664562Sgshapiro** e -- current envelope. 168764562Sgshapiro** 168864562Sgshapiro** Returns: 168964562Sgshapiro** 0 if succesful, -1 otherwise 169064562Sgshapiro*/ 169164562Sgshapiro 169264562Sgshapirostatic int 169364562Sgshapiromilter_reopen_df(e) 169464562Sgshapiro ENVELOPE *e; 169564562Sgshapiro{ 169664562Sgshapiro char dfname[MAXPATHLEN]; 169764562Sgshapiro 1698168515Sgshapiro (void) sm_strlcpy(dfname, queuename(e, DATAFL_LETTER), sizeof(dfname)); 169964562Sgshapiro 170064562Sgshapiro /* 170190792Sgshapiro ** In SuperSafe == SAFE_REALLY mode, e->e_dfp is a read-only FP so 170264562Sgshapiro ** close and reopen writable (later close and reopen 170364562Sgshapiro ** read only again). 170464562Sgshapiro ** 170590792Sgshapiro ** In SuperSafe != SAFE_REALLY mode, e->e_dfp still points at the 1706132943Sgshapiro ** buffered file I/O descriptor, still open for writing so there 1707132943Sgshapiro ** isn't any work to do here (except checking for consistency). 170864562Sgshapiro */ 170964562Sgshapiro 171090792Sgshapiro if (SuperSafe == SAFE_REALLY) 171164562Sgshapiro { 171290792Sgshapiro /* close read-only data file */ 171364562Sgshapiro if (bitset(EF_HAS_DF, e->e_flags) && e->e_dfp != NULL) 171464562Sgshapiro { 171590792Sgshapiro (void) sm_io_close(e->e_dfp, SM_TIME_DEFAULT); 171664562Sgshapiro e->e_flags &= ~EF_HAS_DF; 171764562Sgshapiro } 171864562Sgshapiro 171964562Sgshapiro /* open writable */ 172090792Sgshapiro if ((e->e_dfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, dfname, 1721120256Sgshapiro SM_IO_RDWR_B, NULL)) == NULL) 172264562Sgshapiro { 172390792Sgshapiro MILTER_DF_ERROR("milter_reopen_df: sm_io_open %s: %s"); 172464562Sgshapiro return -1; 172564562Sgshapiro } 172664562Sgshapiro } 172764562Sgshapiro else if (e->e_dfp == NULL) 172864562Sgshapiro { 172964562Sgshapiro /* shouldn't happen */ 173064562Sgshapiro errno = ENOENT; 173164562Sgshapiro MILTER_DF_ERROR("milter_reopen_df: NULL e_dfp (%s: %s)"); 173264562Sgshapiro return -1; 173364562Sgshapiro } 173464562Sgshapiro return 0; 173564562Sgshapiro} 1736168515Sgshapiro 173790792Sgshapiro/* 173890792Sgshapiro** MILTER_RESET_DF -- re-open read-only the data file (for replbody) 173964562Sgshapiro** 174064562Sgshapiro** Parameters: 174164562Sgshapiro** e -- current envelope. 174264562Sgshapiro** 174364562Sgshapiro** Returns: 174464562Sgshapiro** 0 if succesful, -1 otherwise 174564562Sgshapiro*/ 174664562Sgshapiro 174764562Sgshapirostatic int 174864562Sgshapiromilter_reset_df(e) 174964562Sgshapiro ENVELOPE *e; 175064562Sgshapiro{ 175164562Sgshapiro int afd; 175264562Sgshapiro char dfname[MAXPATHLEN]; 175364562Sgshapiro 1754168515Sgshapiro (void) sm_strlcpy(dfname, queuename(e, DATAFL_LETTER), sizeof(dfname)); 175564562Sgshapiro 175690792Sgshapiro if (sm_io_flush(e->e_dfp, SM_TIME_DEFAULT) != 0 || 175790792Sgshapiro sm_io_error(e->e_dfp)) 175864562Sgshapiro { 175964562Sgshapiro MILTER_DF_ERROR("milter_reset_df: error writing/flushing %s: %s"); 176064562Sgshapiro return -1; 176164562Sgshapiro } 176290792Sgshapiro else if (SuperSafe != SAFE_REALLY) 176364562Sgshapiro { 176464562Sgshapiro /* skip next few clauses */ 176564562Sgshapiro /* EMPTY */ 176664562Sgshapiro } 176790792Sgshapiro else if ((afd = sm_io_getinfo(e->e_dfp, SM_IO_WHAT_FD, NULL)) >= 0 176890792Sgshapiro && fsync(afd) < 0) 176964562Sgshapiro { 177064562Sgshapiro MILTER_DF_ERROR("milter_reset_df: error sync'ing %s: %s"); 177164562Sgshapiro return -1; 177264562Sgshapiro } 177390792Sgshapiro else if (sm_io_close(e->e_dfp, SM_TIME_DEFAULT) < 0) 177464562Sgshapiro { 177564562Sgshapiro MILTER_DF_ERROR("milter_reset_df: error closing %s: %s"); 177664562Sgshapiro return -1; 177764562Sgshapiro } 177890792Sgshapiro else if ((e->e_dfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, dfname, 1779120256Sgshapiro SM_IO_RDONLY_B, NULL)) == NULL) 178064562Sgshapiro { 178164562Sgshapiro MILTER_DF_ERROR("milter_reset_df: error reopening %s: %s"); 178264562Sgshapiro return -1; 178364562Sgshapiro } 178464562Sgshapiro else 178564562Sgshapiro e->e_flags |= EF_HAS_DF; 178664562Sgshapiro return 0; 178764562Sgshapiro} 1788168515Sgshapiro 178990792Sgshapiro/* 179064562Sgshapiro** MILTER_QUIT_FILTER -- close down a single filter 179164562Sgshapiro** 179264562Sgshapiro** Parameters: 179364562Sgshapiro** m -- milter structure of filter to close down. 179464562Sgshapiro** e -- current envelope. 179564562Sgshapiro** 179664562Sgshapiro** Returns: 179764562Sgshapiro** none 179864562Sgshapiro*/ 179964562Sgshapiro 180064562Sgshapirostatic void 180164562Sgshapiromilter_quit_filter(m, e) 180264562Sgshapiro struct milter *m; 180364562Sgshapiro ENVELOPE *e; 180464562Sgshapiro{ 180564562Sgshapiro if (tTd(64, 10)) 180690792Sgshapiro sm_dprintf("milter_quit_filter(%s)\n", m->mf_name); 180790792Sgshapiro if (MilterLogLevel > 18) 180890792Sgshapiro sm_syslog(LOG_INFO, e->e_id, "Milter (%s): quit filter", 180990792Sgshapiro m->mf_name); 181064562Sgshapiro 181164562Sgshapiro /* Never replace error state */ 181264562Sgshapiro if (m->mf_state == SMFS_ERROR) 181364562Sgshapiro return; 181464562Sgshapiro 181564562Sgshapiro if (m->mf_sock < 0 || 181664562Sgshapiro m->mf_state == SMFS_CLOSED || 181764562Sgshapiro m->mf_state == SMFS_READY) 181864562Sgshapiro { 181964562Sgshapiro m->mf_sock = -1; 182064562Sgshapiro m->mf_state = SMFS_CLOSED; 182164562Sgshapiro return; 182264562Sgshapiro } 182364562Sgshapiro 182464562Sgshapiro (void) milter_write(m, SMFIC_QUIT, (char *) NULL, 0, 1825168515Sgshapiro m->mf_timeout[SMFTO_WRITE], e, "quit_filter"); 182671345Sgshapiro if (m->mf_sock >= 0) 182771345Sgshapiro { 182871345Sgshapiro (void) close(m->mf_sock); 182971345Sgshapiro m->mf_sock = -1; 183071345Sgshapiro } 183164562Sgshapiro if (m->mf_state != SMFS_ERROR) 183264562Sgshapiro m->mf_state = SMFS_CLOSED; 183364562Sgshapiro} 1834168515Sgshapiro 183590792Sgshapiro/* 183664562Sgshapiro** MILTER_ABORT_FILTER -- tell filter to abort current message 183764562Sgshapiro** 183864562Sgshapiro** Parameters: 183964562Sgshapiro** m -- milter structure of filter to abort. 184064562Sgshapiro** e -- current envelope. 184164562Sgshapiro** 184264562Sgshapiro** Returns: 184364562Sgshapiro** none 184464562Sgshapiro*/ 184564562Sgshapiro 184664562Sgshapirostatic void 184764562Sgshapiromilter_abort_filter(m, e) 184864562Sgshapiro struct milter *m; 184964562Sgshapiro ENVELOPE *e; 185064562Sgshapiro{ 185164562Sgshapiro if (tTd(64, 10)) 185290792Sgshapiro sm_dprintf("milter_abort_filter(%s)\n", m->mf_name); 185390792Sgshapiro if (MilterLogLevel > 10) 185490792Sgshapiro sm_syslog(LOG_INFO, e->e_id, "Milter (%s): abort filter", 185590792Sgshapiro m->mf_name); 185664562Sgshapiro 185764562Sgshapiro if (m->mf_sock < 0 || 185864562Sgshapiro m->mf_state != SMFS_INMSG) 185964562Sgshapiro return; 186064562Sgshapiro 186164562Sgshapiro (void) milter_write(m, SMFIC_ABORT, (char *) NULL, 0, 1862168515Sgshapiro m->mf_timeout[SMFTO_WRITE], e, "abort_filter"); 186364562Sgshapiro if (m->mf_state != SMFS_ERROR) 186464562Sgshapiro m->mf_state = SMFS_DONE; 186564562Sgshapiro} 1866168515Sgshapiro 186790792Sgshapiro/* 186864562Sgshapiro** MILTER_SEND_MACROS -- provide macros to the filters 186964562Sgshapiro** 187064562Sgshapiro** Parameters: 187164562Sgshapiro** m -- milter to send macros to. 187264562Sgshapiro** macros -- macros to send for filter smfi_getsymval(). 187364562Sgshapiro** cmd -- which command the macros are associated with. 187464562Sgshapiro** e -- current envelope (for macro access). 187564562Sgshapiro** 187664562Sgshapiro** Returns: 187764562Sgshapiro** none 187864562Sgshapiro*/ 187964562Sgshapiro 188064562Sgshapirostatic void 188164562Sgshapiromilter_send_macros(m, macros, cmd, e) 188264562Sgshapiro struct milter *m; 188364562Sgshapiro char **macros; 1884168515Sgshapiro int cmd; 188564562Sgshapiro ENVELOPE *e; 188664562Sgshapiro{ 188764562Sgshapiro int i; 188864562Sgshapiro int mid; 1889168515Sgshapiro char command = (char) cmd; 189064562Sgshapiro char *v; 189164562Sgshapiro char *buf, *bp; 189295154Sgshapiro char exp[MAXLINE]; 189364562Sgshapiro ssize_t s; 189464562Sgshapiro 189564562Sgshapiro /* sanity check */ 189664562Sgshapiro if (macros == NULL || macros[0] == NULL) 189764562Sgshapiro return; 189864562Sgshapiro 189964562Sgshapiro /* put together data */ 190064562Sgshapiro s = 1; /* for the command character */ 190164562Sgshapiro for (i = 0; macros[i] != NULL; i++) 190264562Sgshapiro { 190390792Sgshapiro mid = macid(macros[i]); 190471345Sgshapiro if (mid == 0) 190564562Sgshapiro continue; 190664562Sgshapiro v = macvalue(mid, e); 190764562Sgshapiro if (v == NULL) 190864562Sgshapiro continue; 190995154Sgshapiro expand(v, exp, sizeof(exp), e); 191095154Sgshapiro s += strlen(macros[i]) + 1 + strlen(exp) + 1; 191164562Sgshapiro } 191264562Sgshapiro 191390792Sgshapiro if (s < 0) 191490792Sgshapiro return; 191590792Sgshapiro 191690792Sgshapiro buf = (char *) xalloc(s); 191764562Sgshapiro bp = buf; 1918168515Sgshapiro *bp++ = command; 191964562Sgshapiro for (i = 0; macros[i] != NULL; i++) 192064562Sgshapiro { 192190792Sgshapiro mid = macid(macros[i]); 192271345Sgshapiro if (mid == 0) 192364562Sgshapiro continue; 192464562Sgshapiro v = macvalue(mid, e); 192564562Sgshapiro if (v == NULL) 192664562Sgshapiro continue; 192795154Sgshapiro expand(v, exp, sizeof(exp), e); 192864562Sgshapiro 192964562Sgshapiro if (tTd(64, 10)) 193090792Sgshapiro sm_dprintf("milter_send_macros(%s, %c): %s=%s\n", 1931168515Sgshapiro m->mf_name, command, macros[i], exp); 193264562Sgshapiro 193390792Sgshapiro (void) sm_strlcpy(bp, macros[i], s - (bp - buf)); 193464562Sgshapiro bp += strlen(bp) + 1; 193595154Sgshapiro (void) sm_strlcpy(bp, exp, s - (bp - buf)); 193664562Sgshapiro bp += strlen(bp) + 1; 193764562Sgshapiro } 193864562Sgshapiro (void) milter_write(m, SMFIC_MACRO, buf, s, 1939168515Sgshapiro m->mf_timeout[SMFTO_WRITE], e, "send_macros"); 1940147078Sgshapiro sm_free(buf); 194164562Sgshapiro} 194264562Sgshapiro 194390792Sgshapiro/* 194464562Sgshapiro** MILTER_SEND_COMMAND -- send a command and return the response for a filter 194564562Sgshapiro** 194664562Sgshapiro** Parameters: 194764562Sgshapiro** m -- current milter filter 1948168515Sgshapiro** cmd -- command to send. 194964562Sgshapiro** data -- optional command data. 195064562Sgshapiro** sz -- length of buf. 195164562Sgshapiro** e -- current envelope (for e->e_id). 195264562Sgshapiro** state -- return state word. 195364562Sgshapiro** 195464562Sgshapiro** Returns: 195564562Sgshapiro** response string (may be NULL) 195664562Sgshapiro*/ 195764562Sgshapiro 195864562Sgshapirostatic char * 1959168515Sgshapiromilter_send_command(m, cmd, data, sz, e, state, where) 196064562Sgshapiro struct milter *m; 1961168515Sgshapiro int cmd; 196264562Sgshapiro void *data; 196364562Sgshapiro ssize_t sz; 196464562Sgshapiro ENVELOPE *e; 196564562Sgshapiro char *state; 1966168515Sgshapiro const char *where; 196764562Sgshapiro{ 196864562Sgshapiro char rcmd; 196964562Sgshapiro ssize_t rlen; 197090792Sgshapiro unsigned long skipflag; 1971132943Sgshapiro unsigned long norespflag = 0; 1972168515Sgshapiro char command = (char) cmd; 197390792Sgshapiro char *action; 197464562Sgshapiro char *defresponse; 197564562Sgshapiro char *response; 197664562Sgshapiro 197764562Sgshapiro if (tTd(64, 10)) 197890792Sgshapiro sm_dprintf("milter_send_command(%s): cmd %c len %ld\n", 197964562Sgshapiro m->mf_name, (char) command, (long) sz); 198064562Sgshapiro 198164562Sgshapiro /* find skip flag and default failure */ 198264562Sgshapiro switch (command) 198364562Sgshapiro { 198464562Sgshapiro case SMFIC_CONNECT: 198564562Sgshapiro skipflag = SMFIP_NOCONNECT; 1986168515Sgshapiro norespflag = SMFIP_NR_CONN; 198790792Sgshapiro action = "connect"; 198864562Sgshapiro defresponse = "554 Command rejected"; 198964562Sgshapiro break; 199064562Sgshapiro 199164562Sgshapiro case SMFIC_HELO: 199264562Sgshapiro skipflag = SMFIP_NOHELO; 1993168515Sgshapiro norespflag = SMFIP_NR_HELO; 199490792Sgshapiro action = "helo"; 199564562Sgshapiro defresponse = "550 Command rejected"; 199664562Sgshapiro break; 199764562Sgshapiro 199864562Sgshapiro case SMFIC_MAIL: 199964562Sgshapiro skipflag = SMFIP_NOMAIL; 2000168515Sgshapiro norespflag = SMFIP_NR_MAIL; 200190792Sgshapiro action = "mail"; 200264562Sgshapiro defresponse = "550 5.7.1 Command rejected"; 200364562Sgshapiro break; 200464562Sgshapiro 200564562Sgshapiro case SMFIC_RCPT: 200664562Sgshapiro skipflag = SMFIP_NORCPT; 2007168515Sgshapiro norespflag = SMFIP_NR_RCPT; 200890792Sgshapiro action = "rcpt"; 200964562Sgshapiro defresponse = "550 5.7.1 Command rejected"; 201064562Sgshapiro break; 201164562Sgshapiro 201264562Sgshapiro case SMFIC_HEADER: 201364562Sgshapiro skipflag = SMFIP_NOHDRS; 2014168515Sgshapiro norespflag = SMFIP_NR_HDR; 201590792Sgshapiro action = "header"; 201664562Sgshapiro defresponse = "550 5.7.1 Command rejected"; 201764562Sgshapiro break; 201864562Sgshapiro 201964562Sgshapiro case SMFIC_BODY: 202064562Sgshapiro skipflag = SMFIP_NOBODY; 2021168515Sgshapiro norespflag = SMFIP_NR_BODY; 202290792Sgshapiro action = "body"; 202364562Sgshapiro defresponse = "554 5.7.1 Command rejected"; 202464562Sgshapiro break; 202564562Sgshapiro 202664562Sgshapiro case SMFIC_EOH: 202764562Sgshapiro skipflag = SMFIP_NOEOH; 2028168515Sgshapiro norespflag = SMFIP_NR_EOH; 202990792Sgshapiro action = "eoh"; 203064562Sgshapiro defresponse = "550 5.7.1 Command rejected"; 203164562Sgshapiro break; 203264562Sgshapiro 2033132943Sgshapiro case SMFIC_UNKNOWN: 2034157001Sgshapiro skipflag = SMFIP_NOUNKNOWN; 2035168515Sgshapiro norespflag = SMFIP_NR_UNKN; 2036132943Sgshapiro action = "unknown"; 2037132943Sgshapiro defresponse = "550 5.7.1 Command rejected"; 2038132943Sgshapiro break; 2039132943Sgshapiro 2040157001Sgshapiro case SMFIC_DATA: 2041157001Sgshapiro skipflag = SMFIP_NODATA; 2042168515Sgshapiro norespflag = SMFIP_NR_DATA; 2043157001Sgshapiro action = "data"; 2044157001Sgshapiro defresponse = "550 5.7.1 Command rejected"; 2045157001Sgshapiro break; 2046157001Sgshapiro 204764562Sgshapiro case SMFIC_BODYEOB: 204864562Sgshapiro case SMFIC_OPTNEG: 204964562Sgshapiro case SMFIC_MACRO: 205064562Sgshapiro case SMFIC_ABORT: 205164562Sgshapiro case SMFIC_QUIT: 205264562Sgshapiro /* NOTE: not handled by milter_send_command() */ 205364562Sgshapiro /* FALLTHROUGH */ 205464562Sgshapiro 205564562Sgshapiro default: 205664562Sgshapiro skipflag = 0; 205790792Sgshapiro action = "default"; 205864562Sgshapiro defresponse = "550 5.7.1 Command rejected"; 205964562Sgshapiro break; 206064562Sgshapiro } 206164562Sgshapiro 2062168515Sgshapiro if (tTd(64, 10)) 2063168515Sgshapiro sm_dprintf("milter_send_command(%s): skip=%lx, pflags=%x\n", 2064168515Sgshapiro m->mf_name, skipflag, m->mf_pflags); 2065168515Sgshapiro 206664562Sgshapiro /* check if filter wants this command */ 2067168515Sgshapiro if (skipflag != 0 && bitset(skipflag, m->mf_pflags)) 206864562Sgshapiro return NULL; 206964562Sgshapiro 207090792Sgshapiro /* send the command to the filter */ 207164562Sgshapiro (void) milter_write(m, command, data, sz, 2072168515Sgshapiro m->mf_timeout[SMFTO_WRITE], e, where); 207364562Sgshapiro if (m->mf_state == SMFS_ERROR) 207464562Sgshapiro { 2075112810Sgshapiro MILTER_CHECK_ERROR(false, return NULL); 207664562Sgshapiro return NULL; 207764562Sgshapiro } 207864562Sgshapiro 2079132943Sgshapiro /* check if filter sends response to this command */ 2080132943Sgshapiro if (norespflag != 0 && bitset(norespflag, m->mf_pflags)) 2081132943Sgshapiro return NULL; 2082132943Sgshapiro 208390792Sgshapiro /* get the response from the filter */ 208464562Sgshapiro response = milter_read(m, &rcmd, &rlen, 2085168515Sgshapiro m->mf_timeout[SMFTO_READ], e, where); 208664562Sgshapiro if (m->mf_state == SMFS_ERROR) 208764562Sgshapiro { 2088112810Sgshapiro MILTER_CHECK_ERROR(false, return NULL); 208964562Sgshapiro return NULL; 209064562Sgshapiro } 209164562Sgshapiro 209264562Sgshapiro if (tTd(64, 10)) 209390792Sgshapiro sm_dprintf("milter_send_command(%s): returned %c\n", 209490792Sgshapiro m->mf_name, (char) rcmd); 209564562Sgshapiro 209664562Sgshapiro switch (rcmd) 209764562Sgshapiro { 209864562Sgshapiro case SMFIR_REPLYCODE: 209964562Sgshapiro MILTER_CHECK_REPLYCODE(defresponse); 210090792Sgshapiro if (MilterLogLevel > 10) 2101168515Sgshapiro sm_syslog(LOG_INFO, e->e_id, 2102168515Sgshapiro "milter=%s, action=%s, reject=%s", 210390792Sgshapiro m->mf_name, action, response); 210490792Sgshapiro *state = rcmd; 210590792Sgshapiro break; 210664562Sgshapiro 210764562Sgshapiro case SMFIR_REJECT: 210890792Sgshapiro if (MilterLogLevel > 10) 2109168515Sgshapiro sm_syslog(LOG_INFO, e->e_id, 2110168515Sgshapiro "milter=%s, action=%s, reject", 211190792Sgshapiro m->mf_name, action); 211290792Sgshapiro *state = rcmd; 211390792Sgshapiro break; 211490792Sgshapiro 211564562Sgshapiro case SMFIR_DISCARD: 211690792Sgshapiro if (MilterLogLevel > 10) 2117168515Sgshapiro sm_syslog(LOG_INFO, e->e_id, 2118168515Sgshapiro "milter=%s, action=%s, discard", 211990792Sgshapiro m->mf_name, action); 212090792Sgshapiro *state = rcmd; 212190792Sgshapiro break; 212290792Sgshapiro 212364562Sgshapiro case SMFIR_TEMPFAIL: 212490792Sgshapiro if (MilterLogLevel > 10) 2125168515Sgshapiro sm_syslog(LOG_INFO, e->e_id, 2126168515Sgshapiro "milter=%s, action=%s, tempfail", 212790792Sgshapiro m->mf_name, action); 212864562Sgshapiro *state = rcmd; 212964562Sgshapiro break; 213064562Sgshapiro 213164562Sgshapiro case SMFIR_ACCEPT: 213264562Sgshapiro /* this filter is done with message/connection */ 213373188Sgshapiro if (command == SMFIC_HELO || 213473188Sgshapiro command == SMFIC_CONNECT) 213573188Sgshapiro m->mf_state = SMFS_CLOSABLE; 213673188Sgshapiro else 213773188Sgshapiro m->mf_state = SMFS_DONE; 213890792Sgshapiro if (MilterLogLevel > 10) 2139168515Sgshapiro sm_syslog(LOG_INFO, e->e_id, 2140168515Sgshapiro "milter=%s, action=%s, accepted", 214190792Sgshapiro m->mf_name, action); 214264562Sgshapiro break; 214364562Sgshapiro 214464562Sgshapiro case SMFIR_CONTINUE: 214564562Sgshapiro /* if MAIL command is ok, filter is in message state */ 214664562Sgshapiro if (command == SMFIC_MAIL) 214764562Sgshapiro m->mf_state = SMFS_INMSG; 214890792Sgshapiro if (MilterLogLevel > 12) 2149168515Sgshapiro sm_syslog(LOG_INFO, e->e_id, 2150168515Sgshapiro "milter=%s, action=%s, continue", 215190792Sgshapiro m->mf_name, action); 215264562Sgshapiro break; 215364562Sgshapiro 2154168515Sgshapiro case SMFIR_SKIP: 2155168515Sgshapiro if (MilterLogLevel > 12) 2156168515Sgshapiro sm_syslog(LOG_INFO, e->e_id, 2157168515Sgshapiro "milter=%s, action=%s, skip", 2158168515Sgshapiro m->mf_name, action); 2159168515Sgshapiro m->mf_state = SMFS_SKIP; 2160168515Sgshapiro break; 2161168515Sgshapiro 216264562Sgshapiro default: 216364562Sgshapiro /* Invalid response to command */ 216490792Sgshapiro if (MilterLogLevel > 0) 216564562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 216690792Sgshapiro "milter_send_command(%s): action=%s returned bogus response %c", 216790792Sgshapiro m->mf_name, action, rcmd); 2168244928Sgshapiro milter_error(m, e); /* NO ERROR CHECK? */ 216964562Sgshapiro break; 217064562Sgshapiro } 217164562Sgshapiro 2172168515Sgshapiro if (*state != SMFIR_REPLYCODE && response != NULL) 217364562Sgshapiro { 217490792Sgshapiro sm_free(response); /* XXX */ 217564562Sgshapiro response = NULL; 217664562Sgshapiro } 217764562Sgshapiro return response; 217864562Sgshapiro} 217964562Sgshapiro 218090792Sgshapiro/* 218164562Sgshapiro** MILTER_COMMAND -- send a command and return the response for each filter 218264562Sgshapiro** 218364562Sgshapiro** Parameters: 2184168515Sgshapiro** cmd -- command to send. 218564562Sgshapiro** data -- optional command data. 218664562Sgshapiro** sz -- length of buf. 2187249865Sgshapiro** stage -- index of macros to send for filter smfi_getsymval(). 218864562Sgshapiro** e -- current envelope (for macro access). 218964562Sgshapiro** state -- return state word. 2190168515Sgshapiro** where -- description of calling function (logging). 2191168515Sgshapiro** cmd_error -- did the SMTP command cause an error? 219264562Sgshapiro** 219364562Sgshapiro** Returns: 219464562Sgshapiro** response string (may be NULL) 219564562Sgshapiro*/ 219664562Sgshapiro 219764562Sgshapirostatic char * 2198244928Sgshapiromilter_command(cmd, data, sz, stage, e, state, where, cmd_error) 2199168515Sgshapiro int cmd; 220064562Sgshapiro void *data; 220164562Sgshapiro ssize_t sz; 2202244928Sgshapiro int stage; 220364562Sgshapiro ENVELOPE *e; 220464562Sgshapiro char *state; 2205168515Sgshapiro const char *where; 2206168515Sgshapiro bool cmd_error; 220764562Sgshapiro{ 220864562Sgshapiro int i; 2209168515Sgshapiro char command = (char) cmd; 221064562Sgshapiro char *response = NULL; 221190792Sgshapiro time_t tn = 0; 221264562Sgshapiro 221364562Sgshapiro if (tTd(64, 10)) 221490792Sgshapiro sm_dprintf("milter_command: cmd %c len %ld\n", 2215168515Sgshapiro command, (long) sz); 221664562Sgshapiro 221764562Sgshapiro *state = SMFIR_CONTINUE; 221864562Sgshapiro for (i = 0; InputFilters[i] != NULL; i++) 221964562Sgshapiro { 222064562Sgshapiro struct milter *m = InputFilters[i]; 222164562Sgshapiro 222271345Sgshapiro /* previous problem? */ 222371345Sgshapiro if (m->mf_state == SMFS_ERROR) 222471345Sgshapiro { 2225112810Sgshapiro MILTER_CHECK_ERROR(false, continue); 222671345Sgshapiro break; 222771345Sgshapiro } 222871345Sgshapiro 222964562Sgshapiro /* sanity check */ 223064562Sgshapiro if (m->mf_sock < 0 || 223164562Sgshapiro (m->mf_state != SMFS_OPEN && m->mf_state != SMFS_INMSG)) 223264562Sgshapiro continue; 223364562Sgshapiro 2234244928Sgshapiro if (stage >= SMFIM_FIRST && stage <= SMFIM_LAST) 223564562Sgshapiro { 2236244928Sgshapiro int idx; 2237244928Sgshapiro char **macros; 2238244928Sgshapiro 2239244928Sgshapiro if ((m->mf_lflags & MI_LFLAGS_SYM(stage)) != 0) 2240244928Sgshapiro idx = m->mf_idx; 2241244928Sgshapiro else 2242244928Sgshapiro idx = 0; 2243244928Sgshapiro SM_ASSERT(idx >= 0 && idx <= MAXFILTERS); 2244244928Sgshapiro macros = MilterMacros[stage][idx]; 2245244928Sgshapiro 2246244928Sgshapiro /* send macros (regardless of whether we send cmd) */ 2247244928Sgshapiro if (macros != NULL && macros[0] != NULL) 224864562Sgshapiro { 2249244928Sgshapiro milter_send_macros(m, macros, command, e); 2250244928Sgshapiro if (m->mf_state == SMFS_ERROR) 2251244928Sgshapiro { 2252244928Sgshapiro MILTER_CHECK_ERROR(false, continue); 2253244928Sgshapiro break; 2254244928Sgshapiro } 225564562Sgshapiro } 225664562Sgshapiro } 225764562Sgshapiro 225890792Sgshapiro if (MilterLogLevel > 21) 225990792Sgshapiro tn = curtime(); 226090792Sgshapiro 2261168515Sgshapiro /* 2262168515Sgshapiro ** send the command if 2263168515Sgshapiro ** there is no error 2264168515Sgshapiro ** or it's RCPT and the client asked for it: 2265168515Sgshapiro ** !cmd_error || 2266168515Sgshapiro ** where == "rcpt" && m->mf_pflags & SMFIP_RCPT_REJ != 0 2267168515Sgshapiro ** negate that condition and use continue 2268168515Sgshapiro */ 226990792Sgshapiro 2270168515Sgshapiro if (cmd_error && 2271168515Sgshapiro (strcmp(where, "rcpt") != 0 || 2272168515Sgshapiro (m->mf_pflags & SMFIP_RCPT_REJ) == 0)) 2273168515Sgshapiro continue; 2274168515Sgshapiro 2275168515Sgshapiro response = milter_send_command(m, command, data, sz, e, state, 2276168515Sgshapiro where); 2277168515Sgshapiro 227890792Sgshapiro if (MilterLogLevel > 21) 227990792Sgshapiro { 228090792Sgshapiro /* log the time it took for the command per filter */ 228190792Sgshapiro sm_syslog(LOG_INFO, e->e_id, 228290792Sgshapiro "Milter (%s): time command (%c), %d", 228390792Sgshapiro m->mf_name, command, (int) (tn - curtime())); 228490792Sgshapiro } 228590792Sgshapiro 228664562Sgshapiro if (*state != SMFIR_CONTINUE) 228764562Sgshapiro break; 228864562Sgshapiro } 228964562Sgshapiro return response; 229064562Sgshapiro} 2291168515Sgshapiro 2292168515Sgshapirostatic int milter_getsymlist __P((struct milter *, char *, int, int)); 2293168515Sgshapiro 2294168515Sgshapirostatic int 2295168515Sgshapiromilter_getsymlist(m, buf, rlen, offset) 2296168515Sgshapiro struct milter *m; 2297168515Sgshapiro char *buf; 2298168515Sgshapiro int rlen; 2299168515Sgshapiro int offset; 2300168515Sgshapiro{ 2301168515Sgshapiro int i, r, nummac; 2302168515Sgshapiro mi_int32 v; 2303168515Sgshapiro 2304168515Sgshapiro SM_ASSERT(m != NULL); 2305168515Sgshapiro SM_ASSERT(buf != NULL); 2306168515Sgshapiro 2307168515Sgshapiro while (offset + MILTER_LEN_BYTES < rlen) 2308168515Sgshapiro { 2309168515Sgshapiro size_t len; 2310168515Sgshapiro char **macros; 2311168515Sgshapiro 2312168515Sgshapiro nummac = 0; 2313168515Sgshapiro (void) memcpy((char *) &v, buf + offset, MILTER_LEN_BYTES); 2314168515Sgshapiro i = ntohl(v); 2315168515Sgshapiro if (i < SMFIM_FIRST || i > SMFIM_LAST) 2316168515Sgshapiro return -1; 2317168515Sgshapiro offset += MILTER_LEN_BYTES; 2318168515Sgshapiro macros = NULL; 2319168515Sgshapiro 2320168515Sgshapiro switch (i) 2321168515Sgshapiro { 2322244928Sgshapiro case SMFIM_CONNECT: 2323244928Sgshapiro case SMFIM_HELO: 2324244928Sgshapiro case SMFIM_ENVFROM: 2325244928Sgshapiro case SMFIM_ENVRCPT: 2326244928Sgshapiro case SMFIM_EOH: 2327244928Sgshapiro case SMFIM_EOM: 2328244928Sgshapiro case SMFIM_DATA: 2329244928Sgshapiro SM_ASSERT(m->mf_idx > 0 && m->mf_idx < MAXFILTERS); 2330244928Sgshapiro macros = MilterMacros[i][m->mf_idx]; 2331244928Sgshapiro m->mf_lflags |= MI_LFLAGS_SYM(i); 2332168515Sgshapiro len = strlen(buf + offset); 2333168515Sgshapiro if (len > 0) 2334168515Sgshapiro { 2335168515Sgshapiro r = milter_set_macros(m->mf_name, macros, 2336168515Sgshapiro buf + offset, nummac); 2337168515Sgshapiro if (r >= 0) 2338168515Sgshapiro nummac = r; 2339244928Sgshapiro if (tTd(64, 5)) 2340244928Sgshapiro sm_dprintf("milter_getsymlist(%s, %s)=%d\n", 2341244928Sgshapiro m->mf_name, buf + offset, r); 2342168515Sgshapiro } 2343168515Sgshapiro break; 2344168515Sgshapiro 2345168515Sgshapiro default: 2346168515Sgshapiro return -1; 2347168515Sgshapiro } 2348168515Sgshapiro if (len == 0) 2349168515Sgshapiro return -1; 2350168515Sgshapiro offset += len + 1; 2351168515Sgshapiro } 2352168515Sgshapiro 2353168515Sgshapiro return 0; 2354168515Sgshapiro} 2355168515Sgshapiro 235690792Sgshapiro/* 235764562Sgshapiro** MILTER_NEGOTIATE -- get version and flags from filter 235864562Sgshapiro** 235964562Sgshapiro** Parameters: 236064562Sgshapiro** m -- milter filter structure. 236164562Sgshapiro** e -- current envelope. 2362173340Sgshapiro** milters -- milters structure. 236364562Sgshapiro** 236464562Sgshapiro** Returns: 236564562Sgshapiro** 0 on success, -1 otherwise 236664562Sgshapiro*/ 236764562Sgshapiro 236864562Sgshapirostatic int 2369173340Sgshapiromilter_negotiate(m, e, milters) 237064562Sgshapiro struct milter *m; 237164562Sgshapiro ENVELOPE *e; 2372173340Sgshapiro milters_T *milters; 237364562Sgshapiro{ 237464562Sgshapiro char rcmd; 2375168515Sgshapiro mi_int32 fvers, fflags, pflags; 2376168515Sgshapiro mi_int32 mta_prot_vers, mta_prot_flags, mta_actions; 2377157001Sgshapiro ssize_t rlen; 237864562Sgshapiro char *response; 237964562Sgshapiro char data[MILTER_OPTLEN]; 238064562Sgshapiro 238164562Sgshapiro /* sanity check */ 238264562Sgshapiro if (m->mf_sock < 0 || m->mf_state != SMFS_OPEN) 238364562Sgshapiro { 238490792Sgshapiro if (MilterLogLevel > 0) 238564562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 238690792Sgshapiro "Milter (%s): negotiate, impossible state", 238764562Sgshapiro m->mf_name); 238890792Sgshapiro milter_error(m, e); 238964562Sgshapiro return -1; 239064562Sgshapiro } 239164562Sgshapiro 2392168515Sgshapiro#if _FFR_MILTER_CHECK 2393168515Sgshapiro mta_prot_vers = m->mf_mta_prot_version; 2394168515Sgshapiro mta_prot_flags = m->mf_mta_prot_flags; 2395168515Sgshapiro mta_actions = m->mf_mta_actions; 2396168515Sgshapiro#else /* _FFR_MILTER_CHECK */ 2397168515Sgshapiro mta_prot_vers = SMFI_PROT_VERSION; 2398168515Sgshapiro mta_prot_flags = SMFI_CURR_PROT; 2399168515Sgshapiro mta_actions = SMFI_CURR_ACTS; 2400168515Sgshapiro#endif /* _FFR_MILTER_CHECK */ 2401203004Sgshapiro#if _FFR_MDS_NEGOTIATE 2402203004Sgshapiro if (MilterMaxDataSize == MILTER_MDS_256K) 2403203004Sgshapiro mta_prot_flags |= SMFIP_MDS_256K; 2404203004Sgshapiro else if (MilterMaxDataSize == MILTER_MDS_1M) 2405203004Sgshapiro mta_prot_flags |= SMFIP_MDS_1M; 2406203004Sgshapiro#endif /* _FFR_MDS_NEGOTIATE */ 2407168515Sgshapiro 2408168515Sgshapiro fvers = htonl(mta_prot_vers); 2409168515Sgshapiro pflags = htonl(mta_prot_flags); 2410168515Sgshapiro fflags = htonl(mta_actions); 241164562Sgshapiro (void) memcpy(data, (char *) &fvers, MILTER_LEN_BYTES); 241264562Sgshapiro (void) memcpy(data + MILTER_LEN_BYTES, 241364562Sgshapiro (char *) &fflags, MILTER_LEN_BYTES); 241464562Sgshapiro (void) memcpy(data + (MILTER_LEN_BYTES * 2), 241564562Sgshapiro (char *) &pflags, MILTER_LEN_BYTES); 2416168515Sgshapiro (void) milter_write(m, SMFIC_OPTNEG, data, sizeof(data), 2417168515Sgshapiro m->mf_timeout[SMFTO_WRITE], e, "negotiate"); 241864562Sgshapiro 241964562Sgshapiro if (m->mf_state == SMFS_ERROR) 242064562Sgshapiro return -1; 242164562Sgshapiro 2422168515Sgshapiro if (tTd(64, 5)) 2423168515Sgshapiro sm_dprintf("milter_negotiate(%s): send: version %lu, fflags 0x%lx, pflags 0x%lx\n", 2424168515Sgshapiro m->mf_name, ntohl(fvers), ntohl(fflags), ntohl(pflags)); 2425168515Sgshapiro 2426168515Sgshapiro response = milter_read(m, &rcmd, &rlen, m->mf_timeout[SMFTO_READ], e, 2427168515Sgshapiro "negotiate"); 242864562Sgshapiro if (m->mf_state == SMFS_ERROR) 242964562Sgshapiro return -1; 243064562Sgshapiro 243164562Sgshapiro if (rcmd != SMFIC_OPTNEG) 243264562Sgshapiro { 243364562Sgshapiro if (tTd(64, 5)) 243490792Sgshapiro sm_dprintf("milter_negotiate(%s): returned %c instead of %c\n", 243564562Sgshapiro m->mf_name, rcmd, SMFIC_OPTNEG); 243690792Sgshapiro if (MilterLogLevel > 0) 243764562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 243890792Sgshapiro "Milter (%s): negotiate: returned %c instead of %c", 243964562Sgshapiro m->mf_name, rcmd, SMFIC_OPTNEG); 244064562Sgshapiro if (response != NULL) 244190792Sgshapiro sm_free(response); /* XXX */ 244290792Sgshapiro milter_error(m, e); 244364562Sgshapiro return -1; 244464562Sgshapiro } 244564562Sgshapiro 244664562Sgshapiro /* Make sure we have enough bytes for the version */ 244764562Sgshapiro if (response == NULL || rlen < MILTER_LEN_BYTES) 244864562Sgshapiro { 244964562Sgshapiro if (tTd(64, 5)) 245090792Sgshapiro sm_dprintf("milter_negotiate(%s): did not return valid info\n", 245164562Sgshapiro m->mf_name); 245290792Sgshapiro if (MilterLogLevel > 0) 245364562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 245490792Sgshapiro "Milter (%s): negotiate: did not return valid info", 245564562Sgshapiro m->mf_name); 245664562Sgshapiro if (response != NULL) 245790792Sgshapiro sm_free(response); /* XXX */ 245890792Sgshapiro milter_error(m, e); 245964562Sgshapiro return -1; 246064562Sgshapiro } 246164562Sgshapiro 246264562Sgshapiro /* extract information */ 246364562Sgshapiro (void) memcpy((char *) &fvers, response, MILTER_LEN_BYTES); 246464562Sgshapiro 246564562Sgshapiro /* Now make sure we have enough for the feature bitmap */ 2466168515Sgshapiro if (rlen < MILTER_OPTLEN) 246764562Sgshapiro { 246864562Sgshapiro if (tTd(64, 5)) 246990792Sgshapiro sm_dprintf("milter_negotiate(%s): did not return enough info\n", 247064562Sgshapiro m->mf_name); 247190792Sgshapiro if (MilterLogLevel > 0) 247264562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 247390792Sgshapiro "Milter (%s): negotiate: did not return enough info", 247464562Sgshapiro m->mf_name); 247564562Sgshapiro if (response != NULL) 247690792Sgshapiro sm_free(response); /* XXX */ 247790792Sgshapiro milter_error(m, e); 247864562Sgshapiro return -1; 247964562Sgshapiro } 248064562Sgshapiro 248164562Sgshapiro (void) memcpy((char *) &fflags, response + MILTER_LEN_BYTES, 248264562Sgshapiro MILTER_LEN_BYTES); 248364562Sgshapiro (void) memcpy((char *) &pflags, response + (MILTER_LEN_BYTES * 2), 248464562Sgshapiro MILTER_LEN_BYTES); 248564562Sgshapiro 248664562Sgshapiro m->mf_fvers = ntohl(fvers); 248764562Sgshapiro m->mf_fflags = ntohl(fflags); 248864562Sgshapiro m->mf_pflags = ntohl(pflags); 248964562Sgshapiro 249064562Sgshapiro /* check for version compatibility */ 249164562Sgshapiro if (m->mf_fvers == 1 || 249264562Sgshapiro m->mf_fvers > SMFI_VERSION) 249364562Sgshapiro { 249464562Sgshapiro if (tTd(64, 5)) 249594334Sgshapiro sm_dprintf("milter_negotiate(%s): version %d != MTA milter version %d\n", 249664562Sgshapiro m->mf_name, m->mf_fvers, SMFI_VERSION); 249790792Sgshapiro if (MilterLogLevel > 0) 249864562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 249994334Sgshapiro "Milter (%s): negotiate: version %d != MTA milter version %d", 250064562Sgshapiro m->mf_name, m->mf_fvers, SMFI_VERSION); 250190792Sgshapiro milter_error(m, e); 2502168515Sgshapiro goto error; 250364562Sgshapiro } 250464562Sgshapiro 250564562Sgshapiro /* check for filter feature mismatch */ 2506168515Sgshapiro if ((m->mf_fflags & mta_actions) != m->mf_fflags) 250764562Sgshapiro { 250864562Sgshapiro if (tTd(64, 5)) 250994334Sgshapiro sm_dprintf("milter_negotiate(%s): filter abilities 0x%x != MTA milter abilities 0x%lx\n", 251064562Sgshapiro m->mf_name, m->mf_fflags, 2511168515Sgshapiro (unsigned long) mta_actions); 251290792Sgshapiro if (MilterLogLevel > 0) 251364562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 251494334Sgshapiro "Milter (%s): negotiate: filter abilities 0x%x != MTA milter abilities 0x%lx", 251564562Sgshapiro m->mf_name, m->mf_fflags, 2516168515Sgshapiro (unsigned long) mta_actions); 251790792Sgshapiro milter_error(m, e); 2518168515Sgshapiro goto error; 251964562Sgshapiro } 252064562Sgshapiro 2521203004Sgshapiro#if _FFR_MDS_NEGOTIATE 2522203004Sgshapiro /* use a table instead of sequence? */ 2523203004Sgshapiro if (bitset(SMFIP_MDS_1M, m->mf_pflags)) 2524203004Sgshapiro { 2525203004Sgshapiro if (MilterMaxDataSize != MILTER_MDS_1M) 2526203004Sgshapiro { 2527203004Sgshapiro /* this should not happen... */ 2528203004Sgshapiro sm_syslog(LOG_WARNING, NOQID, 2529203004Sgshapiro "WARNING: Milter.maxdatasize: configured=%d, set by libmilter=%d", 2530203004Sgshapiro MilterMaxDataSize, MILTER_MDS_1M); 2531203004Sgshapiro MilterMaxDataSize = MILTER_MDS_1M; 2532203004Sgshapiro } 2533203004Sgshapiro } 2534203004Sgshapiro else if (bitset(SMFIP_MDS_256K, m->mf_pflags)) 2535203004Sgshapiro { 2536203004Sgshapiro if (MilterMaxDataSize != MILTER_MDS_256K) 2537203004Sgshapiro { 2538203004Sgshapiro sm_syslog(LOG_WARNING, NOQID, 2539203004Sgshapiro "WARNING: Milter.maxdatasize: configured=%d, set by libmilter=%d", 2540203004Sgshapiro MilterMaxDataSize, MILTER_MDS_256K); 2541203004Sgshapiro MilterMaxDataSize = MILTER_MDS_256K; 2542203004Sgshapiro } 2543203004Sgshapiro } 2544203004Sgshapiro else if (MilterMaxDataSize != MILTER_MDS_64K) 2545203004Sgshapiro { 2546203004Sgshapiro sm_syslog(LOG_WARNING, NOQID, 2547203004Sgshapiro "WARNING: Milter.maxdatasize: configured=%d, set by libmilter=%d", 2548203004Sgshapiro MilterMaxDataSize, MILTER_MDS_64K); 2549203004Sgshapiro MilterMaxDataSize = MILTER_MDS_64K; 2550203004Sgshapiro } 2551203004Sgshapiro m->mf_pflags &= ~SMFI_INTERNAL; 2552203004Sgshapiro#endif /* _FFR_MDS_NEGOTIATE */ 2553203004Sgshapiro 255464562Sgshapiro /* check for protocol feature mismatch */ 2555168515Sgshapiro if ((m->mf_pflags & mta_prot_flags) != m->mf_pflags) 255664562Sgshapiro { 255764562Sgshapiro if (tTd(64, 5)) 255894334Sgshapiro sm_dprintf("milter_negotiate(%s): protocol abilities 0x%x != MTA milter abilities 0x%lx\n", 255964562Sgshapiro m->mf_name, m->mf_pflags, 2560168515Sgshapiro (unsigned long) mta_prot_flags); 256190792Sgshapiro if (MilterLogLevel > 0) 256264562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 256394334Sgshapiro "Milter (%s): negotiate: protocol abilities 0x%x != MTA milter abilities 0x%lx", 256464562Sgshapiro m->mf_name, m->mf_pflags, 2565168515Sgshapiro (unsigned long) mta_prot_flags); 256690792Sgshapiro milter_error(m, e); 2567168515Sgshapiro goto error; 256864562Sgshapiro } 256964562Sgshapiro 2570157001Sgshapiro if (m->mf_fvers <= 2) 2571157001Sgshapiro m->mf_pflags |= SMFIP_NOUNKNOWN; 2572157001Sgshapiro if (m->mf_fvers <= 3) 2573157001Sgshapiro m->mf_pflags |= SMFIP_NODATA; 2574157001Sgshapiro 2575168515Sgshapiro if (rlen > MILTER_OPTLEN) 2576168515Sgshapiro { 2577168515Sgshapiro milter_getsymlist(m, response, rlen, MILTER_OPTLEN); 2578168515Sgshapiro } 2579168515Sgshapiro 2580173340Sgshapiro if (bitset(SMFIF_DELRCPT, m->mf_fflags)) 2581173340Sgshapiro milters->mis_flags |= MIS_FL_DEL_RCPT; 2582173340Sgshapiro if (!bitset(SMFIP_NORCPT, m->mf_pflags) && 2583173340Sgshapiro !bitset(SMFIP_NR_RCPT, m->mf_pflags)) 2584173340Sgshapiro milters->mis_flags |= MIS_FL_REJ_RCPT; 2585173340Sgshapiro 258664562Sgshapiro if (tTd(64, 5)) 2587168515Sgshapiro sm_dprintf("milter_negotiate(%s): received: version %u, fflags 0x%x, pflags 0x%x\n", 258864562Sgshapiro m->mf_name, m->mf_fvers, m->mf_fflags, m->mf_pflags); 258964562Sgshapiro return 0; 2590168515Sgshapiro 2591168515Sgshapiro error: 2592168515Sgshapiro if (response != NULL) 2593168515Sgshapiro sm_free(response); /* XXX */ 2594168515Sgshapiro return -1; 259564562Sgshapiro} 2596168515Sgshapiro 259790792Sgshapiro/* 259864562Sgshapiro** MILTER_PER_CONNECTION_CHECK -- checks on per-connection commands 259964562Sgshapiro** 260064562Sgshapiro** Reduce code duplication by putting these checks in one place 260164562Sgshapiro** 260264562Sgshapiro** Parameters: 260364562Sgshapiro** e -- current envelope. 260464562Sgshapiro** 260564562Sgshapiro** Returns: 260664562Sgshapiro** none 260764562Sgshapiro*/ 260864562Sgshapiro 260964562Sgshapirostatic void 261064562Sgshapiromilter_per_connection_check(e) 261164562Sgshapiro ENVELOPE *e; 261264562Sgshapiro{ 261364562Sgshapiro int i; 261464562Sgshapiro 261564562Sgshapiro /* see if we are done with any of the filters */ 261664562Sgshapiro for (i = 0; InputFilters[i] != NULL; i++) 261764562Sgshapiro { 261864562Sgshapiro struct milter *m = InputFilters[i]; 261964562Sgshapiro 262073188Sgshapiro if (m->mf_state == SMFS_CLOSABLE) 262164562Sgshapiro milter_quit_filter(m, e); 262264562Sgshapiro } 262364562Sgshapiro} 2624168515Sgshapiro 262590792Sgshapiro/* 262664562Sgshapiro** MILTER_ERROR -- Put a milter filter into error state 262764562Sgshapiro** 262864562Sgshapiro** Parameters: 262964562Sgshapiro** m -- the broken filter. 2630141858Sgshapiro** e -- current envelope. 263164562Sgshapiro** 263264562Sgshapiro** Returns: 263364562Sgshapiro** none 263464562Sgshapiro*/ 263564562Sgshapiro 263664562Sgshapirostatic void 263790792Sgshapiromilter_error(m, e) 263864562Sgshapiro struct milter *m; 263990792Sgshapiro ENVELOPE *e; 264064562Sgshapiro{ 264164562Sgshapiro /* 2642141858Sgshapiro ** We could send a quit here but we may have gotten here due to 2643141858Sgshapiro ** an I/O error so we don't want to try to make things worse. 264464562Sgshapiro */ 264564562Sgshapiro 264664562Sgshapiro if (m->mf_sock >= 0) 264764562Sgshapiro { 264864562Sgshapiro (void) close(m->mf_sock); 264964562Sgshapiro m->mf_sock = -1; 265064562Sgshapiro } 265164562Sgshapiro m->mf_state = SMFS_ERROR; 265290792Sgshapiro 265390792Sgshapiro if (MilterLogLevel > 0) 265490792Sgshapiro sm_syslog(LOG_INFO, e->e_id, "Milter (%s): to error state", 265590792Sgshapiro m->mf_name); 265664562Sgshapiro} 2657168515Sgshapiro 265890792Sgshapiro/* 265964562Sgshapiro** MILTER_HEADERS -- send headers to a single milter filter 266064562Sgshapiro** 266164562Sgshapiro** Parameters: 266264562Sgshapiro** m -- current filter. 266364562Sgshapiro** e -- current envelope. 266464562Sgshapiro** state -- return state from response. 266564562Sgshapiro** 266664562Sgshapiro** Returns: 266764562Sgshapiro** response string (may be NULL) 266864562Sgshapiro*/ 266964562Sgshapiro 267064562Sgshapirostatic char * 267164562Sgshapiromilter_headers(m, e, state) 267264562Sgshapiro struct milter *m; 267364562Sgshapiro ENVELOPE *e; 267464562Sgshapiro char *state; 267564562Sgshapiro{ 267664562Sgshapiro char *response = NULL; 267764562Sgshapiro HDR *h; 267864562Sgshapiro 267990792Sgshapiro if (MilterLogLevel > 17) 268090792Sgshapiro sm_syslog(LOG_INFO, e->e_id, "Milter (%s): headers, send", 268190792Sgshapiro m->mf_name); 268290792Sgshapiro 268364562Sgshapiro for (h = e->e_header; h != NULL; h = h->h_link) 268464562Sgshapiro { 2685168515Sgshapiro int len_n, len_v, len_t, len_f; 2686168515Sgshapiro char *buf, *hv; 268764562Sgshapiro 268864562Sgshapiro /* don't send over deleted headers */ 268964562Sgshapiro if (h->h_value == NULL) 269064562Sgshapiro { 2691132943Sgshapiro /* strip H_USER so not counted in milter_changeheader() */ 269264562Sgshapiro h->h_flags &= ~H_USER; 269364562Sgshapiro continue; 269464562Sgshapiro } 269564562Sgshapiro 269664562Sgshapiro /* skip auto-generated */ 269764562Sgshapiro if (!bitset(H_USER, h->h_flags)) 269864562Sgshapiro continue; 269964562Sgshapiro 270064562Sgshapiro if (tTd(64, 10)) 2701168515Sgshapiro sm_dprintf("milter_headers: %s:%s\n", 270264562Sgshapiro h->h_field, h->h_value); 270390792Sgshapiro if (MilterLogLevel > 21) 270490792Sgshapiro sm_syslog(LOG_INFO, e->e_id, "Milter (%s): header, %s", 270590792Sgshapiro m->mf_name, h->h_field); 270664562Sgshapiro 2707168515Sgshapiro if (bitset(SMFIP_HDR_LEADSPC, m->mf_pflags) 2708168515Sgshapiro || *(h->h_value) != ' ') 2709168515Sgshapiro hv = h->h_value; 2710168515Sgshapiro else 2711168515Sgshapiro hv = h->h_value + 1; 2712168515Sgshapiro len_f = strlen(h->h_field) + 1; 2713168515Sgshapiro len_t = len_f + strlen(hv) + 1; 2714168515Sgshapiro if (len_t < 0) 271590792Sgshapiro continue; 2716168515Sgshapiro buf = (char *) xalloc(len_t); 271764562Sgshapiro 2718168515Sgshapiro /* 2719168515Sgshapiro ** Note: currently the call to dequote_internal_chars() 2720168515Sgshapiro ** is not required as h_field is supposed to be 7-bit US-ASCII. 2721168515Sgshapiro */ 2722168515Sgshapiro 2723168515Sgshapiro len_n = dequote_internal_chars(h->h_field, buf, len_f); 2724168515Sgshapiro SM_ASSERT(len_n < len_f); 2725168515Sgshapiro len_v = dequote_internal_chars(hv, buf + len_n + 1, 2726168515Sgshapiro len_t - len_n - 1); 2727168515Sgshapiro SM_ASSERT(len_t >= len_n + 1 + len_v + 1); 2728168515Sgshapiro len_t = len_n + 1 + len_v + 1; 2729168515Sgshapiro 273064562Sgshapiro /* send it over */ 273164562Sgshapiro response = milter_send_command(m, SMFIC_HEADER, buf, 2732168515Sgshapiro len_t, e, state, "header"); 2733168515Sgshapiro sm_free(buf); 273464562Sgshapiro if (m->mf_state == SMFS_ERROR || 273564562Sgshapiro m->mf_state == SMFS_DONE || 273664562Sgshapiro *state != SMFIR_CONTINUE) 273764562Sgshapiro break; 273864562Sgshapiro } 273990792Sgshapiro if (MilterLogLevel > 17) 274090792Sgshapiro sm_syslog(LOG_INFO, e->e_id, "Milter (%s): headers, sent", 274190792Sgshapiro m->mf_name); 274264562Sgshapiro return response; 274364562Sgshapiro} 2744168515Sgshapiro 274590792Sgshapiro/* 274664562Sgshapiro** MILTER_BODY -- send the body to a filter 274764562Sgshapiro** 274864562Sgshapiro** Parameters: 274964562Sgshapiro** m -- current filter. 275064562Sgshapiro** e -- current envelope. 275164562Sgshapiro** state -- return state from response. 275264562Sgshapiro** 275364562Sgshapiro** Returns: 275464562Sgshapiro** response string (may be NULL) 275564562Sgshapiro*/ 275664562Sgshapiro 275764562Sgshapirostatic char * 275864562Sgshapiromilter_body(m, e, state) 275964562Sgshapiro struct milter *m; 276064562Sgshapiro ENVELOPE *e; 276164562Sgshapiro char *state; 276264562Sgshapiro{ 276364562Sgshapiro char bufchar = '\0'; 276464562Sgshapiro char prevchar = '\0'; 276564562Sgshapiro int c; 276664562Sgshapiro char *response = NULL; 276764562Sgshapiro char *bp; 276864562Sgshapiro char buf[MILTER_CHUNK_SIZE]; 276964562Sgshapiro 277064562Sgshapiro if (tTd(64, 10)) 277190792Sgshapiro sm_dprintf("milter_body\n"); 277264562Sgshapiro 277364562Sgshapiro if (bfrewind(e->e_dfp) < 0) 277464562Sgshapiro { 277564562Sgshapiro ExitStat = EX_IOERR; 277664562Sgshapiro *state = SMFIR_TEMPFAIL; 277790792Sgshapiro syserr("milter_body: %s/%cf%s: rewind error", 277890792Sgshapiro qid_printqueue(e->e_qgrp, e->e_qdir), 277990792Sgshapiro DATAFL_LETTER, e->e_id); 278064562Sgshapiro return NULL; 278164562Sgshapiro } 278264562Sgshapiro 278390792Sgshapiro if (MilterLogLevel > 17) 278490792Sgshapiro sm_syslog(LOG_INFO, e->e_id, "Milter (%s): body, send", 278590792Sgshapiro m->mf_name); 278664562Sgshapiro bp = buf; 278790792Sgshapiro while ((c = sm_io_getc(e->e_dfp, SM_TIME_DEFAULT)) != SM_IO_EOF) 278864562Sgshapiro { 278964562Sgshapiro /* Change LF to CRLF */ 279064562Sgshapiro if (c == '\n') 279164562Sgshapiro { 2792168515Sgshapiro#if !_FFR_MILTER_CONVERT_ALL_LF_TO_CRLF 279364562Sgshapiro /* Not a CRLF already? */ 279464562Sgshapiro if (prevchar != '\r') 2795168515Sgshapiro#endif /* !_FFR_MILTER_CONVERT_ALL_LF_TO_CRLF */ 279664562Sgshapiro { 279764562Sgshapiro /* Room for CR now? */ 2798168515Sgshapiro if (bp + 2 > &buf[sizeof(buf)]) 279964562Sgshapiro { 280064562Sgshapiro /* No room, buffer LF */ 280164562Sgshapiro bufchar = c; 280264562Sgshapiro 280364562Sgshapiro /* and send CR now */ 280464562Sgshapiro c = '\r'; 280564562Sgshapiro } 280664562Sgshapiro else 280764562Sgshapiro { 280864562Sgshapiro /* Room to do it now */ 280964562Sgshapiro *bp++ = '\r'; 281064562Sgshapiro prevchar = '\r'; 281164562Sgshapiro } 281264562Sgshapiro } 281364562Sgshapiro } 281464562Sgshapiro *bp++ = (char) c; 281564562Sgshapiro prevchar = c; 2816168515Sgshapiro if (bp >= &buf[sizeof(buf)]) 281764562Sgshapiro { 281864562Sgshapiro /* send chunk */ 281964562Sgshapiro response = milter_send_command(m, SMFIC_BODY, buf, 2820168515Sgshapiro bp - buf, e, state, 2821168515Sgshapiro "body chunk"); 282264562Sgshapiro bp = buf; 282364562Sgshapiro if (bufchar != '\0') 282464562Sgshapiro { 282564562Sgshapiro *bp++ = bufchar; 282664562Sgshapiro bufchar = '\0'; 282764562Sgshapiro prevchar = bufchar; 282864562Sgshapiro } 282964562Sgshapiro } 283064562Sgshapiro if (m->mf_state == SMFS_ERROR || 283164562Sgshapiro m->mf_state == SMFS_DONE || 2832168515Sgshapiro m->mf_state == SMFS_SKIP || 283364562Sgshapiro *state != SMFIR_CONTINUE) 283464562Sgshapiro break; 283564562Sgshapiro } 283664562Sgshapiro 283764562Sgshapiro /* check for read errors */ 283890792Sgshapiro if (sm_io_error(e->e_dfp)) 283964562Sgshapiro { 284064562Sgshapiro ExitStat = EX_IOERR; 284164562Sgshapiro if (*state == SMFIR_CONTINUE || 2842168515Sgshapiro *state == SMFIR_ACCEPT || 2843168515Sgshapiro m->mf_state == SMFS_SKIP) 284464562Sgshapiro { 284564562Sgshapiro *state = SMFIR_TEMPFAIL; 284664562Sgshapiro if (response != NULL) 284764562Sgshapiro { 284890792Sgshapiro sm_free(response); /* XXX */ 284964562Sgshapiro response = NULL; 285064562Sgshapiro } 285164562Sgshapiro } 285290792Sgshapiro syserr("milter_body: %s/%cf%s: read error", 285390792Sgshapiro qid_printqueue(e->e_qgrp, e->e_qdir), 285490792Sgshapiro DATAFL_LETTER, e->e_id); 285564562Sgshapiro return response; 285664562Sgshapiro } 285764562Sgshapiro 285864562Sgshapiro /* send last body chunk */ 285964562Sgshapiro if (bp > buf && 286064562Sgshapiro m->mf_state != SMFS_ERROR && 286164562Sgshapiro m->mf_state != SMFS_DONE && 2862168515Sgshapiro m->mf_state != SMFS_SKIP && 286364562Sgshapiro *state == SMFIR_CONTINUE) 286464562Sgshapiro { 286564562Sgshapiro /* send chunk */ 286664562Sgshapiro response = milter_send_command(m, SMFIC_BODY, buf, bp - buf, 2867168515Sgshapiro e, state, "last body chunk"); 286864562Sgshapiro bp = buf; 286964562Sgshapiro } 287090792Sgshapiro if (MilterLogLevel > 17) 287190792Sgshapiro sm_syslog(LOG_INFO, e->e_id, "Milter (%s): body, sent", 287290792Sgshapiro m->mf_name); 2873168515Sgshapiro if (m->mf_state == SMFS_SKIP) 2874168515Sgshapiro { 2875168515Sgshapiro *state = SMFIR_CONTINUE; 2876168515Sgshapiro m->mf_state = SMFS_READY; 2877168515Sgshapiro } 2878168515Sgshapiro 287964562Sgshapiro return response; 288064562Sgshapiro} 288164562Sgshapiro 288264562Sgshapiro/* 288364562Sgshapiro** Actions 288464562Sgshapiro*/ 288564562Sgshapiro 288690792Sgshapiro/* 2887168515Sgshapiro** ADDLEADINGSPACE -- Add a leading space to a string 2888168515Sgshapiro** 2889168515Sgshapiro** Parameters: 2890168515Sgshapiro** str -- string 2891168515Sgshapiro** rp -- resource pool for allocations 2892168515Sgshapiro** 2893168515Sgshapiro** Returns: 2894168515Sgshapiro** pointer to new string 2895168515Sgshapiro*/ 2896168515Sgshapiro 2897168515Sgshapirostatic char *addleadingspace __P((char *, SM_RPOOL_T *)); 2898168515Sgshapiro 2899168515Sgshapirostatic char * 2900168515Sgshapiroaddleadingspace(str, rp) 2901168515Sgshapiro char *str; 2902168515Sgshapiro SM_RPOOL_T *rp; 2903168515Sgshapiro{ 2904168515Sgshapiro size_t l; 2905168515Sgshapiro char *new; 2906168515Sgshapiro 2907168515Sgshapiro SM_ASSERT(str != NULL); 2908168515Sgshapiro l = strlen(str); 2909168515Sgshapiro SM_ASSERT(l + 2 > l); 2910168515Sgshapiro new = sm_rpool_malloc_x(rp, l + 2); 2911168515Sgshapiro new[0] = ' '; 2912168515Sgshapiro new[1] = '\0'; 2913168515Sgshapiro sm_strlcpy(new + 1, str, l + 1); 2914168515Sgshapiro return new; 2915168515Sgshapiro} 2916168515Sgshapiro 2917168515Sgshapiro/* 291864562Sgshapiro** MILTER_ADDHEADER -- Add the supplied header to the message 291964562Sgshapiro** 292064562Sgshapiro** Parameters: 2921168515Sgshapiro** m -- current filter. 292264562Sgshapiro** response -- encoded form of header/value. 292364562Sgshapiro** rlen -- length of response. 292464562Sgshapiro** e -- current envelope. 292564562Sgshapiro** 292664562Sgshapiro** Returns: 292764562Sgshapiro** none 292864562Sgshapiro*/ 292964562Sgshapiro 293064562Sgshapirostatic void 2931168515Sgshapiromilter_addheader(m, response, rlen, e) 2932168515Sgshapiro struct milter *m; 293364562Sgshapiro char *response; 293464562Sgshapiro ssize_t rlen; 293564562Sgshapiro ENVELOPE *e; 293664562Sgshapiro{ 2937168515Sgshapiro int mh_v_len; 2938168515Sgshapiro char *val, *mh_value; 293971345Sgshapiro HDR *h; 294064562Sgshapiro 294164562Sgshapiro if (tTd(64, 10)) 294290792Sgshapiro sm_dprintf("milter_addheader: "); 294364562Sgshapiro 294464562Sgshapiro /* sanity checks */ 294564562Sgshapiro if (response == NULL) 294664562Sgshapiro { 294764562Sgshapiro if (tTd(64, 10)) 294890792Sgshapiro sm_dprintf("NULL response\n"); 294964562Sgshapiro return; 295064562Sgshapiro } 295164562Sgshapiro 295264562Sgshapiro if (rlen < 2 || strlen(response) + 1 >= (size_t) rlen) 295364562Sgshapiro { 295464562Sgshapiro if (tTd(64, 10)) 2955168515Sgshapiro sm_dprintf("didn't follow protocol (total len %d != rlen %d)\n", 2956168515Sgshapiro (int) strlen(response), (int) (rlen - 1)); 295764562Sgshapiro return; 295864562Sgshapiro } 295964562Sgshapiro 296064562Sgshapiro /* Find separating NUL */ 296164562Sgshapiro val = response + strlen(response) + 1; 296264562Sgshapiro 296364562Sgshapiro /* another sanity check */ 296464562Sgshapiro if (strlen(response) + strlen(val) + 2 != (size_t) rlen) 296564562Sgshapiro { 296664562Sgshapiro if (tTd(64, 10)) 296790792Sgshapiro sm_dprintf("didn't follow protocol (part len)\n"); 296864562Sgshapiro return; 296964562Sgshapiro } 297064562Sgshapiro 297164562Sgshapiro if (*response == '\0') 297264562Sgshapiro { 297364562Sgshapiro if (tTd(64, 10)) 297490792Sgshapiro sm_dprintf("empty field name\n"); 297564562Sgshapiro return; 297664562Sgshapiro } 297764562Sgshapiro 297871345Sgshapiro for (h = e->e_header; h != NULL; h = h->h_link) 297971345Sgshapiro { 298090792Sgshapiro if (sm_strcasecmp(h->h_field, response) == 0 && 298171345Sgshapiro !bitset(H_USER, h->h_flags) && 298271345Sgshapiro !bitset(H_TRACE, h->h_flags)) 298371345Sgshapiro break; 298471345Sgshapiro } 298571345Sgshapiro 2986168515Sgshapiro mh_v_len = 0; 2987168515Sgshapiro mh_value = quote_internal_chars(val, NULL, &mh_v_len); 2988168515Sgshapiro 298964562Sgshapiro /* add to e_msgsize */ 299064562Sgshapiro e->e_msgsize += strlen(response) + 2 + strlen(val); 299164562Sgshapiro 299271345Sgshapiro if (h != NULL) 299371345Sgshapiro { 299471345Sgshapiro if (tTd(64, 10)) 299590792Sgshapiro sm_dprintf("Replace default header %s value with %s\n", 2996168515Sgshapiro h->h_field, mh_value); 299790792Sgshapiro if (MilterLogLevel > 8) 299890792Sgshapiro sm_syslog(LOG_INFO, e->e_id, 299990792Sgshapiro "Milter change: default header %s value with %s", 3000168515Sgshapiro h->h_field, mh_value); 3001168515Sgshapiro if (bitset(SMFIP_HDR_LEADSPC, m->mf_pflags)) 3002168515Sgshapiro h->h_value = mh_value; 3003168515Sgshapiro else 3004168515Sgshapiro { 3005203004Sgshapiro h->h_value = addleadingspace(mh_value, e->e_rpool); 3006168515Sgshapiro SM_FREE(mh_value); 3007168515Sgshapiro } 300871345Sgshapiro h->h_flags |= H_USER; 300971345Sgshapiro } 301071345Sgshapiro else 301171345Sgshapiro { 301271345Sgshapiro if (tTd(64, 10)) 3013168515Sgshapiro sm_dprintf("Add %s: %s\n", response, mh_value); 301490792Sgshapiro if (MilterLogLevel > 8) 3015168515Sgshapiro sm_syslog(LOG_INFO, e->e_id, 3016168515Sgshapiro "Milter add: header: %s: %s", 3017168515Sgshapiro response, mh_value); 3018168515Sgshapiro addheader(newstr(response), mh_value, H_USER, e, 3019168515Sgshapiro !bitset(SMFIP_HDR_LEADSPC, m->mf_pflags)); 3020168515Sgshapiro SM_FREE(mh_value); 302171345Sgshapiro } 302264562Sgshapiro} 3023168515Sgshapiro 302490792Sgshapiro/* 3025132943Sgshapiro** MILTER_INSHEADER -- Insert the supplied header 3026132943Sgshapiro** 3027132943Sgshapiro** Parameters: 3028168515Sgshapiro** m -- current filter. 3029132943Sgshapiro** response -- encoded form of header/value. 3030132943Sgshapiro** rlen -- length of response. 3031132943Sgshapiro** e -- current envelope. 3032132943Sgshapiro** 3033132943Sgshapiro** Returns: 3034132943Sgshapiro** none 3035132943Sgshapiro** 3036157001Sgshapiro** Notes: 3037157001Sgshapiro** Unlike milter_addheader(), this does not attempt to determine 3038157001Sgshapiro** if the header already exists in the envelope, even a 3039157001Sgshapiro** deleted version. It just blindly inserts. 3040132943Sgshapiro*/ 3041132943Sgshapiro 3042132943Sgshapirostatic void 3043168515Sgshapiromilter_insheader(m, response, rlen, e) 3044168515Sgshapiro struct milter *m; 3045132943Sgshapiro char *response; 3046132943Sgshapiro ssize_t rlen; 3047132943Sgshapiro ENVELOPE *e; 3048132943Sgshapiro{ 3049132943Sgshapiro mi_int32 idx, i; 3050168515Sgshapiro int mh_v_len; 3051168515Sgshapiro char *field, *val, *mh_value; 3052132943Sgshapiro 3053132943Sgshapiro if (tTd(64, 10)) 3054132943Sgshapiro sm_dprintf("milter_insheader: "); 3055132943Sgshapiro 3056132943Sgshapiro /* sanity checks */ 3057132943Sgshapiro if (response == NULL) 3058132943Sgshapiro { 3059132943Sgshapiro if (tTd(64, 10)) 3060132943Sgshapiro sm_dprintf("NULL response\n"); 3061132943Sgshapiro return; 3062132943Sgshapiro } 3063132943Sgshapiro 3064132943Sgshapiro if (rlen < 2 || strlen(response) + 1 >= (size_t) rlen) 3065132943Sgshapiro { 3066132943Sgshapiro if (tTd(64, 10)) 3067132943Sgshapiro sm_dprintf("didn't follow protocol (total len)\n"); 3068132943Sgshapiro return; 3069132943Sgshapiro } 3070132943Sgshapiro 3071132943Sgshapiro /* decode */ 3072132943Sgshapiro (void) memcpy((char *) &i, response, MILTER_LEN_BYTES); 3073132943Sgshapiro idx = ntohl(i); 3074132943Sgshapiro field = response + MILTER_LEN_BYTES; 3075132943Sgshapiro val = field + strlen(field) + 1; 3076132943Sgshapiro 3077132943Sgshapiro /* another sanity check */ 3078132943Sgshapiro if (MILTER_LEN_BYTES + strlen(field) + 1 + 3079132943Sgshapiro strlen(val) + 1 != (size_t) rlen) 3080132943Sgshapiro { 3081132943Sgshapiro if (tTd(64, 10)) 3082132943Sgshapiro sm_dprintf("didn't follow protocol (part len)\n"); 3083132943Sgshapiro return; 3084132943Sgshapiro } 3085132943Sgshapiro 3086132943Sgshapiro if (*field == '\0') 3087132943Sgshapiro { 3088132943Sgshapiro if (tTd(64, 10)) 3089132943Sgshapiro sm_dprintf("empty field name\n"); 3090132943Sgshapiro return; 3091132943Sgshapiro } 3092132943Sgshapiro 3093132943Sgshapiro /* add to e_msgsize */ 3094132943Sgshapiro e->e_msgsize += strlen(response) + 2 + strlen(val); 3095132943Sgshapiro 3096132943Sgshapiro if (tTd(64, 10)) 3097168515Sgshapiro sm_dprintf("Insert (%d) %s: %s\n", idx, field, val); 3098132943Sgshapiro if (MilterLogLevel > 8) 3099132943Sgshapiro sm_syslog(LOG_INFO, e->e_id, 3100157001Sgshapiro "Milter insert (%d): header: %s: %s", 3101132943Sgshapiro idx, field, val); 3102168515Sgshapiro mh_v_len = 0; 3103168515Sgshapiro mh_value = quote_internal_chars(val, NULL, &mh_v_len); 3104168515Sgshapiro insheader(idx, newstr(field), mh_value, H_USER, e, 3105168515Sgshapiro !bitset(SMFIP_HDR_LEADSPC, m->mf_pflags)); 3106168515Sgshapiro SM_FREE(mh_value); 3107132943Sgshapiro} 3108168515Sgshapiro 3109132943Sgshapiro/* 311064562Sgshapiro** MILTER_CHANGEHEADER -- Change the supplied header in the message 311164562Sgshapiro** 311264562Sgshapiro** Parameters: 3113168515Sgshapiro** m -- current filter. 311464562Sgshapiro** response -- encoded form of header/index/value. 311564562Sgshapiro** rlen -- length of response. 311664562Sgshapiro** e -- current envelope. 311764562Sgshapiro** 311864562Sgshapiro** Returns: 311964562Sgshapiro** none 312064562Sgshapiro*/ 312164562Sgshapiro 312264562Sgshapirostatic void 3123168515Sgshapiromilter_changeheader(m, response, rlen, e) 3124168515Sgshapiro struct milter *m; 312564562Sgshapiro char *response; 312664562Sgshapiro ssize_t rlen; 312764562Sgshapiro ENVELOPE *e; 312864562Sgshapiro{ 312964562Sgshapiro mi_int32 i, index; 3130168515Sgshapiro int mh_v_len; 3131168515Sgshapiro char *field, *val, *mh_value; 313271345Sgshapiro HDR *h, *sysheader; 313364562Sgshapiro 313464562Sgshapiro if (tTd(64, 10)) 313590792Sgshapiro sm_dprintf("milter_changeheader: "); 313664562Sgshapiro 313764562Sgshapiro /* sanity checks */ 313864562Sgshapiro if (response == NULL) 313964562Sgshapiro { 314064562Sgshapiro if (tTd(64, 10)) 314190792Sgshapiro sm_dprintf("NULL response\n"); 314264562Sgshapiro return; 314364562Sgshapiro } 314464562Sgshapiro 314564562Sgshapiro if (rlen < 2 || strlen(response) + 1 >= (size_t) rlen) 314664562Sgshapiro { 314764562Sgshapiro if (tTd(64, 10)) 314890792Sgshapiro sm_dprintf("didn't follow protocol (total len)\n"); 314964562Sgshapiro return; 315064562Sgshapiro } 315164562Sgshapiro 315264562Sgshapiro /* Find separating NUL */ 315364562Sgshapiro (void) memcpy((char *) &i, response, MILTER_LEN_BYTES); 315464562Sgshapiro index = ntohl(i); 315564562Sgshapiro field = response + MILTER_LEN_BYTES; 315664562Sgshapiro val = field + strlen(field) + 1; 315764562Sgshapiro 315864562Sgshapiro /* another sanity check */ 315964562Sgshapiro if (MILTER_LEN_BYTES + strlen(field) + 1 + 316064562Sgshapiro strlen(val) + 1 != (size_t) rlen) 316164562Sgshapiro { 316264562Sgshapiro if (tTd(64, 10)) 316390792Sgshapiro sm_dprintf("didn't follow protocol (part len)\n"); 316464562Sgshapiro return; 316564562Sgshapiro } 316664562Sgshapiro 316764562Sgshapiro if (*field == '\0') 316864562Sgshapiro { 316964562Sgshapiro if (tTd(64, 10)) 317090792Sgshapiro sm_dprintf("empty field name\n"); 317164562Sgshapiro return; 317264562Sgshapiro } 317364562Sgshapiro 3174168515Sgshapiro mh_v_len = 0; 3175168515Sgshapiro mh_value = quote_internal_chars(val, NULL, &mh_v_len); 3176168515Sgshapiro 317771345Sgshapiro sysheader = NULL; 317864562Sgshapiro for (h = e->e_header; h != NULL; h = h->h_link) 317964562Sgshapiro { 318090792Sgshapiro if (sm_strcasecmp(h->h_field, field) == 0) 318171345Sgshapiro { 3182168515Sgshapiro if (bitset(H_USER, h->h_flags) && --index <= 0) 318371345Sgshapiro { 318471345Sgshapiro sysheader = NULL; 318571345Sgshapiro break; 318671345Sgshapiro } 318771345Sgshapiro else if (!bitset(H_USER, h->h_flags) && 318871345Sgshapiro !bitset(H_TRACE, h->h_flags)) 318971345Sgshapiro { 319071345Sgshapiro /* 319171345Sgshapiro ** DRUMS msg-fmt draft says can only have 319271345Sgshapiro ** multiple occurences of trace fields, 319371345Sgshapiro ** so make sure we replace any non-trace, 319471345Sgshapiro ** non-user field. 319571345Sgshapiro */ 319671345Sgshapiro 319771345Sgshapiro sysheader = h; 319871345Sgshapiro } 319971345Sgshapiro } 320064562Sgshapiro } 320164562Sgshapiro 320271345Sgshapiro /* if not found as user-provided header at index, use sysheader */ 320364562Sgshapiro if (h == NULL) 320471345Sgshapiro h = sysheader; 320571345Sgshapiro 320671345Sgshapiro if (h == NULL) 320764562Sgshapiro { 320864562Sgshapiro if (*val == '\0') 320964562Sgshapiro { 321064562Sgshapiro if (tTd(64, 10)) 3211141858Sgshapiro sm_dprintf("Delete (noop) %s\n", field); 3212141858Sgshapiro if (MilterLogLevel > 8) 3213141858Sgshapiro sm_syslog(LOG_INFO, e->e_id, 3214141858Sgshapiro "Milter delete (noop): header: %s" 3215141858Sgshapiro , field); 321664562Sgshapiro } 321764562Sgshapiro else 321864562Sgshapiro { 321964562Sgshapiro /* treat modify value with no existing header as add */ 322064562Sgshapiro if (tTd(64, 10)) 3221168515Sgshapiro sm_dprintf("Add %s: %s\n", field, mh_value); 3222141858Sgshapiro if (MilterLogLevel > 8) 3223141858Sgshapiro sm_syslog(LOG_INFO, e->e_id, 3224141858Sgshapiro "Milter change (add): header: %s: %s" 3225168515Sgshapiro , field, mh_value); 3226168515Sgshapiro addheader(newstr(field), mh_value, H_USER, e, 3227168515Sgshapiro !bitset(SMFIP_HDR_LEADSPC, m->mf_pflags)); 322864562Sgshapiro } 322964562Sgshapiro return; 323064562Sgshapiro } 323164562Sgshapiro 323264562Sgshapiro if (tTd(64, 10)) 323364562Sgshapiro { 323464562Sgshapiro if (*val == '\0') 323564562Sgshapiro { 3236168515Sgshapiro sm_dprintf("Delete%s %s:%s\n", 323790792Sgshapiro h == sysheader ? " (default header)" : "", 323890792Sgshapiro field, 323990792Sgshapiro h->h_value == NULL ? "<NULL>" : h->h_value); 324064562Sgshapiro } 324164562Sgshapiro else 324264562Sgshapiro { 324390792Sgshapiro sm_dprintf("Change%s %s: from %s to %s\n", 324490792Sgshapiro h == sysheader ? " (default header)" : "", 324590792Sgshapiro field, 324690792Sgshapiro h->h_value == NULL ? "<NULL>" : h->h_value, 3247168515Sgshapiro mh_value); 324864562Sgshapiro } 324964562Sgshapiro } 325064562Sgshapiro 325190792Sgshapiro if (MilterLogLevel > 8) 325290792Sgshapiro { 325390792Sgshapiro if (*val == '\0') 325490792Sgshapiro { 325590792Sgshapiro sm_syslog(LOG_INFO, e->e_id, 3256168515Sgshapiro "Milter delete: header%s %s:%s", 325790792Sgshapiro h == sysheader ? " (default header)" : "", 325890792Sgshapiro field, 325990792Sgshapiro h->h_value == NULL ? "<NULL>" : h->h_value); 326090792Sgshapiro } 326190792Sgshapiro else 326290792Sgshapiro { 326390792Sgshapiro sm_syslog(LOG_INFO, e->e_id, 3264132943Sgshapiro "Milter change: header%s %s: from %s to %s", 326590792Sgshapiro h == sysheader ? " (default header)" : "", 326690792Sgshapiro field, 326790792Sgshapiro h->h_value == NULL ? "<NULL>" : h->h_value, 3268168515Sgshapiro mh_value); 326990792Sgshapiro } 327090792Sgshapiro } 327190792Sgshapiro 327271345Sgshapiro if (h != sysheader && h->h_value != NULL) 327364562Sgshapiro { 327490792Sgshapiro size_t l; 327590792Sgshapiro 327690792Sgshapiro l = strlen(h->h_value); 327790792Sgshapiro if (l > e->e_msgsize) 327890792Sgshapiro e->e_msgsize = 0; 327990792Sgshapiro else 328090792Sgshapiro e->e_msgsize -= l; 328190792Sgshapiro /* rpool, don't free: sm_free(h->h_value); XXX */ 328264562Sgshapiro } 328364562Sgshapiro 328464562Sgshapiro if (*val == '\0') 328564562Sgshapiro { 328664562Sgshapiro /* Remove "Field: " from message size */ 328771345Sgshapiro if (h != sysheader) 328890792Sgshapiro { 328990792Sgshapiro size_t l; 329090792Sgshapiro 329190792Sgshapiro l = strlen(h->h_field) + 2; 329290792Sgshapiro if (l > e->e_msgsize) 329390792Sgshapiro e->e_msgsize = 0; 329490792Sgshapiro else 329590792Sgshapiro e->e_msgsize -= l; 329690792Sgshapiro } 329764562Sgshapiro h->h_value = NULL; 3298168515Sgshapiro SM_FREE(mh_value); 329964562Sgshapiro } 330064562Sgshapiro else 330164562Sgshapiro { 3302168515Sgshapiro if (bitset(SMFIP_HDR_LEADSPC, m->mf_pflags)) 3303168515Sgshapiro h->h_value = mh_value; 3304168515Sgshapiro else 3305168515Sgshapiro { 3306203004Sgshapiro h->h_value = addleadingspace(mh_value, e->e_rpool); 3307168515Sgshapiro SM_FREE(mh_value); 3308168515Sgshapiro } 330971345Sgshapiro h->h_flags |= H_USER; 331064562Sgshapiro e->e_msgsize += strlen(h->h_value); 331164562Sgshapiro } 331264562Sgshapiro} 3313168515Sgshapiro 331490792Sgshapiro/* 3315168515Sgshapiro** MILTER_SPLIT_RESPONSE -- Split response into fields. 3316168515Sgshapiro** 3317168515Sgshapiro** Parameters: 3318168515Sgshapiro** response -- encoded repsonse. 3319168515Sgshapiro** rlen -- length of response. 3320168515Sgshapiro** pargc -- number of arguments (ouput) 3321168515Sgshapiro** 3322168515Sgshapiro** Returns: 3323168515Sgshapiro** array of pointers to the individual strings 3324168515Sgshapiro*/ 3325168515Sgshapiro 3326168515Sgshapirostatic char **milter_split_response __P((char *, ssize_t, int *)); 3327168515Sgshapiro 3328168515Sgshapirostatic char ** 3329168515Sgshapiromilter_split_response(response, rlen, pargc) 3330168515Sgshapiro char *response; 3331168515Sgshapiro ssize_t rlen; 3332168515Sgshapiro int *pargc; 3333168515Sgshapiro{ 3334168515Sgshapiro char **s; 3335168515Sgshapiro size_t i; 3336168515Sgshapiro int elem, nelem; 3337168515Sgshapiro 3338168515Sgshapiro SM_ASSERT(response != NULL); 3339168515Sgshapiro SM_ASSERT(pargc != NULL); 3340168515Sgshapiro *pargc = 0; 3341168515Sgshapiro if (rlen < 2 || strlen(response) >= (size_t) rlen) 3342168515Sgshapiro { 3343168515Sgshapiro if (tTd(64, 10)) 3344168515Sgshapiro sm_dprintf("didn't follow protocol (total len %d != rlen %d)\n", 3345168515Sgshapiro (int) strlen(response), (int) (rlen - 1)); 3346168515Sgshapiro return NULL; 3347168515Sgshapiro } 3348168515Sgshapiro 3349168515Sgshapiro nelem = 0; 3350168515Sgshapiro for (i = 0; i < rlen; i++) 3351168515Sgshapiro { 3352168515Sgshapiro if (response[i] == '\0') 3353168515Sgshapiro ++nelem; 3354168515Sgshapiro } 3355168515Sgshapiro if (nelem == 0) 3356168515Sgshapiro return NULL; 3357168515Sgshapiro 3358168515Sgshapiro /* last entry is only for the name */ 3359203004Sgshapiro s = (char **)malloc((nelem + 1) * (sizeof(*s))); 3360168515Sgshapiro if (s == NULL) 3361168515Sgshapiro return NULL; 3362168515Sgshapiro s[0] = response; 3363168515Sgshapiro for (i = 0, elem = 0; i < rlen && elem < nelem; i++) 3364168515Sgshapiro { 3365168515Sgshapiro if (response[i] == '\0') 3366168515Sgshapiro { 3367168515Sgshapiro ++elem; 3368168515Sgshapiro if (i + 1 >= rlen) 3369168515Sgshapiro s[elem] = NULL; 3370168515Sgshapiro else 3371168515Sgshapiro s[elem] = &(response[i + 1]); 3372168515Sgshapiro } 3373168515Sgshapiro } 3374168515Sgshapiro *pargc = nelem; 3375168515Sgshapiro 3376168515Sgshapiro if (tTd(64, 10)) 3377168515Sgshapiro { 3378168515Sgshapiro for (elem = 0; elem < nelem; elem++) 3379168515Sgshapiro sm_dprintf("argv[%d]=\"%s\"\n", elem, s[elem]); 3380168515Sgshapiro } 3381168515Sgshapiro 3382168515Sgshapiro /* overwrite last entry (already done above, just paranoia) */ 3383168515Sgshapiro s[elem] = NULL; 3384168515Sgshapiro return s; 3385168515Sgshapiro} 3386168515Sgshapiro 3387168515Sgshapiro/* 3388168515Sgshapiro** MILTER_CHGFROM -- Change the envelope sender address 3389168515Sgshapiro** 3390168515Sgshapiro** Parameters: 3391168515Sgshapiro** response -- encoded form of recipient address. 3392168515Sgshapiro** rlen -- length of response. 3393168515Sgshapiro** e -- current envelope. 3394168515Sgshapiro** 3395168515Sgshapiro** Returns: 3396168515Sgshapiro** none 3397168515Sgshapiro*/ 3398168515Sgshapiro 3399168515Sgshapirostatic void 3400168515Sgshapiromilter_chgfrom(response, rlen, e) 3401168515Sgshapiro char *response; 3402168515Sgshapiro ssize_t rlen; 3403168515Sgshapiro ENVELOPE *e; 3404168515Sgshapiro{ 3405168515Sgshapiro int olderrors, argc; 3406168515Sgshapiro char **argv; 3407168515Sgshapiro 3408168515Sgshapiro if (tTd(64, 10)) 3409168515Sgshapiro sm_dprintf("milter_chgfrom: "); 3410168515Sgshapiro 3411168515Sgshapiro /* sanity checks */ 3412168515Sgshapiro if (response == NULL) 3413168515Sgshapiro { 3414168515Sgshapiro if (tTd(64, 10)) 3415168515Sgshapiro sm_dprintf("NULL response\n"); 3416168515Sgshapiro return; 3417168515Sgshapiro } 3418168515Sgshapiro 3419168515Sgshapiro if (*response == '\0' || 3420168515Sgshapiro strlen(response) + 1 > (size_t) rlen) 3421168515Sgshapiro { 3422168515Sgshapiro if (tTd(64, 10)) 3423168515Sgshapiro sm_dprintf("didn't follow protocol (total len %d != rlen %d)\n", 3424168515Sgshapiro (int) strlen(response), (int) (rlen - 1)); 3425168515Sgshapiro return; 3426168515Sgshapiro } 3427168515Sgshapiro 3428168515Sgshapiro if (tTd(64, 10)) 3429168515Sgshapiro sm_dprintf("%s\n", response); 3430168515Sgshapiro if (MilterLogLevel > 8) 3431168515Sgshapiro sm_syslog(LOG_INFO, e->e_id, "Milter chgfrom: %s", response); 3432168515Sgshapiro argv = milter_split_response(response, rlen, &argc); 3433168515Sgshapiro if (argc < 1 || argc > 2) 3434168515Sgshapiro { 3435168515Sgshapiro if (tTd(64, 10)) 3436168515Sgshapiro sm_dprintf("didn't follow protocol argc=%d\n", argc); 3437168515Sgshapiro return; 3438168515Sgshapiro } 3439168515Sgshapiro 3440168515Sgshapiro olderrors = Errors; 3441168515Sgshapiro setsender(argv[0], e, NULL, '\0', false); 3442168515Sgshapiro if (argc == 2) 3443168515Sgshapiro { 3444168515Sgshapiro reset_mail_esmtp_args(e); 3445168515Sgshapiro 3446168515Sgshapiro /* 3447168515Sgshapiro ** need "features" here: how to get those? via e? 3448168515Sgshapiro ** "fake" it for now: allow everything. 3449168515Sgshapiro */ 3450168515Sgshapiro 3451168515Sgshapiro parse_esmtp_args(e, NULL, argv[0], argv[1], "MAIL", NULL, 3452168515Sgshapiro mail_esmtp_args); 3453168515Sgshapiro } 3454168515Sgshapiro Errors = olderrors; 3455168515Sgshapiro return; 3456168515Sgshapiro} 3457168515Sgshapiro 3458168515Sgshapiro/* 3459168515Sgshapiro** MILTER_ADDRCPT_PAR -- Add the supplied recipient to the message 3460168515Sgshapiro** 3461168515Sgshapiro** Parameters: 3462168515Sgshapiro** response -- encoded form of recipient address. 3463168515Sgshapiro** rlen -- length of response. 3464168515Sgshapiro** e -- current envelope. 3465168515Sgshapiro** 3466168515Sgshapiro** Returns: 3467168515Sgshapiro** none 3468168515Sgshapiro*/ 3469168515Sgshapiro 3470168515Sgshapirostatic void 3471168515Sgshapiromilter_addrcpt_par(response, rlen, e) 3472168515Sgshapiro char *response; 3473168515Sgshapiro ssize_t rlen; 3474168515Sgshapiro ENVELOPE *e; 3475168515Sgshapiro{ 3476168515Sgshapiro int olderrors, argc; 3477168515Sgshapiro char *delimptr; 3478168515Sgshapiro char **argv; 3479168515Sgshapiro ADDRESS *a; 3480168515Sgshapiro 3481168515Sgshapiro if (tTd(64, 10)) 3482168515Sgshapiro sm_dprintf("milter_addrcpt_par: "); 3483168515Sgshapiro 3484168515Sgshapiro /* sanity checks */ 3485168515Sgshapiro if (response == NULL) 3486168515Sgshapiro { 3487168515Sgshapiro if (tTd(64, 10)) 3488168515Sgshapiro sm_dprintf("NULL response\n"); 3489168515Sgshapiro return; 3490168515Sgshapiro } 3491168515Sgshapiro 3492168515Sgshapiro if (tTd(64, 10)) 3493168515Sgshapiro sm_dprintf("%s\n", response); 3494168515Sgshapiro if (MilterLogLevel > 8) 3495168515Sgshapiro sm_syslog(LOG_INFO, e->e_id, "Milter add: rcpt: %s", response); 3496168515Sgshapiro 3497168515Sgshapiro argv = milter_split_response(response, rlen, &argc); 3498168515Sgshapiro if (argc < 1 || argc > 2) 3499168515Sgshapiro { 3500168515Sgshapiro if (tTd(64, 10)) 3501168515Sgshapiro sm_dprintf("didn't follow protocol argc=%d\n", argc); 3502168515Sgshapiro return; 3503168515Sgshapiro } 3504168515Sgshapiro olderrors = Errors; 3505168515Sgshapiro 3506168515Sgshapiro /* how to set ESMTP arguments? */ 3507168515Sgshapiro a = parseaddr(argv[0], NULLADDR, RF_COPYALL, ' ', &delimptr, e, true); 3508168515Sgshapiro 3509168515Sgshapiro if (a != NULL && olderrors == Errors) 3510168515Sgshapiro { 3511168515Sgshapiro parse_esmtp_args(e, a, argv[0], argv[1], "RCPT", NULL, 3512168515Sgshapiro rcpt_esmtp_args); 3513168515Sgshapiro if (olderrors == Errors) 3514168515Sgshapiro a = recipient(a, &e->e_sendqueue, 0, e); 3515168515Sgshapiro else 3516168515Sgshapiro sm_dprintf("olderrors=%d, Errors=%d\n", 3517168515Sgshapiro olderrors, Errors); 3518168515Sgshapiro } 3519168515Sgshapiro else 3520168515Sgshapiro { 3521168515Sgshapiro sm_dprintf("a=%p, olderrors=%d, Errors=%d\n", 3522168515Sgshapiro a, olderrors, Errors); 3523168515Sgshapiro } 3524168515Sgshapiro 3525168515Sgshapiro Errors = olderrors; 3526168515Sgshapiro return; 3527168515Sgshapiro} 3528168515Sgshapiro 3529168515Sgshapiro/* 353064562Sgshapiro** MILTER_ADDRCPT -- Add the supplied recipient to the message 353164562Sgshapiro** 353264562Sgshapiro** Parameters: 353364562Sgshapiro** response -- encoded form of recipient address. 353464562Sgshapiro** rlen -- length of response. 353564562Sgshapiro** e -- current envelope. 353664562Sgshapiro** 353764562Sgshapiro** Returns: 353864562Sgshapiro** none 353964562Sgshapiro*/ 354064562Sgshapiro 354164562Sgshapirostatic void 354264562Sgshapiromilter_addrcpt(response, rlen, e) 354364562Sgshapiro char *response; 354464562Sgshapiro ssize_t rlen; 354564562Sgshapiro ENVELOPE *e; 354664562Sgshapiro{ 3547120256Sgshapiro int olderrors; 3548120256Sgshapiro 354964562Sgshapiro if (tTd(64, 10)) 355090792Sgshapiro sm_dprintf("milter_addrcpt: "); 355164562Sgshapiro 355264562Sgshapiro /* sanity checks */ 355364562Sgshapiro if (response == NULL) 355464562Sgshapiro { 355564562Sgshapiro if (tTd(64, 10)) 355690792Sgshapiro sm_dprintf("NULL response\n"); 355764562Sgshapiro return; 355864562Sgshapiro } 355964562Sgshapiro 356064562Sgshapiro if (*response == '\0' || 356164562Sgshapiro strlen(response) + 1 != (size_t) rlen) 356264562Sgshapiro { 356364562Sgshapiro if (tTd(64, 10)) 356490792Sgshapiro sm_dprintf("didn't follow protocol (total len %d != rlen %d)\n", 356590792Sgshapiro (int) strlen(response), (int) (rlen - 1)); 356664562Sgshapiro return; 356764562Sgshapiro } 356864562Sgshapiro 356964562Sgshapiro if (tTd(64, 10)) 357090792Sgshapiro sm_dprintf("%s\n", response); 357190792Sgshapiro if (MilterLogLevel > 8) 357290792Sgshapiro sm_syslog(LOG_INFO, e->e_id, "Milter add: rcpt: %s", response); 3573120256Sgshapiro olderrors = Errors; 3574132943Sgshapiro (void) sendtolist(response, NULLADDR, &e->e_sendqueue, 0, e); 3575132943Sgshapiro Errors = olderrors; 357664562Sgshapiro return; 357764562Sgshapiro} 3578168515Sgshapiro 357990792Sgshapiro/* 358064562Sgshapiro** MILTER_DELRCPT -- Delete the supplied recipient from the message 358164562Sgshapiro** 358264562Sgshapiro** Parameters: 358364562Sgshapiro** response -- encoded form of recipient address. 358464562Sgshapiro** rlen -- length of response. 358564562Sgshapiro** e -- current envelope. 358664562Sgshapiro** 358764562Sgshapiro** Returns: 358864562Sgshapiro** none 358964562Sgshapiro*/ 359064562Sgshapiro 359164562Sgshapirostatic void 359264562Sgshapiromilter_delrcpt(response, rlen, e) 359364562Sgshapiro char *response; 359464562Sgshapiro ssize_t rlen; 359564562Sgshapiro ENVELOPE *e; 359664562Sgshapiro{ 359764562Sgshapiro if (tTd(64, 10)) 359890792Sgshapiro sm_dprintf("milter_delrcpt: "); 359964562Sgshapiro 360064562Sgshapiro /* sanity checks */ 360164562Sgshapiro if (response == NULL) 360264562Sgshapiro { 360364562Sgshapiro if (tTd(64, 10)) 360490792Sgshapiro sm_dprintf("NULL response\n"); 360564562Sgshapiro return; 360664562Sgshapiro } 360764562Sgshapiro 360864562Sgshapiro if (*response == '\0' || 360964562Sgshapiro strlen(response) + 1 != (size_t) rlen) 361064562Sgshapiro { 361164562Sgshapiro if (tTd(64, 10)) 3612168515Sgshapiro sm_dprintf("didn't follow protocol (total len %d != rlen %d)\n", 3613168515Sgshapiro (int) strlen(response), (int) (rlen - 1)); 361464562Sgshapiro return; 361564562Sgshapiro } 361664562Sgshapiro 361764562Sgshapiro if (tTd(64, 10)) 361890792Sgshapiro sm_dprintf("%s\n", response); 361990792Sgshapiro if (MilterLogLevel > 8) 362090792Sgshapiro sm_syslog(LOG_INFO, e->e_id, "Milter delete: rcpt %s", 362190792Sgshapiro response); 362264562Sgshapiro (void) removefromlist(response, &e->e_sendqueue, e); 362364562Sgshapiro return; 362464562Sgshapiro} 3625168515Sgshapiro 362690792Sgshapiro/* 362790792Sgshapiro** MILTER_REPLBODY -- Replace the current data file with new body 362864562Sgshapiro** 362964562Sgshapiro** Parameters: 363064562Sgshapiro** response -- encoded form of new body. 363164562Sgshapiro** rlen -- length of response. 363264562Sgshapiro** newfilter -- if first time called by a new filter 363364562Sgshapiro** e -- current envelope. 363464562Sgshapiro** 363564562Sgshapiro** Returns: 363664562Sgshapiro** 0 upon success, -1 upon failure 363764562Sgshapiro*/ 363864562Sgshapiro 363964562Sgshapirostatic int 364064562Sgshapiromilter_replbody(response, rlen, newfilter, e) 364164562Sgshapiro char *response; 364264562Sgshapiro ssize_t rlen; 364364562Sgshapiro bool newfilter; 364464562Sgshapiro ENVELOPE *e; 364564562Sgshapiro{ 364664562Sgshapiro static char prevchar; 364764562Sgshapiro int i; 364864562Sgshapiro 364964562Sgshapiro if (tTd(64, 10)) 365090792Sgshapiro sm_dprintf("milter_replbody\n"); 365164562Sgshapiro 365290792Sgshapiro /* If a new filter, reset previous character and truncate data file */ 365364562Sgshapiro if (newfilter) 365464562Sgshapiro { 365594334Sgshapiro off_t prevsize; 365664562Sgshapiro char dfname[MAXPATHLEN]; 365764562Sgshapiro 365890792Sgshapiro (void) sm_strlcpy(dfname, queuename(e, DATAFL_LETTER), 3659168515Sgshapiro sizeof(dfname)); 366064562Sgshapiro 366164562Sgshapiro /* Reset prevchar */ 366264562Sgshapiro prevchar = '\0'; 366364562Sgshapiro 366490792Sgshapiro /* Get the current data file information */ 366594334Sgshapiro prevsize = sm_io_getinfo(e->e_dfp, SM_IO_WHAT_SIZE, NULL); 366694334Sgshapiro if (prevsize < 0) 366794334Sgshapiro prevsize = 0; 366864562Sgshapiro 366990792Sgshapiro /* truncate current data file */ 367090792Sgshapiro if (sm_io_getinfo(e->e_dfp, SM_IO_WHAT_ISTYPE, BF_FILE_TYPE)) 367164562Sgshapiro { 367290792Sgshapiro if (sm_io_setinfo(e->e_dfp, SM_BF_TRUNCATE, NULL) < 0) 367390792Sgshapiro { 367490792Sgshapiro MILTER_DF_ERROR("milter_replbody: sm_io truncate %s: %s"); 367590792Sgshapiro return -1; 367690792Sgshapiro } 367764562Sgshapiro } 367864562Sgshapiro else 367964562Sgshapiro { 368090792Sgshapiro int err; 368190792Sgshapiro 368290792Sgshapiro err = sm_io_error(e->e_dfp); 368390792Sgshapiro (void) sm_io_flush(e->e_dfp, SM_TIME_DEFAULT); 368490792Sgshapiro 368590792Sgshapiro /* 368690792Sgshapiro ** Clear error if tried to fflush() 368790792Sgshapiro ** a read-only file pointer and 368890792Sgshapiro ** there wasn't a previous error. 368990792Sgshapiro */ 369090792Sgshapiro 369190792Sgshapiro if (err == 0) 369290792Sgshapiro sm_io_clearerr(e->e_dfp); 369390792Sgshapiro 369490792Sgshapiro /* errno is set implicitly by fseek() before return */ 369590792Sgshapiro err = sm_io_seek(e->e_dfp, SM_TIME_DEFAULT, 369690792Sgshapiro 0, SEEK_SET); 369795154Sgshapiro if (err < 0) 369895154Sgshapiro { 369995154Sgshapiro MILTER_DF_ERROR("milter_replbody: sm_io_seek %s: %s"); 370095154Sgshapiro return -1; 370195154Sgshapiro } 370295154Sgshapiro# if NOFTRUNCATE 370395154Sgshapiro /* XXX: Not much we can do except rewind it */ 370495154Sgshapiro errno = EINVAL; 370595154Sgshapiro MILTER_DF_ERROR("milter_replbody: ftruncate not available on this platform (%s:%s)"); 370695154Sgshapiro return -1; 370790792Sgshapiro# else /* NOFTRUNCATE */ 370890792Sgshapiro err = ftruncate(sm_io_getinfo(e->e_dfp, 370990792Sgshapiro SM_IO_WHAT_FD, NULL), 371090792Sgshapiro 0); 371190792Sgshapiro if (err < 0) 371290792Sgshapiro { 371390792Sgshapiro MILTER_DF_ERROR("milter_replbody: sm_io ftruncate %s: %s"); 371490792Sgshapiro return -1; 371590792Sgshapiro } 371695154Sgshapiro# endif /* NOFTRUNCATE */ 371764562Sgshapiro } 371890792Sgshapiro 371990792Sgshapiro if (prevsize > e->e_msgsize) 372090792Sgshapiro e->e_msgsize = 0; 372190792Sgshapiro else 372290792Sgshapiro e->e_msgsize -= prevsize; 372364562Sgshapiro } 372464562Sgshapiro 372590792Sgshapiro if (newfilter && MilterLogLevel > 8) 372690792Sgshapiro sm_syslog(LOG_INFO, e->e_id, "Milter message: body replaced"); 372790792Sgshapiro 372864562Sgshapiro if (response == NULL) 372964562Sgshapiro { 373064562Sgshapiro /* Flush the buffered '\r' */ 373164562Sgshapiro if (prevchar == '\r') 373264562Sgshapiro { 373390792Sgshapiro (void) sm_io_putc(e->e_dfp, SM_TIME_DEFAULT, prevchar); 373464562Sgshapiro e->e_msgsize++; 373564562Sgshapiro } 373664562Sgshapiro return 0; 373764562Sgshapiro } 373864562Sgshapiro 373964562Sgshapiro for (i = 0; i < rlen; i++) 374064562Sgshapiro { 374164562Sgshapiro /* Buffered char from last chunk */ 374264562Sgshapiro if (i == 0 && prevchar == '\r') 374364562Sgshapiro { 374464562Sgshapiro /* Not CRLF, output prevchar */ 374564562Sgshapiro if (response[i] != '\n') 374664562Sgshapiro { 374790792Sgshapiro (void) sm_io_putc(e->e_dfp, SM_TIME_DEFAULT, 374890792Sgshapiro prevchar); 374964562Sgshapiro e->e_msgsize++; 375064562Sgshapiro } 375164562Sgshapiro prevchar = '\0'; 375264562Sgshapiro } 375364562Sgshapiro 375464562Sgshapiro /* Turn CRLF into LF */ 375564562Sgshapiro if (response[i] == '\r') 375664562Sgshapiro { 375764562Sgshapiro /* check if at end of chunk */ 375864562Sgshapiro if (i + 1 < rlen) 375964562Sgshapiro { 376064562Sgshapiro /* If LF, strip CR */ 376164562Sgshapiro if (response[i + 1] == '\n') 376264562Sgshapiro i++; 376364562Sgshapiro } 376464562Sgshapiro else 376564562Sgshapiro { 376664562Sgshapiro /* check next chunk */ 376764562Sgshapiro prevchar = '\r'; 376864562Sgshapiro continue; 376964562Sgshapiro } 377064562Sgshapiro } 377190792Sgshapiro (void) sm_io_putc(e->e_dfp, SM_TIME_DEFAULT, response[i]); 377264562Sgshapiro e->e_msgsize++; 377364562Sgshapiro } 377464562Sgshapiro return 0; 377564562Sgshapiro} 377664562Sgshapiro 377764562Sgshapiro/* 377864562Sgshapiro** MTA callouts 377964562Sgshapiro*/ 378064562Sgshapiro 378190792Sgshapiro/* 378264562Sgshapiro** MILTER_INIT -- open and negotiate with all of the filters 378364562Sgshapiro** 378464562Sgshapiro** Parameters: 378564562Sgshapiro** e -- current envelope. 378664562Sgshapiro** state -- return state from response. 3787173340Sgshapiro** milters -- milters structure. 378864562Sgshapiro** 378964562Sgshapiro** Returns: 379090792Sgshapiro** true iff at least one filter is active 379164562Sgshapiro*/ 379264562Sgshapiro 379364562Sgshapiro/* ARGSUSED */ 379490792Sgshapirobool 3795173340Sgshapiromilter_init(e, state, milters) 379664562Sgshapiro ENVELOPE *e; 379764562Sgshapiro char *state; 3798173340Sgshapiro milters_T *milters; 379964562Sgshapiro{ 380064562Sgshapiro int i; 380164562Sgshapiro 380264562Sgshapiro if (tTd(64, 10)) 380390792Sgshapiro sm_dprintf("milter_init\n"); 380464562Sgshapiro 3805173340Sgshapiro memset(milters, '\0', sizeof(*milters)); 380664562Sgshapiro *state = SMFIR_CONTINUE; 380790792Sgshapiro if (InputFilters[0] == NULL) 380890792Sgshapiro { 380990792Sgshapiro if (MilterLogLevel > 10) 381090792Sgshapiro sm_syslog(LOG_INFO, e->e_id, 381190792Sgshapiro "Milter: no active filter"); 381290792Sgshapiro return false; 381390792Sgshapiro } 381490792Sgshapiro 381564562Sgshapiro for (i = 0; InputFilters[i] != NULL; i++) 381664562Sgshapiro { 381764562Sgshapiro struct milter *m = InputFilters[i]; 381864562Sgshapiro 381990792Sgshapiro m->mf_sock = milter_open(m, false, e); 382064562Sgshapiro if (m->mf_state == SMFS_ERROR) 382164562Sgshapiro { 3822112810Sgshapiro MILTER_CHECK_ERROR(true, continue); 382364562Sgshapiro break; 382464562Sgshapiro } 382564562Sgshapiro 382664562Sgshapiro if (m->mf_sock < 0 || 3827173340Sgshapiro milter_negotiate(m, e, milters) < 0 || 382864562Sgshapiro m->mf_state == SMFS_ERROR) 382964562Sgshapiro { 383064562Sgshapiro if (tTd(64, 5)) 383190792Sgshapiro sm_dprintf("milter_init(%s): failed to %s\n", 383290792Sgshapiro m->mf_name, 383390792Sgshapiro m->mf_sock < 0 ? "open" : 383490792Sgshapiro "negotiate"); 383590792Sgshapiro if (MilterLogLevel > 0) 383690792Sgshapiro sm_syslog(LOG_ERR, e->e_id, 383790792Sgshapiro "Milter (%s): init failed to %s", 383890792Sgshapiro m->mf_name, 383990792Sgshapiro m->mf_sock < 0 ? "open" : 384090792Sgshapiro "negotiate"); 384164562Sgshapiro 3842203004Sgshapiro /* if negotiation failure, close socket */ 384390792Sgshapiro milter_error(m, e); 3844112810Sgshapiro MILTER_CHECK_ERROR(true, continue); 3845132943Sgshapiro continue; 384664562Sgshapiro } 384790792Sgshapiro if (MilterLogLevel > 9) 384890792Sgshapiro sm_syslog(LOG_INFO, e->e_id, 384990792Sgshapiro "Milter (%s): init success to %s", 385090792Sgshapiro m->mf_name, 385190792Sgshapiro m->mf_sock < 0 ? "open" : "negotiate"); 385264562Sgshapiro } 385364562Sgshapiro 385464562Sgshapiro /* 385564562Sgshapiro ** If something temp/perm failed with one of the filters, 385664562Sgshapiro ** we won't be using any of them, so clear any existing 385764562Sgshapiro ** connections. 385864562Sgshapiro */ 385964562Sgshapiro 386064562Sgshapiro if (*state != SMFIR_CONTINUE) 386164562Sgshapiro milter_quit(e); 386290792Sgshapiro 386390792Sgshapiro return true; 386464562Sgshapiro} 3865168515Sgshapiro 386690792Sgshapiro/* 386764562Sgshapiro** MILTER_CONNECT -- send connection info to milter filters 386864562Sgshapiro** 386964562Sgshapiro** Parameters: 387064562Sgshapiro** hostname -- hostname of remote machine. 387164562Sgshapiro** addr -- address of remote machine. 387264562Sgshapiro** e -- current envelope. 387364562Sgshapiro** state -- return state from response. 387464562Sgshapiro** 387564562Sgshapiro** Returns: 387664562Sgshapiro** response string (may be NULL) 387764562Sgshapiro*/ 387864562Sgshapiro 387964562Sgshapirochar * 388064562Sgshapiromilter_connect(hostname, addr, e, state) 388164562Sgshapiro char *hostname; 388264562Sgshapiro SOCKADDR addr; 388364562Sgshapiro ENVELOPE *e; 388464562Sgshapiro char *state; 388564562Sgshapiro{ 388664562Sgshapiro char family; 388790792Sgshapiro unsigned short port; 388864562Sgshapiro char *buf, *bp; 388964562Sgshapiro char *response; 389064562Sgshapiro char *sockinfo = NULL; 389164562Sgshapiro ssize_t s; 389264562Sgshapiro# if NETINET6 389364562Sgshapiro char buf6[INET6_ADDRSTRLEN]; 389464562Sgshapiro# endif /* NETINET6 */ 389564562Sgshapiro 389664562Sgshapiro if (tTd(64, 10)) 389790792Sgshapiro sm_dprintf("milter_connect(%s)\n", hostname); 389890792Sgshapiro if (MilterLogLevel > 9) 389990792Sgshapiro sm_syslog(LOG_INFO, e->e_id, "Milter: connect to filters"); 390064562Sgshapiro 390164562Sgshapiro /* gather data */ 390264562Sgshapiro switch (addr.sa.sa_family) 390364562Sgshapiro { 390464562Sgshapiro# if NETUNIX 390564562Sgshapiro case AF_UNIX: 390664562Sgshapiro family = SMFIA_UNIX; 390764562Sgshapiro port = htons(0); 390864562Sgshapiro sockinfo = addr.sunix.sun_path; 390964562Sgshapiro break; 391064562Sgshapiro# endif /* NETUNIX */ 391164562Sgshapiro 391264562Sgshapiro# if NETINET 391364562Sgshapiro case AF_INET: 391464562Sgshapiro family = SMFIA_INET; 391594334Sgshapiro port = addr.sin.sin_port; 391664562Sgshapiro sockinfo = (char *) inet_ntoa(addr.sin.sin_addr); 391764562Sgshapiro break; 391864562Sgshapiro# endif /* NETINET */ 391964562Sgshapiro 392064562Sgshapiro# if NETINET6 392164562Sgshapiro case AF_INET6: 392280785Sgshapiro if (IN6_IS_ADDR_V4MAPPED(&addr.sin6.sin6_addr)) 392380785Sgshapiro family = SMFIA_INET; 392480785Sgshapiro else 392580785Sgshapiro family = SMFIA_INET6; 392694334Sgshapiro port = addr.sin6.sin6_port; 392764562Sgshapiro sockinfo = anynet_ntop(&addr.sin6.sin6_addr, buf6, 3928168515Sgshapiro sizeof(buf6)); 392964562Sgshapiro if (sockinfo == NULL) 393064562Sgshapiro sockinfo = ""; 393164562Sgshapiro break; 393264562Sgshapiro# endif /* NETINET6 */ 393364562Sgshapiro 393464562Sgshapiro default: 393564562Sgshapiro family = SMFIA_UNKNOWN; 393664562Sgshapiro break; 393764562Sgshapiro } 393864562Sgshapiro 393964562Sgshapiro s = strlen(hostname) + 1 + sizeof(family); 394064562Sgshapiro if (family != SMFIA_UNKNOWN) 394164562Sgshapiro s += sizeof(port) + strlen(sockinfo) + 1; 394264562Sgshapiro 394390792Sgshapiro buf = (char *) xalloc(s); 394464562Sgshapiro bp = buf; 394564562Sgshapiro 394664562Sgshapiro /* put together data */ 394764562Sgshapiro (void) memcpy(bp, hostname, strlen(hostname)); 394864562Sgshapiro bp += strlen(hostname); 394964562Sgshapiro *bp++ = '\0'; 3950168515Sgshapiro (void) memcpy(bp, &family, sizeof(family)); 3951168515Sgshapiro bp += sizeof(family); 395264562Sgshapiro if (family != SMFIA_UNKNOWN) 395364562Sgshapiro { 3954168515Sgshapiro (void) memcpy(bp, &port, sizeof(port)); 3955168515Sgshapiro bp += sizeof(port); 395664562Sgshapiro 395764562Sgshapiro /* include trailing '\0' */ 395864562Sgshapiro (void) memcpy(bp, sockinfo, strlen(sockinfo) + 1); 395964562Sgshapiro } 396064562Sgshapiro 3961244928Sgshapiro response = milter_command(SMFIC_CONNECT, buf, s, SMFIM_CONNECT, 3962168515Sgshapiro e, state, "connect", false); 396390792Sgshapiro sm_free(buf); /* XXX */ 396464562Sgshapiro 396564562Sgshapiro /* 396664562Sgshapiro ** If this message connection is done for, 396764562Sgshapiro ** close the filters. 396864562Sgshapiro */ 396964562Sgshapiro 397064562Sgshapiro if (*state != SMFIR_CONTINUE) 397190792Sgshapiro { 397290792Sgshapiro if (MilterLogLevel > 9) 397390792Sgshapiro sm_syslog(LOG_INFO, e->e_id, "Milter: connect, ending"); 397464562Sgshapiro milter_quit(e); 397590792Sgshapiro } 397664562Sgshapiro else 397764562Sgshapiro milter_per_connection_check(e); 397864562Sgshapiro 397964562Sgshapiro /* 398064562Sgshapiro ** SMFIR_REPLYCODE can't work with connect due to 398164562Sgshapiro ** the requirements of SMTP. Therefore, ignore the 398264562Sgshapiro ** reply code text but keep the state it would reflect. 398364562Sgshapiro */ 398464562Sgshapiro 398564562Sgshapiro if (*state == SMFIR_REPLYCODE) 398664562Sgshapiro { 398764562Sgshapiro if (response != NULL && 398864562Sgshapiro *response == '4') 3989110560Sgshapiro { 3990110560Sgshapiro if (strncmp(response, "421 ", 4) == 0) 3991110560Sgshapiro *state = SMFIR_SHUTDOWN; 3992110560Sgshapiro else 3993110560Sgshapiro *state = SMFIR_TEMPFAIL; 3994110560Sgshapiro } 399564562Sgshapiro else 399664562Sgshapiro *state = SMFIR_REJECT; 399764562Sgshapiro if (response != NULL) 399864562Sgshapiro { 399990792Sgshapiro sm_free(response); /* XXX */ 400064562Sgshapiro response = NULL; 400164562Sgshapiro } 400264562Sgshapiro } 400364562Sgshapiro return response; 400464562Sgshapiro} 4005168515Sgshapiro 400690792Sgshapiro/* 400764562Sgshapiro** MILTER_HELO -- send SMTP HELO/EHLO command info to milter filters 400864562Sgshapiro** 400964562Sgshapiro** Parameters: 401064562Sgshapiro** helo -- argument to SMTP HELO/EHLO command. 401164562Sgshapiro** e -- current envelope. 401264562Sgshapiro** state -- return state from response. 401364562Sgshapiro** 401464562Sgshapiro** Returns: 401564562Sgshapiro** response string (may be NULL) 401664562Sgshapiro*/ 401764562Sgshapiro 401864562Sgshapirochar * 401964562Sgshapiromilter_helo(helo, e, state) 402064562Sgshapiro char *helo; 402164562Sgshapiro ENVELOPE *e; 402264562Sgshapiro char *state; 402364562Sgshapiro{ 402473188Sgshapiro int i; 402564562Sgshapiro char *response; 402664562Sgshapiro 402764562Sgshapiro if (tTd(64, 10)) 402890792Sgshapiro sm_dprintf("milter_helo(%s)\n", helo); 402964562Sgshapiro 403090792Sgshapiro /* HELO/EHLO can come at any point */ 403173188Sgshapiro for (i = 0; InputFilters[i] != NULL; i++) 403273188Sgshapiro { 403373188Sgshapiro struct milter *m = InputFilters[i]; 403473188Sgshapiro 403573188Sgshapiro switch (m->mf_state) 403673188Sgshapiro { 403773188Sgshapiro case SMFS_INMSG: 403873188Sgshapiro /* abort in message filters */ 403973188Sgshapiro milter_abort_filter(m, e); 404073188Sgshapiro /* FALLTHROUGH */ 404173188Sgshapiro 404273188Sgshapiro case SMFS_DONE: 404373188Sgshapiro /* reset done filters */ 404473188Sgshapiro m->mf_state = SMFS_OPEN; 404573188Sgshapiro break; 404673188Sgshapiro } 404773188Sgshapiro } 404873188Sgshapiro 404964562Sgshapiro response = milter_command(SMFIC_HELO, helo, strlen(helo) + 1, 4050247141Sgshapiro SMFIM_HELO, e, state, "helo", false); 405164562Sgshapiro milter_per_connection_check(e); 405264562Sgshapiro return response; 405364562Sgshapiro} 4054168515Sgshapiro 405590792Sgshapiro/* 405664562Sgshapiro** MILTER_ENVFROM -- send SMTP MAIL command info to milter filters 405764562Sgshapiro** 405864562Sgshapiro** Parameters: 405964562Sgshapiro** args -- SMTP MAIL command args (args[0] == sender). 406064562Sgshapiro** e -- current envelope. 406164562Sgshapiro** state -- return state from response. 406264562Sgshapiro** 406364562Sgshapiro** Returns: 406464562Sgshapiro** response string (may be NULL) 406564562Sgshapiro*/ 406664562Sgshapiro 406764562Sgshapirochar * 406864562Sgshapiromilter_envfrom(args, e, state) 406964562Sgshapiro char **args; 407064562Sgshapiro ENVELOPE *e; 407164562Sgshapiro char *state; 407264562Sgshapiro{ 407364562Sgshapiro int i; 407464562Sgshapiro char *buf, *bp; 407564562Sgshapiro char *response; 407664562Sgshapiro ssize_t s; 407764562Sgshapiro 407864562Sgshapiro if (tTd(64, 10)) 407964562Sgshapiro { 408090792Sgshapiro sm_dprintf("milter_envfrom:"); 408164562Sgshapiro for (i = 0; args[i] != NULL; i++) 408290792Sgshapiro sm_dprintf(" %s", args[i]); 408390792Sgshapiro sm_dprintf("\n"); 408464562Sgshapiro } 408564562Sgshapiro 408664562Sgshapiro /* sanity check */ 408764562Sgshapiro if (args[0] == NULL) 408864562Sgshapiro { 408964562Sgshapiro *state = SMFIR_REJECT; 409090792Sgshapiro if (MilterLogLevel > 10) 409190792Sgshapiro sm_syslog(LOG_INFO, e->e_id, 409290792Sgshapiro "Milter: reject, no sender"); 409364562Sgshapiro return NULL; 409464562Sgshapiro } 409564562Sgshapiro 409664562Sgshapiro /* new message, so ... */ 409764562Sgshapiro for (i = 0; InputFilters[i] != NULL; i++) 409864562Sgshapiro { 409964562Sgshapiro struct milter *m = InputFilters[i]; 410064562Sgshapiro 410164562Sgshapiro switch (m->mf_state) 410264562Sgshapiro { 410364562Sgshapiro case SMFS_INMSG: 410464562Sgshapiro /* abort in message filters */ 410564562Sgshapiro milter_abort_filter(m, e); 410664562Sgshapiro /* FALLTHROUGH */ 410764562Sgshapiro 410864562Sgshapiro case SMFS_DONE: 410964562Sgshapiro /* reset done filters */ 411064562Sgshapiro m->mf_state = SMFS_OPEN; 411164562Sgshapiro break; 411264562Sgshapiro } 411364562Sgshapiro } 411464562Sgshapiro 411564562Sgshapiro /* put together data */ 411664562Sgshapiro s = 0; 411764562Sgshapiro for (i = 0; args[i] != NULL; i++) 411864562Sgshapiro s += strlen(args[i]) + 1; 411990792Sgshapiro 412090792Sgshapiro if (s < 0) 412190792Sgshapiro { 412290792Sgshapiro *state = SMFIR_TEMPFAIL; 412390792Sgshapiro return NULL; 412490792Sgshapiro } 412590792Sgshapiro 412690792Sgshapiro buf = (char *) xalloc(s); 412764562Sgshapiro bp = buf; 412864562Sgshapiro for (i = 0; args[i] != NULL; i++) 412964562Sgshapiro { 413090792Sgshapiro (void) sm_strlcpy(bp, args[i], s - (bp - buf)); 413164562Sgshapiro bp += strlen(bp) + 1; 413264562Sgshapiro } 413364562Sgshapiro 413490792Sgshapiro if (MilterLogLevel > 14) 4135168515Sgshapiro sm_syslog(LOG_INFO, e->e_id, "Milter: sender: %s", buf); 413690792Sgshapiro 413764562Sgshapiro /* send it over */ 4138244928Sgshapiro response = milter_command(SMFIC_MAIL, buf, s, SMFIM_ENVFROM, 4139168515Sgshapiro e, state, "mail", false); 414090792Sgshapiro sm_free(buf); /* XXX */ 414164562Sgshapiro 414264562Sgshapiro /* 414364562Sgshapiro ** If filter rejects/discards a per message command, 414464562Sgshapiro ** abort the other filters since we are done with the 414564562Sgshapiro ** current message. 414664562Sgshapiro */ 414764562Sgshapiro 414864562Sgshapiro MILTER_CHECK_DONE_MSG(); 414990792Sgshapiro if (MilterLogLevel > 10 && *state == SMFIR_REJECT) 4150168515Sgshapiro sm_syslog(LOG_INFO, e->e_id, "Milter: reject, sender"); 415164562Sgshapiro return response; 415264562Sgshapiro} 4153132943Sgshapiro 415490792Sgshapiro/* 415564562Sgshapiro** MILTER_ENVRCPT -- send SMTP RCPT command info to milter filters 415664562Sgshapiro** 415764562Sgshapiro** Parameters: 415864562Sgshapiro** args -- SMTP MAIL command args (args[0] == recipient). 415964562Sgshapiro** e -- current envelope. 416064562Sgshapiro** state -- return state from response. 4161168515Sgshapiro** rcpt_error -- does RCPT have an error? 416264562Sgshapiro** 416364562Sgshapiro** Returns: 416464562Sgshapiro** response string (may be NULL) 416564562Sgshapiro*/ 416664562Sgshapiro 416764562Sgshapirochar * 4168168515Sgshapiromilter_envrcpt(args, e, state, rcpt_error) 416964562Sgshapiro char **args; 417064562Sgshapiro ENVELOPE *e; 417164562Sgshapiro char *state; 4172168515Sgshapiro bool rcpt_error; 417364562Sgshapiro{ 417464562Sgshapiro int i; 417564562Sgshapiro char *buf, *bp; 417664562Sgshapiro char *response; 417764562Sgshapiro ssize_t s; 417864562Sgshapiro 417964562Sgshapiro if (tTd(64, 10)) 418064562Sgshapiro { 418190792Sgshapiro sm_dprintf("milter_envrcpt:"); 418264562Sgshapiro for (i = 0; args[i] != NULL; i++) 418390792Sgshapiro sm_dprintf(" %s", args[i]); 418490792Sgshapiro sm_dprintf("\n"); 418564562Sgshapiro } 418664562Sgshapiro 418764562Sgshapiro /* sanity check */ 418864562Sgshapiro if (args[0] == NULL) 418964562Sgshapiro { 419064562Sgshapiro *state = SMFIR_REJECT; 419190792Sgshapiro if (MilterLogLevel > 10) 419290792Sgshapiro sm_syslog(LOG_INFO, e->e_id, "Milter: reject, no rcpt"); 419364562Sgshapiro return NULL; 419464562Sgshapiro } 419564562Sgshapiro 419664562Sgshapiro /* put together data */ 419764562Sgshapiro s = 0; 419864562Sgshapiro for (i = 0; args[i] != NULL; i++) 419964562Sgshapiro s += strlen(args[i]) + 1; 420090792Sgshapiro 420190792Sgshapiro if (s < 0) 420290792Sgshapiro { 420390792Sgshapiro *state = SMFIR_TEMPFAIL; 420490792Sgshapiro return NULL; 420590792Sgshapiro } 420690792Sgshapiro 420790792Sgshapiro buf = (char *) xalloc(s); 420864562Sgshapiro bp = buf; 420964562Sgshapiro for (i = 0; args[i] != NULL; i++) 421064562Sgshapiro { 421190792Sgshapiro (void) sm_strlcpy(bp, args[i], s - (bp - buf)); 421264562Sgshapiro bp += strlen(bp) + 1; 421364562Sgshapiro } 421464562Sgshapiro 421590792Sgshapiro if (MilterLogLevel > 14) 421690792Sgshapiro sm_syslog(LOG_INFO, e->e_id, "Milter: rcpts: %s", buf); 421790792Sgshapiro 421864562Sgshapiro /* send it over */ 4219244928Sgshapiro response = milter_command(SMFIC_RCPT, buf, s, SMFIM_ENVRCPT, 4220168515Sgshapiro e, state, "rcpt", rcpt_error); 422190792Sgshapiro sm_free(buf); /* XXX */ 422264562Sgshapiro return response; 422364562Sgshapiro} 4224132943Sgshapiro 422590792Sgshapiro/* 4226132943Sgshapiro** MILTER_DATA_CMD -- send SMTP DATA command info to milter filters 4227132943Sgshapiro** 4228132943Sgshapiro** Parameters: 4229132943Sgshapiro** e -- current envelope. 4230132943Sgshapiro** state -- return state from response. 4231132943Sgshapiro** 4232132943Sgshapiro** Returns: 4233132943Sgshapiro** response string (may be NULL) 4234132943Sgshapiro*/ 4235132943Sgshapiro 4236132943Sgshapirochar * 4237132943Sgshapiromilter_data_cmd(e, state) 4238132943Sgshapiro ENVELOPE *e; 4239132943Sgshapiro char *state; 4240132943Sgshapiro{ 4241132943Sgshapiro if (tTd(64, 10)) 4242132943Sgshapiro sm_dprintf("milter_data_cmd\n"); 4243132943Sgshapiro 4244132943Sgshapiro /* send it over */ 4245244928Sgshapiro return milter_command(SMFIC_DATA, NULL, 0, SMFIM_DATA, 4246244928Sgshapiro e, state, "data", false); 4247132943Sgshapiro} 4248132943Sgshapiro 4249132943Sgshapiro/* 425064562Sgshapiro** MILTER_DATA -- send message headers/body and gather final message results 425164562Sgshapiro** 425264562Sgshapiro** Parameters: 425364562Sgshapiro** e -- current envelope. 425464562Sgshapiro** state -- return state from response. 425564562Sgshapiro** 425664562Sgshapiro** Returns: 425764562Sgshapiro** response string (may be NULL) 425864562Sgshapiro** 425964562Sgshapiro** Side effects: 426064562Sgshapiro** - Uses e->e_dfp for access to the body 426164562Sgshapiro** - Can call the various milter action routines to 426264562Sgshapiro** modify the envelope or message. 426364562Sgshapiro*/ 426464562Sgshapiro 4265244928Sgshapiro/* flow through code using continue; don't wrap in do {} while */ 426664562Sgshapiro# define MILTER_CHECK_RESULTS() \ 4267244928Sgshapiro if (m->mf_state == SMFS_ERROR && *state == SMFIR_CONTINUE) \ 4268244928Sgshapiro { \ 4269244928Sgshapiro MILTER_SET_STATE; \ 4270244928Sgshapiro } \ 427164562Sgshapiro if (*state == SMFIR_ACCEPT || \ 427264562Sgshapiro m->mf_state == SMFS_DONE || \ 427364562Sgshapiro m->mf_state == SMFS_ERROR) \ 427464562Sgshapiro { \ 427564562Sgshapiro if (m->mf_state != SMFS_ERROR) \ 427664562Sgshapiro m->mf_state = SMFS_DONE; \ 427764562Sgshapiro continue; /* to next filter */ \ 427864562Sgshapiro } \ 427964562Sgshapiro if (*state != SMFIR_CONTINUE) \ 428064562Sgshapiro { \ 428164562Sgshapiro m->mf_state = SMFS_DONE; \ 428264562Sgshapiro goto finishup; \ 428364562Sgshapiro } 428464562Sgshapiro 428564562Sgshapirochar * 428664562Sgshapiromilter_data(e, state) 428764562Sgshapiro ENVELOPE *e; 428864562Sgshapiro char *state; 428964562Sgshapiro{ 429090792Sgshapiro bool replbody = false; /* milter_replbody() called? */ 429190792Sgshapiro bool replfailed = false; /* milter_replbody() failed? */ 429290792Sgshapiro bool rewind = false; /* rewind data file? */ 429390792Sgshapiro bool dfopen = false; /* data file open for writing? */ 429464562Sgshapiro bool newfilter; /* reset on each new filter */ 429564562Sgshapiro char rcmd; 429664562Sgshapiro int i; 429764562Sgshapiro int save_errno; 429864562Sgshapiro char *response = NULL; 429964562Sgshapiro time_t eomsent; 430064562Sgshapiro ssize_t rlen; 430164562Sgshapiro 430264562Sgshapiro if (tTd(64, 10)) 430390792Sgshapiro sm_dprintf("milter_data\n"); 430464562Sgshapiro 430564562Sgshapiro *state = SMFIR_CONTINUE; 430664562Sgshapiro 430764562Sgshapiro /* 430864562Sgshapiro ** XXX: Should actually send body chunks to each filter 430964562Sgshapiro ** a chunk at a time instead of sending the whole body to 431064562Sgshapiro ** each filter in turn. However, only if the filters don't 431164562Sgshapiro ** change the body. 431264562Sgshapiro */ 431364562Sgshapiro 431464562Sgshapiro for (i = 0; InputFilters[i] != NULL; i++) 431564562Sgshapiro { 4316244928Sgshapiro int idx; 4317244928Sgshapiro char **macros; 431864562Sgshapiro struct milter *m = InputFilters[i]; 431964562Sgshapiro 432064562Sgshapiro if (*state != SMFIR_CONTINUE && 432164562Sgshapiro *state != SMFIR_ACCEPT) 432264562Sgshapiro { 432364562Sgshapiro /* 432464562Sgshapiro ** A previous filter has dealt with the message, 432564562Sgshapiro ** safe to stop processing the filters. 432664562Sgshapiro */ 432764562Sgshapiro 432864562Sgshapiro break; 432964562Sgshapiro } 433064562Sgshapiro 433164562Sgshapiro /* Now reset state for later evaluation */ 433264562Sgshapiro *state = SMFIR_CONTINUE; 433390792Sgshapiro newfilter = true; 433464562Sgshapiro 433571345Sgshapiro /* previous problem? */ 433671345Sgshapiro if (m->mf_state == SMFS_ERROR) 433771345Sgshapiro { 4338112810Sgshapiro MILTER_CHECK_ERROR(false, continue); 433971345Sgshapiro break; 434071345Sgshapiro } 434171345Sgshapiro 434264562Sgshapiro /* sanity checks */ 434364562Sgshapiro if (m->mf_sock < 0 || 434464562Sgshapiro (m->mf_state != SMFS_OPEN && m->mf_state != SMFS_INMSG)) 434564562Sgshapiro continue; 434664562Sgshapiro 434764562Sgshapiro m->mf_state = SMFS_INMSG; 434864562Sgshapiro 434964562Sgshapiro /* check if filter wants the headers */ 435064562Sgshapiro if (!bitset(SMFIP_NOHDRS, m->mf_pflags)) 435164562Sgshapiro { 435264562Sgshapiro response = milter_headers(m, e, state); 435364562Sgshapiro MILTER_CHECK_RESULTS(); 435464562Sgshapiro } 435564562Sgshapiro 435664562Sgshapiro /* check if filter wants EOH */ 435764562Sgshapiro if (!bitset(SMFIP_NOEOH, m->mf_pflags)) 435864562Sgshapiro { 435964562Sgshapiro if (tTd(64, 10)) 436090792Sgshapiro sm_dprintf("milter_data: eoh\n"); 436164562Sgshapiro 4362244928Sgshapiro if ((m->mf_lflags & MI_LFLAGS_SYM(SMFIM_EOH)) != 0) 4363244928Sgshapiro idx = m->mf_idx; 4364244928Sgshapiro else 4365244928Sgshapiro idx = 0; 4366244928Sgshapiro SM_ASSERT(idx >= 0 && idx <= MAXFILTERS); 4367244928Sgshapiro macros = MilterMacros[SMFIM_EOH][idx]; 4368244928Sgshapiro 4369244928Sgshapiro if (macros != NULL) 4370168515Sgshapiro { 4371244928Sgshapiro milter_send_macros(m, macros, SMFIC_EOH, e); 4372168515Sgshapiro MILTER_CHECK_RESULTS(); 4373168515Sgshapiro } 4374168515Sgshapiro 437564562Sgshapiro /* send it over */ 437664562Sgshapiro response = milter_send_command(m, SMFIC_EOH, NULL, 0, 4377168515Sgshapiro e, state, "eoh"); 437864562Sgshapiro MILTER_CHECK_RESULTS(); 437964562Sgshapiro } 438064562Sgshapiro 438164562Sgshapiro /* check if filter wants the body */ 438264562Sgshapiro if (!bitset(SMFIP_NOBODY, m->mf_pflags) && 438364562Sgshapiro e->e_dfp != NULL) 438464562Sgshapiro { 438590792Sgshapiro rewind = true; 438664562Sgshapiro response = milter_body(m, e, state); 438764562Sgshapiro MILTER_CHECK_RESULTS(); 438864562Sgshapiro } 438964562Sgshapiro 4390244928Sgshapiro if ((m->mf_lflags & MI_LFLAGS_SYM(SMFIM_EOH)) != 0) 4391244928Sgshapiro idx = m->mf_idx; 4392244928Sgshapiro else 4393244928Sgshapiro idx = 0; 4394244928Sgshapiro SM_ASSERT(idx >= 0 && idx <= MAXFILTERS); 4395244928Sgshapiro macros = MilterMacros[SMFIM_EOM][idx]; 4396244928Sgshapiro if (macros != NULL) 4397147078Sgshapiro { 4398244928Sgshapiro milter_send_macros(m, macros, SMFIC_BODYEOB, e); 4399147078Sgshapiro MILTER_CHECK_RESULTS(); 4400147078Sgshapiro } 4401125820Sgshapiro 440264562Sgshapiro /* send the final body chunk */ 440364562Sgshapiro (void) milter_write(m, SMFIC_BODYEOB, NULL, 0, 4404168515Sgshapiro m->mf_timeout[SMFTO_WRITE], e, "eom"); 440564562Sgshapiro 440664562Sgshapiro /* Get time EOM sent for timeout */ 440764562Sgshapiro eomsent = curtime(); 440864562Sgshapiro 440964562Sgshapiro /* deal with the possibility of multiple responses */ 441064562Sgshapiro while (*state == SMFIR_CONTINUE) 441164562Sgshapiro { 441264562Sgshapiro /* Check total timeout from EOM to final ACK/NAK */ 441364562Sgshapiro if (m->mf_timeout[SMFTO_EOM] > 0 && 441464562Sgshapiro curtime() - eomsent >= m->mf_timeout[SMFTO_EOM]) 441564562Sgshapiro { 441664562Sgshapiro if (tTd(64, 5)) 441790792Sgshapiro sm_dprintf("milter_data(%s): EOM ACK/NAK timeout\n", 441864562Sgshapiro m->mf_name); 441990792Sgshapiro if (MilterLogLevel > 0) 442064562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 442190792Sgshapiro "milter_data(%s): EOM ACK/NAK timeout", 442264562Sgshapiro m->mf_name); 442390792Sgshapiro milter_error(m, e); 4424112810Sgshapiro MILTER_CHECK_ERROR(false, break); 442564562Sgshapiro break; 442664562Sgshapiro } 442764562Sgshapiro 442864562Sgshapiro response = milter_read(m, &rcmd, &rlen, 4429168515Sgshapiro m->mf_timeout[SMFTO_READ], e, 4430203004Sgshapiro "eom"); 443164562Sgshapiro if (m->mf_state == SMFS_ERROR) 443264562Sgshapiro break; 443364562Sgshapiro 443464562Sgshapiro if (tTd(64, 10)) 443590792Sgshapiro sm_dprintf("milter_data(%s): state %c\n", 443690792Sgshapiro m->mf_name, (char) rcmd); 443764562Sgshapiro 443864562Sgshapiro switch (rcmd) 443964562Sgshapiro { 444064562Sgshapiro case SMFIR_REPLYCODE: 444164562Sgshapiro MILTER_CHECK_REPLYCODE("554 5.7.1 Command rejected"); 444290792Sgshapiro if (MilterLogLevel > 12) 444390792Sgshapiro sm_syslog(LOG_INFO, e->e_id, "milter=%s, reject=%s", 444490792Sgshapiro m->mf_name, response); 444564562Sgshapiro *state = rcmd; 444664562Sgshapiro m->mf_state = SMFS_DONE; 444764562Sgshapiro break; 444864562Sgshapiro 444990792Sgshapiro case SMFIR_REJECT: /* log msg at end of function */ 445090792Sgshapiro if (MilterLogLevel > 12) 445190792Sgshapiro sm_syslog(LOG_INFO, e->e_id, "milter=%s, reject", 445290792Sgshapiro m->mf_name); 445390792Sgshapiro *state = rcmd; 445490792Sgshapiro m->mf_state = SMFS_DONE; 445590792Sgshapiro break; 445690792Sgshapiro 445764562Sgshapiro case SMFIR_DISCARD: 445890792Sgshapiro if (MilterLogLevel > 12) 445990792Sgshapiro sm_syslog(LOG_INFO, e->e_id, "milter=%s, discard", 446090792Sgshapiro m->mf_name); 446190792Sgshapiro *state = rcmd; 446290792Sgshapiro m->mf_state = SMFS_DONE; 446390792Sgshapiro break; 446490792Sgshapiro 446564562Sgshapiro case SMFIR_TEMPFAIL: 446690792Sgshapiro if (MilterLogLevel > 12) 446790792Sgshapiro sm_syslog(LOG_INFO, e->e_id, "milter=%s, tempfail", 446890792Sgshapiro m->mf_name); 446964562Sgshapiro *state = rcmd; 447064562Sgshapiro m->mf_state = SMFS_DONE; 447164562Sgshapiro break; 447264562Sgshapiro 447364562Sgshapiro case SMFIR_CONTINUE: 447464562Sgshapiro case SMFIR_ACCEPT: 447564562Sgshapiro /* this filter is done with message */ 447664562Sgshapiro if (replfailed) 447764562Sgshapiro *state = SMFIR_TEMPFAIL; 447864562Sgshapiro else 447964562Sgshapiro *state = SMFIR_ACCEPT; 448064562Sgshapiro m->mf_state = SMFS_DONE; 448164562Sgshapiro break; 448264562Sgshapiro 448364562Sgshapiro case SMFIR_PROGRESS: 448464562Sgshapiro break; 448564562Sgshapiro 448690792Sgshapiro case SMFIR_QUARANTINE: 448790792Sgshapiro if (!bitset(SMFIF_QUARANTINE, m->mf_fflags)) 448890792Sgshapiro { 448990792Sgshapiro if (MilterLogLevel > 9) 449090792Sgshapiro sm_syslog(LOG_WARNING, e->e_id, 449190792Sgshapiro "milter_data(%s): lied about quarantining, honoring request anyway", 449290792Sgshapiro m->mf_name); 449390792Sgshapiro } 449490792Sgshapiro if (response == NULL) 449590792Sgshapiro response = newstr(""); 449690792Sgshapiro if (MilterLogLevel > 3) 449790792Sgshapiro sm_syslog(LOG_INFO, e->e_id, 449890792Sgshapiro "milter=%s, quarantine=%s", 449990792Sgshapiro m->mf_name, response); 450090792Sgshapiro e->e_quarmsg = sm_rpool_strdup_x(e->e_rpool, 450190792Sgshapiro response); 450290792Sgshapiro macdefine(&e->e_macro, A_PERM, 450390792Sgshapiro macid("{quarantine}"), e->e_quarmsg); 450490792Sgshapiro break; 450590792Sgshapiro 450664562Sgshapiro case SMFIR_ADDHEADER: 450764562Sgshapiro if (!bitset(SMFIF_ADDHDRS, m->mf_fflags)) 450864562Sgshapiro { 450990792Sgshapiro if (MilterLogLevel > 9) 451064562Sgshapiro sm_syslog(LOG_WARNING, e->e_id, 451164562Sgshapiro "milter_data(%s): lied about adding headers, honoring request anyway", 451264562Sgshapiro m->mf_name); 451364562Sgshapiro } 4514168515Sgshapiro milter_addheader(m, response, rlen, e); 451564562Sgshapiro break; 451664562Sgshapiro 4517132943Sgshapiro case SMFIR_INSHEADER: 4518132943Sgshapiro if (!bitset(SMFIF_ADDHDRS, m->mf_fflags)) 4519132943Sgshapiro { 4520132943Sgshapiro if (MilterLogLevel > 9) 4521132943Sgshapiro sm_syslog(LOG_WARNING, e->e_id, 4522132943Sgshapiro "milter_data(%s): lied about adding headers, honoring request anyway", 4523132943Sgshapiro m->mf_name); 4524132943Sgshapiro } 4525168515Sgshapiro milter_insheader(m, response, rlen, e); 4526132943Sgshapiro break; 4527132943Sgshapiro 452864562Sgshapiro case SMFIR_CHGHEADER: 452964562Sgshapiro if (!bitset(SMFIF_CHGHDRS, m->mf_fflags)) 453064562Sgshapiro { 453190792Sgshapiro if (MilterLogLevel > 9) 453264562Sgshapiro sm_syslog(LOG_WARNING, e->e_id, 453364562Sgshapiro "milter_data(%s): lied about changing headers, honoring request anyway", 453464562Sgshapiro m->mf_name); 453564562Sgshapiro } 4536168515Sgshapiro milter_changeheader(m, response, rlen, e); 453764562Sgshapiro break; 453864562Sgshapiro 4539168515Sgshapiro case SMFIR_CHGFROM: 4540168515Sgshapiro if (!bitset(SMFIF_CHGFROM, m->mf_fflags)) 4541168515Sgshapiro { 4542168515Sgshapiro if (MilterLogLevel > 9) 4543168515Sgshapiro sm_syslog(LOG_WARNING, e->e_id, 4544168515Sgshapiro "milter_data(%s) lied about changing sender, honoring request anyway", 4545168515Sgshapiro m->mf_name); 4546168515Sgshapiro } 4547168515Sgshapiro milter_chgfrom(response, rlen, e); 4548168515Sgshapiro break; 4549168515Sgshapiro 455064562Sgshapiro case SMFIR_ADDRCPT: 455164562Sgshapiro if (!bitset(SMFIF_ADDRCPT, m->mf_fflags)) 455264562Sgshapiro { 455390792Sgshapiro if (MilterLogLevel > 9) 455464562Sgshapiro sm_syslog(LOG_WARNING, e->e_id, 455564562Sgshapiro "milter_data(%s) lied about adding recipients, honoring request anyway", 455664562Sgshapiro m->mf_name); 455764562Sgshapiro } 455864562Sgshapiro milter_addrcpt(response, rlen, e); 455964562Sgshapiro break; 456064562Sgshapiro 4561168515Sgshapiro case SMFIR_ADDRCPT_PAR: 4562168515Sgshapiro if (!bitset(SMFIF_ADDRCPT_PAR, m->mf_fflags)) 4563168515Sgshapiro { 4564168515Sgshapiro if (MilterLogLevel > 9) 4565168515Sgshapiro sm_syslog(LOG_WARNING, e->e_id, 4566168515Sgshapiro "milter_data(%s) lied about adding recipients with parameters, honoring request anyway", 4567168515Sgshapiro m->mf_name); 4568168515Sgshapiro } 4569168515Sgshapiro milter_addrcpt_par(response, rlen, e); 4570168515Sgshapiro break; 4571168515Sgshapiro 457264562Sgshapiro case SMFIR_DELRCPT: 457364562Sgshapiro if (!bitset(SMFIF_DELRCPT, m->mf_fflags)) 457464562Sgshapiro { 457590792Sgshapiro if (MilterLogLevel > 9) 457664562Sgshapiro sm_syslog(LOG_WARNING, e->e_id, 457764562Sgshapiro "milter_data(%s): lied about removing recipients, honoring request anyway", 457864562Sgshapiro m->mf_name); 457964562Sgshapiro } 458064562Sgshapiro milter_delrcpt(response, rlen, e); 458164562Sgshapiro break; 458264562Sgshapiro 458364562Sgshapiro case SMFIR_REPLBODY: 458464562Sgshapiro if (!bitset(SMFIF_MODBODY, m->mf_fflags)) 458564562Sgshapiro { 4586132943Sgshapiro if (MilterLogLevel > 0) 458764562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 458864562Sgshapiro "milter_data(%s): lied about replacing body, rejecting request and tempfailing message", 458964562Sgshapiro m->mf_name); 459090792Sgshapiro replfailed = true; 459164562Sgshapiro break; 459264562Sgshapiro } 459364562Sgshapiro 459464562Sgshapiro /* already failed in attempt */ 459564562Sgshapiro if (replfailed) 459664562Sgshapiro break; 459764562Sgshapiro 459864562Sgshapiro if (!dfopen) 459964562Sgshapiro { 460064562Sgshapiro if (milter_reopen_df(e) < 0) 460164562Sgshapiro { 460290792Sgshapiro replfailed = true; 460364562Sgshapiro break; 460464562Sgshapiro } 460590792Sgshapiro dfopen = true; 460690792Sgshapiro rewind = true; 460764562Sgshapiro } 460864562Sgshapiro 460964562Sgshapiro if (milter_replbody(response, rlen, 461064562Sgshapiro newfilter, e) < 0) 461190792Sgshapiro replfailed = true; 461290792Sgshapiro newfilter = false; 461390792Sgshapiro replbody = true; 461464562Sgshapiro break; 461564562Sgshapiro 461664562Sgshapiro default: 461764562Sgshapiro /* Invalid response to command */ 461890792Sgshapiro if (MilterLogLevel > 0) 461964562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 462064562Sgshapiro "milter_data(%s): returned bogus response %c", 462164562Sgshapiro m->mf_name, rcmd); 462290792Sgshapiro milter_error(m, e); 462364562Sgshapiro break; 462464562Sgshapiro } 462590792Sgshapiro if (rcmd != SMFIR_REPLYCODE && response != NULL) 462664562Sgshapiro { 462790792Sgshapiro sm_free(response); /* XXX */ 462864562Sgshapiro response = NULL; 462964562Sgshapiro } 463064562Sgshapiro 463164562Sgshapiro if (m->mf_state == SMFS_ERROR) 463264562Sgshapiro break; 463364562Sgshapiro } 463464562Sgshapiro 463564562Sgshapiro if (replbody && !replfailed) 463664562Sgshapiro { 463764562Sgshapiro /* flush possible buffered character */ 463864562Sgshapiro milter_replbody(NULL, 0, !replbody, e); 463990792Sgshapiro replbody = false; 464064562Sgshapiro } 464164562Sgshapiro 464264562Sgshapiro if (m->mf_state == SMFS_ERROR) 464364562Sgshapiro { 4644112810Sgshapiro MILTER_CHECK_ERROR(false, continue); 464564562Sgshapiro goto finishup; 464664562Sgshapiro } 464764562Sgshapiro } 464864562Sgshapiro 464964562Sgshapirofinishup: 465064562Sgshapiro /* leave things in the expected state if we touched it */ 465164562Sgshapiro if (replfailed) 465264562Sgshapiro { 465364562Sgshapiro if (*state == SMFIR_CONTINUE || 465464562Sgshapiro *state == SMFIR_ACCEPT) 465564562Sgshapiro { 465664562Sgshapiro *state = SMFIR_TEMPFAIL; 465790792Sgshapiro SM_FREE_CLR(response); 465864562Sgshapiro } 465964562Sgshapiro 466064562Sgshapiro if (dfopen) 466164562Sgshapiro { 466290792Sgshapiro (void) sm_io_close(e->e_dfp, SM_TIME_DEFAULT); 466364562Sgshapiro e->e_dfp = NULL; 466464562Sgshapiro e->e_flags &= ~EF_HAS_DF; 466590792Sgshapiro dfopen = false; 466664562Sgshapiro } 466790792Sgshapiro rewind = false; 466864562Sgshapiro } 466964562Sgshapiro 467064562Sgshapiro if ((dfopen && milter_reset_df(e) < 0) || 467164562Sgshapiro (rewind && bfrewind(e->e_dfp) < 0)) 467264562Sgshapiro { 467364562Sgshapiro save_errno = errno; 467464562Sgshapiro ExitStat = EX_IOERR; 467564562Sgshapiro 467664562Sgshapiro /* 467764562Sgshapiro ** If filter told us to keep message but we had 467864562Sgshapiro ** an error, we can't really keep it, tempfail it. 467964562Sgshapiro */ 468064562Sgshapiro 468164562Sgshapiro if (*state == SMFIR_CONTINUE || 468264562Sgshapiro *state == SMFIR_ACCEPT) 468364562Sgshapiro { 468464562Sgshapiro *state = SMFIR_TEMPFAIL; 468590792Sgshapiro SM_FREE_CLR(response); 468664562Sgshapiro } 468764562Sgshapiro 468864562Sgshapiro errno = save_errno; 468990792Sgshapiro syserr("milter_data: %s/%cf%s: read error", 469090792Sgshapiro qid_printqueue(e->e_qgrp, e->e_qdir), 469190792Sgshapiro DATAFL_LETTER, e->e_id); 469264562Sgshapiro } 469390792Sgshapiro 469464562Sgshapiro MILTER_CHECK_DONE_MSG(); 469590792Sgshapiro if (MilterLogLevel > 10 && *state == SMFIR_REJECT) 469690792Sgshapiro sm_syslog(LOG_INFO, e->e_id, "Milter: reject, data"); 469764562Sgshapiro return response; 469864562Sgshapiro} 4699132943Sgshapiro 470090792Sgshapiro/* 4701132943Sgshapiro** MILTER_UNKNOWN -- send any unrecognized or unimplemented command 4702132943Sgshapiro** string to milter filters 4703132943Sgshapiro** 4704132943Sgshapiro** Parameters: 4705168515Sgshapiro** smtpcmd -- the string itself. 4706132943Sgshapiro** e -- current envelope. 4707132943Sgshapiro** state -- return state from response. 4708132943Sgshapiro** 4709132943Sgshapiro** 4710132943Sgshapiro** Returns: 4711132943Sgshapiro** response string (may be NULL) 4712132943Sgshapiro*/ 4713132943Sgshapiro 4714132943Sgshapirochar * 4715168515Sgshapiromilter_unknown(smtpcmd, e, state) 4716168515Sgshapiro char *smtpcmd; 4717132943Sgshapiro ENVELOPE *e; 4718132943Sgshapiro char *state; 4719132943Sgshapiro{ 4720132943Sgshapiro if (tTd(64, 10)) 4721168515Sgshapiro sm_dprintf("milter_unknown(%s)\n", smtpcmd); 4722132943Sgshapiro 4723168515Sgshapiro return milter_command(SMFIC_UNKNOWN, smtpcmd, strlen(smtpcmd) + 1, 4724244928Sgshapiro SMFIM_NOMACROS, e, state, "unknown", false); 4725132943Sgshapiro} 4726132943Sgshapiro 4727132943Sgshapiro/* 472864562Sgshapiro** MILTER_QUIT -- informs the filter(s) we are done and closes connection(s) 472964562Sgshapiro** 473064562Sgshapiro** Parameters: 473164562Sgshapiro** e -- current envelope. 473264562Sgshapiro** 473364562Sgshapiro** Returns: 473464562Sgshapiro** none 473564562Sgshapiro*/ 473664562Sgshapiro 473764562Sgshapirovoid 473864562Sgshapiromilter_quit(e) 473964562Sgshapiro ENVELOPE *e; 474064562Sgshapiro{ 474164562Sgshapiro int i; 474264562Sgshapiro 474364562Sgshapiro if (tTd(64, 10)) 474490792Sgshapiro sm_dprintf("milter_quit(%s)\n", e->e_id); 474564562Sgshapiro 474664562Sgshapiro for (i = 0; InputFilters[i] != NULL; i++) 474764562Sgshapiro milter_quit_filter(InputFilters[i], e); 474864562Sgshapiro} 4749168515Sgshapiro 475090792Sgshapiro/* 475164562Sgshapiro** MILTER_ABORT -- informs the filter(s) that we are aborting current message 475264562Sgshapiro** 475364562Sgshapiro** Parameters: 475464562Sgshapiro** e -- current envelope. 475564562Sgshapiro** 475664562Sgshapiro** Returns: 475764562Sgshapiro** none 475864562Sgshapiro*/ 475964562Sgshapiro 476064562Sgshapirovoid 476164562Sgshapiromilter_abort(e) 476264562Sgshapiro ENVELOPE *e; 476364562Sgshapiro{ 476464562Sgshapiro int i; 476564562Sgshapiro 476664562Sgshapiro if (tTd(64, 10)) 476790792Sgshapiro sm_dprintf("milter_abort\n"); 476864562Sgshapiro 476964562Sgshapiro for (i = 0; InputFilters[i] != NULL; i++) 477064562Sgshapiro { 477164562Sgshapiro struct milter *m = InputFilters[i]; 477264562Sgshapiro 477364562Sgshapiro /* sanity checks */ 477464562Sgshapiro if (m->mf_sock < 0 || m->mf_state != SMFS_INMSG) 477564562Sgshapiro continue; 477664562Sgshapiro 477764562Sgshapiro milter_abort_filter(m, e); 477864562Sgshapiro } 477964562Sgshapiro} 478090792Sgshapiro#endif /* MILTER */ 4781