milter.c revision 112810
164562Sgshapiro/* 2112810Sgshapiro * Copyright (c) 1999-2003 Sendmail, 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 13112810SgshapiroSM_RCSID("@(#)$Id: milter.c,v 8.197.2.7 2003/03/22 18:54:25 ca Exp $") 1464562Sgshapiro 1590792Sgshapiro#if MILTER 1690792Sgshapiro# include <libmilter/mfapi.h> 1790792Sgshapiro# include <libmilter/mfdef.h> 1890792Sgshapiro 1964562Sgshapiro# include <errno.h> 2064562Sgshapiro# include <sys/time.h> 2164562Sgshapiro 2264562Sgshapiro# if NETINET || NETINET6 2364562Sgshapiro# include <arpa/inet.h> 2464562Sgshapiro# endif /* NETINET || NETINET6 */ 2564562Sgshapiro 2690792Sgshapiro# include <sm/fdset.h> 2764562Sgshapiro 2880785Sgshapirostatic void milter_connect_timeout __P((void)); 2990792Sgshapirostatic void milter_error __P((struct milter *, ENVELOPE *)); 3064562Sgshapirostatic int milter_open __P((struct milter *, bool, ENVELOPE *)); 3164562Sgshapirostatic void milter_parse_timeouts __P((char *, struct milter *)); 3264562Sgshapiro 3364562Sgshapirostatic char *MilterConnectMacros[MAXFILTERMACROS + 1]; 3464562Sgshapirostatic char *MilterHeloMacros[MAXFILTERMACROS + 1]; 3564562Sgshapirostatic char *MilterEnvFromMacros[MAXFILTERMACROS + 1]; 3664562Sgshapirostatic char *MilterEnvRcptMacros[MAXFILTERMACROS + 1]; 3764562Sgshapiro 3864562Sgshapiro# define MILTER_CHECK_DONE_MSG() \ 3964562Sgshapiro if (*state == SMFIR_REPLYCODE || \ 4064562Sgshapiro *state == SMFIR_REJECT || \ 4164562Sgshapiro *state == SMFIR_DISCARD || \ 4264562Sgshapiro *state == SMFIR_TEMPFAIL) \ 4364562Sgshapiro { \ 4464562Sgshapiro /* Abort the filters to let them know we are done with msg */ \ 4564562Sgshapiro milter_abort(e); \ 4664562Sgshapiro } 4764562Sgshapiro 48102528Sgshapiro# if _FFR_QUARANTINE 49112810Sgshapiro# define MILTER_CHECK_ERROR(initial, action) \ 50112810Sgshapiro if (!initial && tTd(71, 100)) \ 51102528Sgshapiro { \ 52102528Sgshapiro if (e->e_quarmsg == NULL) \ 53102528Sgshapiro { \ 54102528Sgshapiro e->e_quarmsg = sm_rpool_strdup_x(e->e_rpool, \ 55102528Sgshapiro "filter failure"); \ 56102528Sgshapiro macdefine(&e->e_macro, A_PERM, macid("{quarantine}"), \ 57102528Sgshapiro e->e_quarmsg); \ 58102528Sgshapiro } \ 59102528Sgshapiro } \ 60112810Sgshapiro else if (tTd(71, 101)) \ 61112810Sgshapiro { \ 62112810Sgshapiro if (e->e_quarmsg == NULL) \ 63112810Sgshapiro { \ 64112810Sgshapiro e->e_quarmsg = sm_rpool_strdup_x(e->e_rpool, \ 65112810Sgshapiro "filter failure"); \ 66112810Sgshapiro macdefine(&e->e_macro, A_PERM, macid("{quarantine}"), \ 67112810Sgshapiro e->e_quarmsg); \ 68112810Sgshapiro } \ 69112810Sgshapiro } \ 70102528Sgshapiro else if (bitnset(SMF_TEMPFAIL, m->mf_flags)) \ 71102528Sgshapiro *state = SMFIR_TEMPFAIL; \ 72102528Sgshapiro else if (bitnset(SMF_REJECT, m->mf_flags)) \ 73102528Sgshapiro *state = SMFIR_REJECT; \ 74102528Sgshapiro else \ 75102528Sgshapiro action; 76102528Sgshapiro# else /* _FFR_QUARANTINE */ 77112810Sgshapiro# define MILTER_CHECK_ERROR(initial, action) \ 7864562Sgshapiro if (bitnset(SMF_TEMPFAIL, m->mf_flags)) \ 7964562Sgshapiro *state = SMFIR_TEMPFAIL; \ 8064562Sgshapiro else if (bitnset(SMF_REJECT, m->mf_flags)) \ 8164562Sgshapiro *state = SMFIR_REJECT; \ 8264562Sgshapiro else \ 8364562Sgshapiro action; 84102528Sgshapiro# endif /* _FFR_QUARANTINE */ 8564562Sgshapiro 8664562Sgshapiro# define MILTER_CHECK_REPLYCODE(default) \ 8764562Sgshapiro if (response == NULL || \ 8864562Sgshapiro strlen(response) + 1 != (size_t) rlen || \ 8964562Sgshapiro rlen < 3 || \ 9064562Sgshapiro (response[0] != '4' && response[0] != '5') || \ 9164562Sgshapiro !isascii(response[1]) || !isdigit(response[1]) || \ 9264562Sgshapiro !isascii(response[2]) || !isdigit(response[2])) \ 9364562Sgshapiro { \ 9464562Sgshapiro if (response != NULL) \ 9590792Sgshapiro sm_free(response); /* XXX */ \ 9664562Sgshapiro response = newstr(default); \ 9764562Sgshapiro } \ 9864562Sgshapiro else \ 9964562Sgshapiro { \ 10064562Sgshapiro char *ptr = response; \ 10164562Sgshapiro \ 10264562Sgshapiro /* Check for unprotected %'s in the string */ \ 10364562Sgshapiro while (*ptr != '\0') \ 10464562Sgshapiro { \ 10564562Sgshapiro if (*ptr == '%' && *++ptr != '%') \ 10664562Sgshapiro { \ 10790792Sgshapiro sm_free(response); /* XXX */ \ 10864562Sgshapiro response = newstr(default); \ 10964562Sgshapiro break; \ 11064562Sgshapiro } \ 11164562Sgshapiro ptr++; \ 11264562Sgshapiro } \ 11364562Sgshapiro } 11464562Sgshapiro 11564562Sgshapiro# define MILTER_DF_ERROR(msg) \ 11664562Sgshapiro{ \ 11764562Sgshapiro int save_errno = errno; \ 11864562Sgshapiro \ 11964562Sgshapiro if (tTd(64, 5)) \ 12064562Sgshapiro { \ 12190792Sgshapiro sm_dprintf(msg, dfname, sm_errstring(save_errno)); \ 12290792Sgshapiro sm_dprintf("\n"); \ 12364562Sgshapiro } \ 12490792Sgshapiro if (MilterLogLevel > 0) \ 12590792Sgshapiro sm_syslog(LOG_ERR, e->e_id, msg, dfname, sm_errstring(save_errno)); \ 12690792Sgshapiro if (SuperSafe == SAFE_REALLY) \ 12764562Sgshapiro { \ 12864562Sgshapiro if (e->e_dfp != NULL) \ 12964562Sgshapiro { \ 13090792Sgshapiro (void) sm_io_close(e->e_dfp, SM_TIME_DEFAULT); \ 13164562Sgshapiro e->e_dfp = NULL; \ 13264562Sgshapiro } \ 13364562Sgshapiro e->e_flags &= ~EF_HAS_DF; \ 13464562Sgshapiro } \ 13564562Sgshapiro errno = save_errno; \ 13664562Sgshapiro} 13764562Sgshapiro 13864562Sgshapiro/* 13964562Sgshapiro** MILTER_TIMEOUT -- make sure socket is ready in time 14064562Sgshapiro** 14164562Sgshapiro** Parameters: 14264562Sgshapiro** routine -- routine name for debug/logging 14364562Sgshapiro** secs -- number of seconds in timeout 14464562Sgshapiro** write -- waiting to read or write? 14590792Sgshapiro** started -- whether this is part of a previous sequence 14664562Sgshapiro** 14764562Sgshapiro** Assumes 'm' is a milter structure for the current socket. 14864562Sgshapiro*/ 14964562Sgshapiro 15090792Sgshapiro# define MILTER_TIMEOUT(routine, secs, write, started) \ 15164562Sgshapiro{ \ 15264562Sgshapiro int ret; \ 15364562Sgshapiro int save_errno; \ 15464562Sgshapiro fd_set fds; \ 15564562Sgshapiro struct timeval tv; \ 15664562Sgshapiro \ 15790792Sgshapiro if (SM_FD_SETSIZE > 0 && m->mf_sock >= SM_FD_SETSIZE) \ 15864562Sgshapiro { \ 15964562Sgshapiro if (tTd(64, 5)) \ 16090792Sgshapiro sm_dprintf("milter_%s(%s): socket %d is larger than FD_SETSIZE %d\n", \ 16190792Sgshapiro (routine), m->mf_name, m->mf_sock, \ 16290792Sgshapiro SM_FD_SETSIZE); \ 16390792Sgshapiro if (MilterLogLevel > 0) \ 16464562Sgshapiro sm_syslog(LOG_ERR, e->e_id, \ 16590792Sgshapiro "Milter (%s): socket(%s) %d is larger than FD_SETSIZE %d", \ 16690792Sgshapiro m->mf_name, (routine), m->mf_sock, \ 16790792Sgshapiro SM_FD_SETSIZE); \ 16890792Sgshapiro milter_error(m, e); \ 16964562Sgshapiro return NULL; \ 17064562Sgshapiro } \ 17164562Sgshapiro \ 17294334Sgshapiro do \ 17394334Sgshapiro { \ 17494334Sgshapiro FD_ZERO(&fds); \ 17594334Sgshapiro SM_FD_SET(m->mf_sock, &fds); \ 17694334Sgshapiro tv.tv_sec = (secs); \ 17794334Sgshapiro tv.tv_usec = 0; \ 17894334Sgshapiro ret = select(m->mf_sock + 1, \ 17994334Sgshapiro (write) ? NULL : &fds, \ 18094334Sgshapiro (write) ? &fds : NULL, \ 18194334Sgshapiro NULL, &tv); \ 18294334Sgshapiro } while (ret < 0 && errno == EINTR); \ 18364562Sgshapiro \ 18464562Sgshapiro switch (ret) \ 18564562Sgshapiro { \ 18664562Sgshapiro case 0: \ 18764562Sgshapiro if (tTd(64, 5)) \ 18890792Sgshapiro sm_dprintf("milter_%s(%s): timeout\n", (routine), \ 18990792Sgshapiro m->mf_name); \ 19090792Sgshapiro if (MilterLogLevel > 0) \ 19190792Sgshapiro sm_syslog(LOG_ERR, e->e_id, \ 19290792Sgshapiro "Milter (%s): %s %s %s %s", \ 19390792Sgshapiro m->mf_name, "timeout", \ 19490792Sgshapiro started ? "during" : "before", \ 19590792Sgshapiro "data", (routine)); \ 19690792Sgshapiro milter_error(m, e); \ 19764562Sgshapiro return NULL; \ 19864562Sgshapiro \ 19964562Sgshapiro case -1: \ 20064562Sgshapiro save_errno = errno; \ 20164562Sgshapiro if (tTd(64, 5)) \ 20290792Sgshapiro sm_dprintf("milter_%s(%s): select: %s\n", (routine), \ 20390792Sgshapiro m->mf_name, sm_errstring(save_errno)); \ 20490792Sgshapiro if (MilterLogLevel > 0) \ 20590792Sgshapiro { \ 20664562Sgshapiro sm_syslog(LOG_ERR, e->e_id, \ 20790792Sgshapiro "Milter (%s): select(%s): %s", \ 20890792Sgshapiro m->mf_name, (routine), \ 20990792Sgshapiro sm_errstring(save_errno)); \ 21090792Sgshapiro } \ 21190792Sgshapiro milter_error(m, e); \ 21264562Sgshapiro return NULL; \ 21364562Sgshapiro \ 21464562Sgshapiro default: \ 21571345Sgshapiro if (SM_FD_ISSET(m->mf_sock, &fds)) \ 21664562Sgshapiro break; \ 21764562Sgshapiro if (tTd(64, 5)) \ 21890792Sgshapiro sm_dprintf("milter_%s(%s): socket not ready\n", \ 21990792Sgshapiro (routine), m->mf_name); \ 22090792Sgshapiro if (MilterLogLevel > 0) \ 22190792Sgshapiro { \ 22264562Sgshapiro sm_syslog(LOG_ERR, e->e_id, \ 22390792Sgshapiro "Milter (%s): socket(%s) not ready", \ 22490792Sgshapiro m->mf_name, (routine)); \ 22590792Sgshapiro } \ 22690792Sgshapiro milter_error(m, e); \ 22764562Sgshapiro return NULL; \ 22864562Sgshapiro } \ 22964562Sgshapiro} 23064562Sgshapiro 23164562Sgshapiro/* 23264562Sgshapiro** Low level functions 23364562Sgshapiro*/ 23464562Sgshapiro 23590792Sgshapiro/* 23664562Sgshapiro** MILTER_READ -- read from a remote milter filter 23764562Sgshapiro** 23864562Sgshapiro** Parameters: 23964562Sgshapiro** m -- milter to read from. 24064562Sgshapiro** cmd -- return param for command read. 24164562Sgshapiro** rlen -- return length of response string. 24264562Sgshapiro** to -- timeout in seconds. 24364562Sgshapiro** e -- current envelope. 24464562Sgshapiro** 24564562Sgshapiro** Returns: 24664562Sgshapiro** response string (may be NULL) 24764562Sgshapiro*/ 24864562Sgshapiro 24964562Sgshapirostatic char * 25064562Sgshapiromilter_sysread(m, buf, sz, to, e) 25164562Sgshapiro struct milter *m; 25264562Sgshapiro char *buf; 25364562Sgshapiro ssize_t sz; 25464562Sgshapiro time_t to; 25564562Sgshapiro ENVELOPE *e; 25664562Sgshapiro{ 25766494Sgshapiro time_t readstart = 0; 25864562Sgshapiro ssize_t len, curl; 25990792Sgshapiro bool started = false; 26064562Sgshapiro 26164562Sgshapiro curl = 0; 26264562Sgshapiro 26364562Sgshapiro if (to > 0) 26464562Sgshapiro readstart = curtime(); 26564562Sgshapiro 26664562Sgshapiro for (;;) 26764562Sgshapiro { 26864562Sgshapiro if (to > 0) 26964562Sgshapiro { 27064562Sgshapiro time_t now; 27164562Sgshapiro 27264562Sgshapiro now = curtime(); 27364562Sgshapiro if (now - readstart >= to) 27464562Sgshapiro { 27564562Sgshapiro if (tTd(64, 5)) 27690792Sgshapiro sm_dprintf("milter_read (%s): %s %s %s", 27790792Sgshapiro m->mf_name, "timeout", 27890792Sgshapiro started ? "during" : "before", 27990792Sgshapiro "data read"); 28090792Sgshapiro if (MilterLogLevel > 0) 28164562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 28290792Sgshapiro "Milter (%s): %s %s %s", 28390792Sgshapiro m->mf_name, "timeout", 28490792Sgshapiro started ? "during" : "before", 28590792Sgshapiro "data read"); 28690792Sgshapiro milter_error(m, e); 28764562Sgshapiro return NULL; 28864562Sgshapiro } 28964562Sgshapiro to -= now - readstart; 29064562Sgshapiro readstart = now; 29190792Sgshapiro MILTER_TIMEOUT("read", to, false, started); 29264562Sgshapiro } 29364562Sgshapiro 29464562Sgshapiro len = read(m->mf_sock, buf + curl, sz - curl); 29564562Sgshapiro 29664562Sgshapiro if (len < 0) 29764562Sgshapiro { 29864562Sgshapiro int save_errno = errno; 29964562Sgshapiro 30064562Sgshapiro if (tTd(64, 5)) 30190792Sgshapiro sm_dprintf("milter_read(%s): read returned %ld: %s\n", 30264562Sgshapiro m->mf_name, (long) len, 30390792Sgshapiro sm_errstring(save_errno)); 30490792Sgshapiro if (MilterLogLevel > 0) 30564562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 30690792Sgshapiro "Milter (%s): read returned %ld: %s", 30764562Sgshapiro m->mf_name, (long) len, 30890792Sgshapiro sm_errstring(save_errno)); 30990792Sgshapiro milter_error(m, e); 31064562Sgshapiro return NULL; 31164562Sgshapiro } 31264562Sgshapiro 31390792Sgshapiro started = true; 31464562Sgshapiro curl += len; 31573188Sgshapiro if (len == 0 || curl >= sz) 31664562Sgshapiro break; 31764562Sgshapiro 31864562Sgshapiro } 31964562Sgshapiro 32064562Sgshapiro if (curl != sz) 32164562Sgshapiro { 32264562Sgshapiro if (tTd(64, 5)) 32390792Sgshapiro sm_dprintf("milter_read(%s): cmd read returned %ld, expecting %ld\n", 32464562Sgshapiro m->mf_name, (long) curl, (long) sz); 32590792Sgshapiro if (MilterLogLevel > 0) 32664562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 32790792Sgshapiro "milter_read(%s): cmd read returned %ld, expecting %ld", 32864562Sgshapiro m->mf_name, (long) curl, (long) sz); 32990792Sgshapiro milter_error(m, e); 33064562Sgshapiro return NULL; 33164562Sgshapiro } 33264562Sgshapiro return buf; 33364562Sgshapiro} 33464562Sgshapiro 33564562Sgshapirostatic char * 33664562Sgshapiromilter_read(m, cmd, rlen, to, e) 33764562Sgshapiro struct milter *m; 33864562Sgshapiro char *cmd; 33964562Sgshapiro ssize_t *rlen; 34064562Sgshapiro time_t to; 34164562Sgshapiro ENVELOPE *e; 34264562Sgshapiro{ 34366494Sgshapiro time_t readstart = 0; 34464562Sgshapiro ssize_t expl; 34564562Sgshapiro mi_int32 i; 34664562Sgshapiro char *buf; 34764562Sgshapiro char data[MILTER_LEN_BYTES + 1]; 34864562Sgshapiro 34964562Sgshapiro *rlen = 0; 35064562Sgshapiro *cmd = '\0'; 35164562Sgshapiro 35264562Sgshapiro if (to > 0) 35364562Sgshapiro readstart = curtime(); 35464562Sgshapiro 35564562Sgshapiro if (milter_sysread(m, data, sizeof data, to, e) == NULL) 35664562Sgshapiro return NULL; 35764562Sgshapiro 35864562Sgshapiro /* reset timeout */ 35964562Sgshapiro if (to > 0) 36064562Sgshapiro { 36164562Sgshapiro time_t now; 36264562Sgshapiro 36364562Sgshapiro now = curtime(); 36464562Sgshapiro if (now - readstart >= to) 36564562Sgshapiro { 36664562Sgshapiro if (tTd(64, 5)) 36790792Sgshapiro sm_dprintf("milter_read(%s): timeout before data read\n", 36864562Sgshapiro m->mf_name); 36990792Sgshapiro if (MilterLogLevel > 0) 37064562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 37190792Sgshapiro "Milter read(%s): timeout before data read", 37264562Sgshapiro m->mf_name); 37390792Sgshapiro milter_error(m, e); 37464562Sgshapiro return NULL; 37564562Sgshapiro } 37664562Sgshapiro to -= now - readstart; 37764562Sgshapiro } 37864562Sgshapiro 37964562Sgshapiro *cmd = data[MILTER_LEN_BYTES]; 38064562Sgshapiro data[MILTER_LEN_BYTES] = '\0'; 38164562Sgshapiro (void) memcpy(&i, data, MILTER_LEN_BYTES); 38264562Sgshapiro expl = ntohl(i) - 1; 38364562Sgshapiro 38464562Sgshapiro if (tTd(64, 25)) 38590792Sgshapiro sm_dprintf("milter_read(%s): expecting %ld bytes\n", 38664562Sgshapiro m->mf_name, (long) expl); 38764562Sgshapiro 38864562Sgshapiro if (expl < 0) 38964562Sgshapiro { 39064562Sgshapiro if (tTd(64, 5)) 39190792Sgshapiro sm_dprintf("milter_read(%s): read size %ld out of range\n", 39264562Sgshapiro m->mf_name, (long) expl); 39390792Sgshapiro if (MilterLogLevel > 0) 39464562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 39564562Sgshapiro "milter_read(%s): read size %ld out of range", 39664562Sgshapiro m->mf_name, (long) expl); 39790792Sgshapiro milter_error(m, e); 39864562Sgshapiro return NULL; 39964562Sgshapiro } 40064562Sgshapiro 40164562Sgshapiro if (expl == 0) 40264562Sgshapiro return NULL; 40364562Sgshapiro 40490792Sgshapiro buf = (char *) xalloc(expl); 40564562Sgshapiro 40664562Sgshapiro if (milter_sysread(m, buf, expl, to, e) == NULL) 40764562Sgshapiro { 40890792Sgshapiro sm_free(buf); /* XXX */ 40964562Sgshapiro return NULL; 41064562Sgshapiro } 41164562Sgshapiro 41264562Sgshapiro if (tTd(64, 50)) 41390792Sgshapiro sm_dprintf("milter_read(%s): Returning %*s\n", 41464562Sgshapiro m->mf_name, (int) expl, buf); 41564562Sgshapiro *rlen = expl; 41664562Sgshapiro return buf; 41764562Sgshapiro} 41890792Sgshapiro/* 41964562Sgshapiro** MILTER_WRITE -- write to a remote milter filter 42064562Sgshapiro** 42164562Sgshapiro** Parameters: 42264562Sgshapiro** m -- milter to read from. 42364562Sgshapiro** cmd -- command to send. 42464562Sgshapiro** buf -- optional command data. 42564562Sgshapiro** len -- length of buf. 42664562Sgshapiro** to -- timeout in seconds. 42764562Sgshapiro** e -- current envelope. 42864562Sgshapiro** 42964562Sgshapiro** Returns: 43064562Sgshapiro** buf if successful, NULL otherwise 43164562Sgshapiro** Not actually used anywhere but function prototype 43264562Sgshapiro** must match milter_read() 43364562Sgshapiro*/ 43464562Sgshapiro 43564562Sgshapirostatic char * 43664562Sgshapiromilter_write(m, cmd, buf, len, to, e) 43764562Sgshapiro struct milter *m; 43864562Sgshapiro char cmd; 43964562Sgshapiro char *buf; 44064562Sgshapiro ssize_t len; 44164562Sgshapiro time_t to; 44264562Sgshapiro ENVELOPE *e; 44364562Sgshapiro{ 44464562Sgshapiro time_t writestart = (time_t) 0; 44564562Sgshapiro ssize_t sl, i; 44664562Sgshapiro mi_int32 nl; 44764562Sgshapiro char data[MILTER_LEN_BYTES + 1]; 44890792Sgshapiro bool started = false; 44964562Sgshapiro 45064562Sgshapiro if (len < 0 || len > MILTER_CHUNK_SIZE) 45164562Sgshapiro { 45264562Sgshapiro if (tTd(64, 5)) 45390792Sgshapiro sm_dprintf("milter_write(%s): length %ld out of range\n", 45464562Sgshapiro m->mf_name, (long) len); 45590792Sgshapiro if (MilterLogLevel > 0) 45664562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 45764562Sgshapiro "milter_write(%s): length %ld out of range", 45864562Sgshapiro m->mf_name, (long) len); 45990792Sgshapiro milter_error(m, e); 46064562Sgshapiro return NULL; 46164562Sgshapiro } 46264562Sgshapiro 46364562Sgshapiro if (tTd(64, 20)) 46490792Sgshapiro sm_dprintf("milter_write(%s): cmd %c, len %ld\n", 46590792Sgshapiro m->mf_name, cmd, (long) len); 46664562Sgshapiro 46764562Sgshapiro nl = htonl(len + 1); /* add 1 for the cmd char */ 46864562Sgshapiro (void) memcpy(data, (char *) &nl, MILTER_LEN_BYTES); 46964562Sgshapiro data[MILTER_LEN_BYTES] = cmd; 47064562Sgshapiro sl = MILTER_LEN_BYTES + 1; 47164562Sgshapiro 47264562Sgshapiro if (to > 0) 47364562Sgshapiro { 47464562Sgshapiro writestart = curtime(); 47590792Sgshapiro MILTER_TIMEOUT("write", to, true, started); 47664562Sgshapiro } 47764562Sgshapiro 47864562Sgshapiro /* use writev() instead to send the whole stuff at once? */ 47964562Sgshapiro i = write(m->mf_sock, (void *) data, sl); 48064562Sgshapiro if (i != sl) 48164562Sgshapiro { 48264562Sgshapiro int save_errno = errno; 48364562Sgshapiro 48464562Sgshapiro if (tTd(64, 5)) 48590792Sgshapiro sm_dprintf("milter_write (%s): write(%c) returned %ld, expected %ld: %s\n", 48690792Sgshapiro m->mf_name, cmd, (long) i, (long) sl, 48790792Sgshapiro sm_errstring(save_errno)); 48890792Sgshapiro if (MilterLogLevel > 0) 48964562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 49090792Sgshapiro "Milter (%s): write(%c) returned %ld, expected %ld: %s", 49164562Sgshapiro m->mf_name, cmd, (long) i, (long) sl, 49290792Sgshapiro sm_errstring(save_errno)); 49390792Sgshapiro milter_error(m, e); 49464562Sgshapiro return buf; 49564562Sgshapiro } 49664562Sgshapiro 49764562Sgshapiro if (len <= 0 || buf == NULL) 49864562Sgshapiro return buf; 49964562Sgshapiro 50064562Sgshapiro if (tTd(64, 50)) 50190792Sgshapiro sm_dprintf("milter_write(%s): Sending %*s\n", 50290792Sgshapiro m->mf_name, (int) len, buf); 50390792Sgshapiro started = true; 50464562Sgshapiro 50564562Sgshapiro if (to > 0) 50664562Sgshapiro { 50764562Sgshapiro time_t now; 50864562Sgshapiro 50964562Sgshapiro now = curtime(); 51064562Sgshapiro if (now - writestart >= to) 51164562Sgshapiro { 51264562Sgshapiro if (tTd(64, 5)) 51390792Sgshapiro sm_dprintf("milter_write(%s): timeout before data write\n", 51490792Sgshapiro m->mf_name); 51590792Sgshapiro if (MilterLogLevel > 0) 51664562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 51790792Sgshapiro "Milter (%s): timeout before data write", 51864562Sgshapiro m->mf_name); 51990792Sgshapiro milter_error(m, e); 52064562Sgshapiro return NULL; 52164562Sgshapiro } 52264562Sgshapiro else 52364562Sgshapiro { 52464562Sgshapiro to -= now - writestart; 52590792Sgshapiro MILTER_TIMEOUT("write", to, true, started); 52664562Sgshapiro } 52764562Sgshapiro } 52864562Sgshapiro 52964562Sgshapiro i = write(m->mf_sock, (void *) buf, len); 53064562Sgshapiro if (i != len) 53164562Sgshapiro { 53264562Sgshapiro int save_errno = errno; 53364562Sgshapiro 53464562Sgshapiro if (tTd(64, 5)) 53590792Sgshapiro sm_dprintf("milter_write(%s): write(%c) returned %ld, expected %ld: %s\n", 53690792Sgshapiro m->mf_name, cmd, (long) i, (long) sl, 53790792Sgshapiro sm_errstring(save_errno)); 53890792Sgshapiro if (MilterLogLevel > 0) 53964562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 54090792Sgshapiro "Milter (%s): write(%c) returned %ld, expected %ld: %s", 54164562Sgshapiro m->mf_name, cmd, (long) i, (long) len, 54290792Sgshapiro sm_errstring(save_errno)); 54390792Sgshapiro milter_error(m, e); 54464562Sgshapiro return NULL; 54564562Sgshapiro } 54664562Sgshapiro return buf; 54764562Sgshapiro} 54864562Sgshapiro 54964562Sgshapiro/* 55064562Sgshapiro** Utility functions 55164562Sgshapiro*/ 55264562Sgshapiro 55390792Sgshapiro/* 55464562Sgshapiro** MILTER_OPEN -- connect to remote milter filter 55564562Sgshapiro** 55664562Sgshapiro** Parameters: 55764562Sgshapiro** m -- milter to connect to. 55864562Sgshapiro** parseonly -- parse but don't connect. 55964562Sgshapiro** e -- current envelope. 56064562Sgshapiro** 56164562Sgshapiro** Returns: 562111823Sgshapiro** connected socket if successful && !parseonly, 56364562Sgshapiro** 0 upon parse success if parseonly, 56464562Sgshapiro** -1 otherwise. 56564562Sgshapiro*/ 56664562Sgshapiro 56780785Sgshapirostatic jmp_buf MilterConnectTimeout; 56880785Sgshapiro 56964562Sgshapirostatic int 57064562Sgshapiromilter_open(m, parseonly, e) 57164562Sgshapiro struct milter *m; 57264562Sgshapiro bool parseonly; 57364562Sgshapiro ENVELOPE *e; 57464562Sgshapiro{ 57564562Sgshapiro int sock = 0; 57664562Sgshapiro SOCKADDR_LEN_T addrlen = 0; 57764562Sgshapiro int addrno = 0; 57864562Sgshapiro int save_errno; 57964562Sgshapiro char *p; 58064562Sgshapiro char *colon; 58164562Sgshapiro char *at; 58264562Sgshapiro struct hostent *hp = NULL; 58364562Sgshapiro SOCKADDR addr; 58464562Sgshapiro 58564562Sgshapiro if (m->mf_conn == NULL || m->mf_conn[0] == '\0') 58664562Sgshapiro { 58764562Sgshapiro if (tTd(64, 5)) 58890792Sgshapiro sm_dprintf("X%s: empty or missing socket information\n", 58990792Sgshapiro m->mf_name); 59064562Sgshapiro if (parseonly) 59164562Sgshapiro syserr("X%s: empty or missing socket information", 59264562Sgshapiro m->mf_name); 593110560Sgshapiro else if (MilterLogLevel > 0) 59464562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 59590792Sgshapiro "Milter (%s): empty or missing socket information", 59664562Sgshapiro m->mf_name); 59790792Sgshapiro milter_error(m, e); 59864562Sgshapiro return -1; 59964562Sgshapiro } 60064562Sgshapiro 60164562Sgshapiro /* protocol:filename or protocol:port@host */ 60294334Sgshapiro memset(&addr, '\0', sizeof addr); 60364562Sgshapiro p = m->mf_conn; 60464562Sgshapiro colon = strchr(p, ':'); 60564562Sgshapiro if (colon != NULL) 60664562Sgshapiro { 60764562Sgshapiro *colon = '\0'; 60864562Sgshapiro 60964562Sgshapiro if (*p == '\0') 61064562Sgshapiro { 61164562Sgshapiro# if NETUNIX 61264562Sgshapiro /* default to AF_UNIX */ 61364562Sgshapiro addr.sa.sa_family = AF_UNIX; 61464562Sgshapiro# else /* NETUNIX */ 61564562Sgshapiro# if NETINET 61664562Sgshapiro /* default to AF_INET */ 61764562Sgshapiro addr.sa.sa_family = AF_INET; 61864562Sgshapiro# else /* NETINET */ 61964562Sgshapiro# if NETINET6 62064562Sgshapiro /* default to AF_INET6 */ 62164562Sgshapiro addr.sa.sa_family = AF_INET6; 62264562Sgshapiro# else /* NETINET6 */ 62364562Sgshapiro /* no protocols available */ 624110560Sgshapiro if (MilterLogLevel > 0) 625110560Sgshapiro sm_syslog(LOG_ERR, e->e_id, 626110560Sgshapiro "Milter (%s): no valid socket protocols available", 627110560Sgshapiro m->mf_name); 62890792Sgshapiro milter_error(m, e); 62964562Sgshapiro return -1; 63064562Sgshapiro# endif /* NETINET6 */ 63164562Sgshapiro# endif /* NETINET */ 63264562Sgshapiro# endif /* NETUNIX */ 63364562Sgshapiro } 63464562Sgshapiro# if NETUNIX 63590792Sgshapiro else if (sm_strcasecmp(p, "unix") == 0 || 63690792Sgshapiro sm_strcasecmp(p, "local") == 0) 63764562Sgshapiro addr.sa.sa_family = AF_UNIX; 63864562Sgshapiro# endif /* NETUNIX */ 63964562Sgshapiro# if NETINET 64090792Sgshapiro else if (sm_strcasecmp(p, "inet") == 0) 64164562Sgshapiro addr.sa.sa_family = AF_INET; 64264562Sgshapiro# endif /* NETINET */ 64364562Sgshapiro# if NETINET6 64490792Sgshapiro else if (sm_strcasecmp(p, "inet6") == 0) 64564562Sgshapiro addr.sa.sa_family = AF_INET6; 64664562Sgshapiro# endif /* NETINET6 */ 64764562Sgshapiro else 64864562Sgshapiro { 64964562Sgshapiro# ifdef EPROTONOSUPPORT 65064562Sgshapiro errno = EPROTONOSUPPORT; 65164562Sgshapiro# else /* EPROTONOSUPPORT */ 65264562Sgshapiro errno = EINVAL; 65364562Sgshapiro# endif /* EPROTONOSUPPORT */ 65464562Sgshapiro if (tTd(64, 5)) 65590792Sgshapiro sm_dprintf("X%s: unknown socket type %s\n", 65664562Sgshapiro m->mf_name, p); 65764562Sgshapiro if (parseonly) 65864562Sgshapiro syserr("X%s: unknown socket type %s", 65964562Sgshapiro m->mf_name, p); 660110560Sgshapiro else if (MilterLogLevel > 0) 66164562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 66290792Sgshapiro "Milter (%s): unknown socket type %s", 66364562Sgshapiro m->mf_name, p); 66490792Sgshapiro milter_error(m, e); 66564562Sgshapiro return -1; 66664562Sgshapiro } 66764562Sgshapiro *colon++ = ':'; 66864562Sgshapiro } 66964562Sgshapiro else 67064562Sgshapiro { 67164562Sgshapiro /* default to AF_UNIX */ 67264562Sgshapiro addr.sa.sa_family = AF_UNIX; 67364562Sgshapiro colon = p; 67464562Sgshapiro } 67564562Sgshapiro 67664562Sgshapiro# if NETUNIX 67764562Sgshapiro if (addr.sa.sa_family == AF_UNIX) 67864562Sgshapiro { 67964562Sgshapiro long sff = SFF_SAFEDIRPATH|SFF_OPENASROOT|SFF_NOLINK|SFF_EXECOK; 68064562Sgshapiro 68164562Sgshapiro at = colon; 68264562Sgshapiro if (strlen(colon) >= sizeof addr.sunix.sun_path) 68364562Sgshapiro { 68464562Sgshapiro if (tTd(64, 5)) 68590792Sgshapiro sm_dprintf("X%s: local socket name %s too long\n", 68664562Sgshapiro m->mf_name, colon); 68764562Sgshapiro errno = EINVAL; 68864562Sgshapiro if (parseonly) 68964562Sgshapiro syserr("X%s: local socket name %s too long", 69064562Sgshapiro m->mf_name, colon); 691110560Sgshapiro else if (MilterLogLevel > 0) 69264562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 69390792Sgshapiro "Milter (%s): local socket name %s too long", 69464562Sgshapiro m->mf_name, colon); 69590792Sgshapiro milter_error(m, e); 69664562Sgshapiro return -1; 69764562Sgshapiro } 69864562Sgshapiro errno = safefile(colon, RunAsUid, RunAsGid, RunAsUserName, sff, 69964562Sgshapiro S_IRUSR|S_IWUSR, NULL); 70064562Sgshapiro 70164562Sgshapiro /* if just parsing .cf file, socket doesn't need to exist */ 70264562Sgshapiro if (parseonly && errno == ENOENT) 70364562Sgshapiro { 70464562Sgshapiro if (OpMode == MD_DAEMON || 70564562Sgshapiro OpMode == MD_FGDAEMON) 70690792Sgshapiro (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, 70790792Sgshapiro "WARNING: X%s: local socket name %s missing\n", 70890792Sgshapiro m->mf_name, colon); 70964562Sgshapiro } 71064562Sgshapiro else if (errno != 0) 71164562Sgshapiro { 71264562Sgshapiro /* if not safe, don't create */ 71364562Sgshapiro save_errno = errno; 71464562Sgshapiro if (tTd(64, 5)) 71590792Sgshapiro sm_dprintf("X%s: local socket name %s unsafe\n", 71664562Sgshapiro m->mf_name, colon); 71764562Sgshapiro errno = save_errno; 71864562Sgshapiro if (parseonly) 71964562Sgshapiro { 72064562Sgshapiro if (OpMode == MD_DAEMON || 72164562Sgshapiro OpMode == MD_FGDAEMON || 72264562Sgshapiro OpMode == MD_SMTP) 72364562Sgshapiro syserr("X%s: local socket name %s unsafe", 72464562Sgshapiro m->mf_name, colon); 72564562Sgshapiro } 726110560Sgshapiro else if (MilterLogLevel > 0) 72764562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 72890792Sgshapiro "Milter (%s): local socket name %s unsafe", 72964562Sgshapiro m->mf_name, colon); 73090792Sgshapiro milter_error(m, e); 73164562Sgshapiro return -1; 73264562Sgshapiro } 73364562Sgshapiro 73490792Sgshapiro (void) sm_strlcpy(addr.sunix.sun_path, colon, 73564562Sgshapiro sizeof addr.sunix.sun_path); 73664562Sgshapiro addrlen = sizeof (struct sockaddr_un); 73764562Sgshapiro } 73864562Sgshapiro else 73964562Sgshapiro# endif /* NETUNIX */ 74064562Sgshapiro# if NETINET || NETINET6 74190792Sgshapiro if (false 74264562Sgshapiro# if NETINET 74364562Sgshapiro || addr.sa.sa_family == AF_INET 74464562Sgshapiro# endif /* NETINET */ 74564562Sgshapiro# if NETINET6 74664562Sgshapiro || addr.sa.sa_family == AF_INET6 74764562Sgshapiro# endif /* NETINET6 */ 74864562Sgshapiro ) 74964562Sgshapiro { 75090792Sgshapiro unsigned short port; 75164562Sgshapiro 75264562Sgshapiro /* Parse port@host */ 75364562Sgshapiro at = strchr(colon, '@'); 75464562Sgshapiro if (at == NULL) 75564562Sgshapiro { 75664562Sgshapiro if (tTd(64, 5)) 75790792Sgshapiro sm_dprintf("X%s: bad address %s (expected port@host)\n", 75864562Sgshapiro m->mf_name, colon); 75964562Sgshapiro if (parseonly) 76064562Sgshapiro syserr("X%s: bad address %s (expected port@host)", 76164562Sgshapiro m->mf_name, colon); 762110560Sgshapiro else if (MilterLogLevel > 0) 76364562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 76490792Sgshapiro "Milter (%s): bad address %s (expected port@host)", 76564562Sgshapiro m->mf_name, colon); 76690792Sgshapiro milter_error(m, e); 76764562Sgshapiro return -1; 76864562Sgshapiro } 76964562Sgshapiro *at = '\0'; 77064562Sgshapiro if (isascii(*colon) && isdigit(*colon)) 77190792Sgshapiro port = htons((unsigned short) atoi(colon)); 77264562Sgshapiro else 77364562Sgshapiro { 77464562Sgshapiro# ifdef NO_GETSERVBYNAME 77564562Sgshapiro if (tTd(64, 5)) 77690792Sgshapiro sm_dprintf("X%s: invalid port number %s\n", 77764562Sgshapiro m->mf_name, colon); 77864562Sgshapiro if (parseonly) 77964562Sgshapiro syserr("X%s: invalid port number %s", 78064562Sgshapiro m->mf_name, colon); 781110560Sgshapiro else if (MilterLogLevel > 0) 78264562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 78390792Sgshapiro "Milter (%s): invalid port number %s", 78464562Sgshapiro m->mf_name, colon); 78590792Sgshapiro milter_error(m, e); 78664562Sgshapiro return -1; 78764562Sgshapiro# else /* NO_GETSERVBYNAME */ 78864562Sgshapiro register struct servent *sp; 78964562Sgshapiro 79064562Sgshapiro sp = getservbyname(colon, "tcp"); 79164562Sgshapiro if (sp == NULL) 79264562Sgshapiro { 79364562Sgshapiro save_errno = errno; 79464562Sgshapiro if (tTd(64, 5)) 79590792Sgshapiro sm_dprintf("X%s: unknown port name %s\n", 79664562Sgshapiro m->mf_name, colon); 79764562Sgshapiro errno = save_errno; 79864562Sgshapiro if (parseonly) 79964562Sgshapiro syserr("X%s: unknown port name %s", 80064562Sgshapiro m->mf_name, colon); 801110560Sgshapiro else if (MilterLogLevel > 0) 80264562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 80390792Sgshapiro "Milter (%s): unknown port name %s", 80464562Sgshapiro m->mf_name, colon); 80590792Sgshapiro milter_error(m, e); 80664562Sgshapiro return -1; 80764562Sgshapiro } 80864562Sgshapiro port = sp->s_port; 80964562Sgshapiro# endif /* NO_GETSERVBYNAME */ 81064562Sgshapiro } 81164562Sgshapiro *at++ = '@'; 81264562Sgshapiro if (*at == '[') 81364562Sgshapiro { 81464562Sgshapiro char *end; 81564562Sgshapiro 81664562Sgshapiro end = strchr(at, ']'); 81764562Sgshapiro if (end != NULL) 81864562Sgshapiro { 81990792Sgshapiro bool found = false; 82064562Sgshapiro# if NETINET 82164562Sgshapiro unsigned long hid = INADDR_NONE; 82264562Sgshapiro# endif /* NETINET */ 82364562Sgshapiro# if NETINET6 82464562Sgshapiro struct sockaddr_in6 hid6; 82564562Sgshapiro# endif /* NETINET6 */ 82664562Sgshapiro 82764562Sgshapiro *end = '\0'; 82864562Sgshapiro# if NETINET 82964562Sgshapiro if (addr.sa.sa_family == AF_INET && 83064562Sgshapiro (hid = inet_addr(&at[1])) != INADDR_NONE) 83164562Sgshapiro { 83264562Sgshapiro addr.sin.sin_addr.s_addr = hid; 83364562Sgshapiro addr.sin.sin_port = port; 83490792Sgshapiro found = true; 83564562Sgshapiro } 83664562Sgshapiro# endif /* NETINET */ 83764562Sgshapiro# if NETINET6 83864562Sgshapiro (void) memset(&hid6, '\0', sizeof hid6); 83964562Sgshapiro if (addr.sa.sa_family == AF_INET6 && 84090792Sgshapiro anynet_pton(AF_INET6, &at[1], 84190792Sgshapiro &hid6.sin6_addr) == 1) 84264562Sgshapiro { 84364562Sgshapiro addr.sin6.sin6_addr = hid6.sin6_addr; 84464562Sgshapiro addr.sin6.sin6_port = port; 84590792Sgshapiro found = true; 84664562Sgshapiro } 84764562Sgshapiro# endif /* NETINET6 */ 84864562Sgshapiro *end = ']'; 84964562Sgshapiro if (!found) 85064562Sgshapiro { 85164562Sgshapiro if (tTd(64, 5)) 85290792Sgshapiro sm_dprintf("X%s: Invalid numeric domain spec \"%s\"\n", 85364562Sgshapiro m->mf_name, at); 85464562Sgshapiro if (parseonly) 85564562Sgshapiro syserr("X%s: Invalid numeric domain spec \"%s\"", 85664562Sgshapiro m->mf_name, at); 857110560Sgshapiro else if (MilterLogLevel > 0) 85864562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 85990792Sgshapiro "Milter (%s): Invalid numeric domain spec \"%s\"", 86064562Sgshapiro m->mf_name, at); 86190792Sgshapiro milter_error(m, e); 86264562Sgshapiro return -1; 86364562Sgshapiro } 86464562Sgshapiro } 86564562Sgshapiro else 86664562Sgshapiro { 86764562Sgshapiro if (tTd(64, 5)) 86890792Sgshapiro sm_dprintf("X%s: Invalid numeric domain spec \"%s\"\n", 86964562Sgshapiro m->mf_name, at); 87064562Sgshapiro if (parseonly) 87164562Sgshapiro syserr("X%s: Invalid numeric domain spec \"%s\"", 87264562Sgshapiro m->mf_name, at); 873110560Sgshapiro else if (MilterLogLevel > 0) 87464562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 87590792Sgshapiro "Milter (%s): Invalid numeric domain spec \"%s\"", 87664562Sgshapiro m->mf_name, at); 87790792Sgshapiro milter_error(m, e); 87864562Sgshapiro return -1; 87964562Sgshapiro } 88064562Sgshapiro } 88164562Sgshapiro else 88264562Sgshapiro { 88364562Sgshapiro hp = sm_gethostbyname(at, addr.sa.sa_family); 88464562Sgshapiro if (hp == NULL) 88564562Sgshapiro { 88664562Sgshapiro save_errno = errno; 88764562Sgshapiro if (tTd(64, 5)) 88890792Sgshapiro sm_dprintf("X%s: Unknown host name %s\n", 88990792Sgshapiro m->mf_name, at); 89064562Sgshapiro errno = save_errno; 89164562Sgshapiro if (parseonly) 89264562Sgshapiro syserr("X%s: Unknown host name %s", 89364562Sgshapiro m->mf_name, at); 894110560Sgshapiro else if (MilterLogLevel > 0) 89564562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 89690792Sgshapiro "Milter (%s): Unknown host name %s", 89764562Sgshapiro m->mf_name, at); 89890792Sgshapiro milter_error(m, e); 89964562Sgshapiro return -1; 90064562Sgshapiro } 90164562Sgshapiro addr.sa.sa_family = hp->h_addrtype; 90264562Sgshapiro switch (hp->h_addrtype) 90364562Sgshapiro { 90464562Sgshapiro# if NETINET 90564562Sgshapiro case AF_INET: 90664562Sgshapiro memmove(&addr.sin.sin_addr, 90790792Sgshapiro hp->h_addr, INADDRSZ); 90864562Sgshapiro addr.sin.sin_port = port; 90964562Sgshapiro addrlen = sizeof (struct sockaddr_in); 91064562Sgshapiro addrno = 1; 91164562Sgshapiro break; 91264562Sgshapiro# endif /* NETINET */ 91364562Sgshapiro 91464562Sgshapiro# if NETINET6 91564562Sgshapiro case AF_INET6: 91664562Sgshapiro memmove(&addr.sin6.sin6_addr, 91790792Sgshapiro hp->h_addr, IN6ADDRSZ); 91864562Sgshapiro addr.sin6.sin6_port = port; 91964562Sgshapiro addrlen = sizeof (struct sockaddr_in6); 92064562Sgshapiro addrno = 1; 92164562Sgshapiro break; 92264562Sgshapiro# endif /* NETINET6 */ 92364562Sgshapiro 92464562Sgshapiro default: 92564562Sgshapiro if (tTd(64, 5)) 92690792Sgshapiro sm_dprintf("X%s: Unknown protocol for %s (%d)\n", 92790792Sgshapiro m->mf_name, at, 92890792Sgshapiro hp->h_addrtype); 92964562Sgshapiro if (parseonly) 93064562Sgshapiro syserr("X%s: Unknown protocol for %s (%d)", 93164562Sgshapiro m->mf_name, at, hp->h_addrtype); 932110560Sgshapiro else if (MilterLogLevel > 0) 93364562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 93490792Sgshapiro "Milter (%s): Unknown protocol for %s (%d)", 93564562Sgshapiro m->mf_name, at, 93664562Sgshapiro hp->h_addrtype); 93790792Sgshapiro milter_error(m, e); 93890792Sgshapiro# if NETINET6 93971345Sgshapiro freehostent(hp); 94090792Sgshapiro# endif /* NETINET6 */ 94164562Sgshapiro return -1; 94264562Sgshapiro } 94364562Sgshapiro } 94464562Sgshapiro } 94564562Sgshapiro else 94664562Sgshapiro# endif /* NETINET || NETINET6 */ 94764562Sgshapiro { 94864562Sgshapiro if (tTd(64, 5)) 94990792Sgshapiro sm_dprintf("X%s: unknown socket protocol\n", 95090792Sgshapiro m->mf_name); 95164562Sgshapiro if (parseonly) 95264562Sgshapiro syserr("X%s: unknown socket protocol", m->mf_name); 953110560Sgshapiro else if (MilterLogLevel > 0) 95464562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 95590792Sgshapiro "Milter (%s): unknown socket protocol", 95690792Sgshapiro m->mf_name); 95790792Sgshapiro milter_error(m, e); 95864562Sgshapiro return -1; 95964562Sgshapiro } 96064562Sgshapiro 96164562Sgshapiro /* just parsing through? */ 96264562Sgshapiro if (parseonly) 96364562Sgshapiro { 96464562Sgshapiro m->mf_state = SMFS_READY; 96590792Sgshapiro# if NETINET6 96671345Sgshapiro if (hp != NULL) 96771345Sgshapiro freehostent(hp); 96890792Sgshapiro# endif /* NETINET6 */ 96964562Sgshapiro return 0; 97064562Sgshapiro } 97164562Sgshapiro 97264562Sgshapiro /* sanity check */ 97364562Sgshapiro if (m->mf_state != SMFS_READY && 97464562Sgshapiro m->mf_state != SMFS_CLOSED) 97564562Sgshapiro { 97664562Sgshapiro /* shouldn't happen */ 97764562Sgshapiro if (tTd(64, 1)) 97890792Sgshapiro sm_dprintf("Milter (%s): Trying to open filter in state %c\n", 97990792Sgshapiro m->mf_name, (char) m->mf_state); 98090792Sgshapiro milter_error(m, e); 98190792Sgshapiro# if NETINET6 98271345Sgshapiro if (hp != NULL) 98371345Sgshapiro freehostent(hp); 98490792Sgshapiro# endif /* NETINET6 */ 98564562Sgshapiro return -1; 98664562Sgshapiro } 98764562Sgshapiro 98864562Sgshapiro /* nope, actually connecting */ 98964562Sgshapiro for (;;) 99064562Sgshapiro { 99164562Sgshapiro sock = socket(addr.sa.sa_family, SOCK_STREAM, 0); 99264562Sgshapiro if (sock < 0) 99364562Sgshapiro { 99464562Sgshapiro save_errno = errno; 99564562Sgshapiro if (tTd(64, 5)) 99690792Sgshapiro sm_dprintf("Milter (%s): error creating socket: %s\n", 99790792Sgshapiro m->mf_name, 99890792Sgshapiro sm_errstring(save_errno)); 99990792Sgshapiro if (MilterLogLevel > 0) 100064562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 100190792Sgshapiro "Milter (%s): error creating socket: %s", 100290792Sgshapiro m->mf_name, sm_errstring(save_errno)); 100390792Sgshapiro milter_error(m, e); 100490792Sgshapiro# if NETINET6 100571345Sgshapiro if (hp != NULL) 100671345Sgshapiro freehostent(hp); 100790792Sgshapiro# endif /* NETINET6 */ 100864562Sgshapiro return -1; 100964562Sgshapiro } 101064562Sgshapiro 101180785Sgshapiro if (setjmp(MilterConnectTimeout) == 0) 101280785Sgshapiro { 101390792Sgshapiro SM_EVENT *ev = NULL; 101480785Sgshapiro int i; 101564562Sgshapiro 101680785Sgshapiro if (m->mf_timeout[SMFTO_CONNECT] > 0) 101790792Sgshapiro ev = sm_setevent(m->mf_timeout[SMFTO_CONNECT], 101890792Sgshapiro milter_connect_timeout, 0); 101980785Sgshapiro 102080785Sgshapiro i = connect(sock, (struct sockaddr *) &addr, addrlen); 102180785Sgshapiro save_errno = errno; 102280785Sgshapiro if (ev != NULL) 102390792Sgshapiro sm_clrevent(ev); 102480785Sgshapiro errno = save_errno; 102580785Sgshapiro if (i >= 0) 102680785Sgshapiro break; 102780785Sgshapiro } 102880785Sgshapiro 102964562Sgshapiro /* couldn't connect.... try next address */ 103064562Sgshapiro save_errno = errno; 103166494Sgshapiro p = CurHostName; 103266494Sgshapiro CurHostName = at; 103364562Sgshapiro if (tTd(64, 5)) 103490792Sgshapiro sm_dprintf("milter_open (%s): open %s failed: %s\n", 103590792Sgshapiro m->mf_name, at, sm_errstring(save_errno)); 103690792Sgshapiro if (MilterLogLevel > 13) 103764562Sgshapiro sm_syslog(LOG_INFO, e->e_id, 103890792Sgshapiro "Milter (%s): open %s failed: %s", 103990792Sgshapiro m->mf_name, at, sm_errstring(save_errno)); 104066494Sgshapiro CurHostName = p; 104164562Sgshapiro (void) close(sock); 104264562Sgshapiro 104364562Sgshapiro /* try next address */ 104464562Sgshapiro if (hp != NULL && hp->h_addr_list[addrno] != NULL) 104564562Sgshapiro { 104664562Sgshapiro switch (addr.sa.sa_family) 104764562Sgshapiro { 104864562Sgshapiro# if NETINET 104964562Sgshapiro case AF_INET: 105064562Sgshapiro memmove(&addr.sin.sin_addr, 105164562Sgshapiro hp->h_addr_list[addrno++], 105264562Sgshapiro INADDRSZ); 105364562Sgshapiro break; 105464562Sgshapiro# endif /* NETINET */ 105564562Sgshapiro 105664562Sgshapiro# if NETINET6 105764562Sgshapiro case AF_INET6: 105864562Sgshapiro memmove(&addr.sin6.sin6_addr, 105964562Sgshapiro hp->h_addr_list[addrno++], 106064562Sgshapiro IN6ADDRSZ); 106164562Sgshapiro break; 106264562Sgshapiro# endif /* NETINET6 */ 106364562Sgshapiro 106464562Sgshapiro default: 106564562Sgshapiro if (tTd(64, 5)) 106690792Sgshapiro sm_dprintf("X%s: Unknown protocol for %s (%d)\n", 106790792Sgshapiro m->mf_name, at, 106890792Sgshapiro hp->h_addrtype); 106990792Sgshapiro if (MilterLogLevel > 0) 107064562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 107190792Sgshapiro "Milter (%s): Unknown protocol for %s (%d)", 107264562Sgshapiro m->mf_name, at, 107364562Sgshapiro hp->h_addrtype); 107490792Sgshapiro milter_error(m, e); 107590792Sgshapiro# if NETINET6 107671345Sgshapiro freehostent(hp); 107790792Sgshapiro# endif /* NETINET6 */ 107864562Sgshapiro return -1; 107964562Sgshapiro } 108064562Sgshapiro continue; 108164562Sgshapiro } 108280785Sgshapiro p = CurHostName; 108380785Sgshapiro CurHostName = at; 108464562Sgshapiro if (tTd(64, 5)) 108590792Sgshapiro sm_dprintf("X%s: error connecting to filter: %s\n", 108690792Sgshapiro m->mf_name, sm_errstring(save_errno)); 108790792Sgshapiro if (MilterLogLevel > 0) 108864562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 108990792Sgshapiro "Milter (%s): error connecting to filter: %s", 109090792Sgshapiro m->mf_name, sm_errstring(save_errno)); 109180785Sgshapiro CurHostName = p; 109290792Sgshapiro milter_error(m, e); 109390792Sgshapiro# if NETINET6 109471345Sgshapiro if (hp != NULL) 109571345Sgshapiro freehostent(hp); 109690792Sgshapiro# endif /* NETINET6 */ 109764562Sgshapiro return -1; 109864562Sgshapiro } 109964562Sgshapiro m->mf_state = SMFS_OPEN; 110090792Sgshapiro# if NETINET6 110171345Sgshapiro if (hp != NULL) 110271345Sgshapiro { 110371345Sgshapiro freehostent(hp); 110471345Sgshapiro hp = NULL; 110571345Sgshapiro } 110690792Sgshapiro# endif /* NETINET6 */ 110764562Sgshapiro return sock; 110864562Sgshapiro} 110980785Sgshapiro 111080785Sgshapirostatic void 111180785Sgshapiromilter_connect_timeout() 111280785Sgshapiro{ 111380785Sgshapiro /* 111480785Sgshapiro ** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD 111580785Sgshapiro ** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE 111680785Sgshapiro ** DOING. 111780785Sgshapiro */ 111880785Sgshapiro 111980785Sgshapiro errno = ETIMEDOUT; 112080785Sgshapiro longjmp(MilterConnectTimeout, 1); 112180785Sgshapiro} 112290792Sgshapiro/* 112364562Sgshapiro** MILTER_SETUP -- setup structure for a mail filter 112464562Sgshapiro** 112564562Sgshapiro** Parameters: 112664562Sgshapiro** line -- the options line. 112764562Sgshapiro** 112864562Sgshapiro** Returns: 112964562Sgshapiro** none 113064562Sgshapiro*/ 113164562Sgshapiro 113264562Sgshapirovoid 113364562Sgshapiromilter_setup(line) 113464562Sgshapiro char *line; 113564562Sgshapiro{ 113664562Sgshapiro char fcode; 113764562Sgshapiro register char *p; 113864562Sgshapiro register struct milter *m; 113964562Sgshapiro STAB *s; 114064562Sgshapiro 114166494Sgshapiro /* collect the filter name */ 114264562Sgshapiro for (p = line; 114364562Sgshapiro *p != '\0' && *p != ',' && !(isascii(*p) && isspace(*p)); 114464562Sgshapiro p++) 114564562Sgshapiro continue; 114664562Sgshapiro if (*p != '\0') 114764562Sgshapiro *p++ = '\0'; 114864562Sgshapiro if (line[0] == '\0') 114964562Sgshapiro { 115064562Sgshapiro syserr("name required for mail filter"); 115164562Sgshapiro return; 115264562Sgshapiro } 115390792Sgshapiro m = (struct milter *) xalloc(sizeof *m); 115464562Sgshapiro memset((char *) m, '\0', sizeof *m); 115564562Sgshapiro m->mf_name = newstr(line); 115664562Sgshapiro m->mf_state = SMFS_READY; 115764562Sgshapiro m->mf_sock = -1; 115890792Sgshapiro m->mf_timeout[SMFTO_CONNECT] = (time_t) 300; 115964562Sgshapiro m->mf_timeout[SMFTO_WRITE] = (time_t) 10; 116064562Sgshapiro m->mf_timeout[SMFTO_READ] = (time_t) 10; 116164562Sgshapiro m->mf_timeout[SMFTO_EOM] = (time_t) 300; 116264562Sgshapiro 116364562Sgshapiro /* now scan through and assign info from the fields */ 116464562Sgshapiro while (*p != '\0') 116564562Sgshapiro { 116664562Sgshapiro char *delimptr; 116764562Sgshapiro 116864562Sgshapiro while (*p != '\0' && 116964562Sgshapiro (*p == ',' || (isascii(*p) && isspace(*p)))) 117064562Sgshapiro p++; 117164562Sgshapiro 117264562Sgshapiro /* p now points to field code */ 117364562Sgshapiro fcode = *p; 117464562Sgshapiro while (*p != '\0' && *p != '=' && *p != ',') 117564562Sgshapiro p++; 117664562Sgshapiro if (*p++ != '=') 117764562Sgshapiro { 117864562Sgshapiro syserr("X%s: `=' expected", m->mf_name); 117964562Sgshapiro return; 118064562Sgshapiro } 118164562Sgshapiro while (isascii(*p) && isspace(*p)) 118264562Sgshapiro p++; 118364562Sgshapiro 118464562Sgshapiro /* p now points to the field body */ 118564562Sgshapiro p = munchstring(p, &delimptr, ','); 118664562Sgshapiro 118766494Sgshapiro /* install the field into the filter struct */ 118864562Sgshapiro switch (fcode) 118964562Sgshapiro { 119064562Sgshapiro case 'S': /* socket */ 119164562Sgshapiro if (p == NULL) 119264562Sgshapiro m->mf_conn = NULL; 119364562Sgshapiro else 119464562Sgshapiro m->mf_conn = newstr(p); 119564562Sgshapiro break; 119664562Sgshapiro 119764562Sgshapiro case 'F': /* Milter flags configured on MTA */ 119864562Sgshapiro for (; *p != '\0'; p++) 119964562Sgshapiro { 120064562Sgshapiro if (!(isascii(*p) && isspace(*p))) 120171345Sgshapiro setbitn(bitidx(*p), m->mf_flags); 120264562Sgshapiro } 120364562Sgshapiro break; 120464562Sgshapiro 120564562Sgshapiro case 'T': /* timeouts */ 120664562Sgshapiro milter_parse_timeouts(p, m); 120764562Sgshapiro break; 120864562Sgshapiro 120964562Sgshapiro default: 121064562Sgshapiro syserr("X%s: unknown filter equate %c=", 121164562Sgshapiro m->mf_name, fcode); 121264562Sgshapiro break; 121364562Sgshapiro } 121464562Sgshapiro p = delimptr; 121564562Sgshapiro } 121664562Sgshapiro 121764562Sgshapiro /* early check for errors */ 121890792Sgshapiro (void) milter_open(m, true, CurEnv); 121964562Sgshapiro 122066494Sgshapiro /* enter the filter into the symbol table */ 122164562Sgshapiro s = stab(m->mf_name, ST_MILTER, ST_ENTER); 122264562Sgshapiro if (s->s_milter != NULL) 122364562Sgshapiro syserr("X%s: duplicate filter definition", m->mf_name); 122464562Sgshapiro else 122564562Sgshapiro s->s_milter = m; 122664562Sgshapiro} 122790792Sgshapiro/* 122890792Sgshapiro** MILTER_CONFIG -- parse option list into an array and check config 122964562Sgshapiro** 123064562Sgshapiro** Called when reading configuration file. 123164562Sgshapiro** 123264562Sgshapiro** Parameters: 123364562Sgshapiro** spec -- the filter list. 123464562Sgshapiro** list -- the array to fill in. 123564562Sgshapiro** max -- the maximum number of entries in list. 123664562Sgshapiro** 123764562Sgshapiro** Returns: 123864562Sgshapiro** none 123964562Sgshapiro*/ 124064562Sgshapiro 124164562Sgshapirovoid 124290792Sgshapiromilter_config(spec, list, max) 124364562Sgshapiro char *spec; 124464562Sgshapiro struct milter **list; 124564562Sgshapiro int max; 124664562Sgshapiro{ 124764562Sgshapiro int numitems = 0; 124864562Sgshapiro register char *p; 124964562Sgshapiro 125064562Sgshapiro /* leave one for the NULL signifying the end of the list */ 125164562Sgshapiro max--; 125264562Sgshapiro 125364562Sgshapiro for (p = spec; p != NULL; ) 125464562Sgshapiro { 125564562Sgshapiro STAB *s; 125664562Sgshapiro 125764562Sgshapiro while (isascii(*p) && isspace(*p)) 125864562Sgshapiro p++; 125964562Sgshapiro if (*p == '\0') 126064562Sgshapiro break; 126164562Sgshapiro spec = p; 126264562Sgshapiro 126364562Sgshapiro if (numitems >= max) 126464562Sgshapiro { 126564562Sgshapiro syserr("Too many filters defined, %d max", max); 126664562Sgshapiro if (max > 0) 126764562Sgshapiro list[0] = NULL; 126864562Sgshapiro return; 126964562Sgshapiro } 127090792Sgshapiro#if _FFR_MILTER_PERDAEMON 127190792Sgshapiro p = strpbrk(p, ";,"); 127290792Sgshapiro#else /* _FFR_MILTER_PERDAEMON */ 127364562Sgshapiro p = strpbrk(p, ","); 127490792Sgshapiro#endif /* _FFR_MILTER_PERDAEMON */ 127564562Sgshapiro if (p != NULL) 127664562Sgshapiro *p++ = '\0'; 127764562Sgshapiro 127864562Sgshapiro s = stab(spec, ST_MILTER, ST_FIND); 127964562Sgshapiro if (s == NULL) 128064562Sgshapiro { 128164562Sgshapiro syserr("InputFilter %s not defined", spec); 128264562Sgshapiro ExitStat = EX_CONFIG; 128364562Sgshapiro return; 128464562Sgshapiro } 128564562Sgshapiro list[numitems++] = s->s_milter; 128664562Sgshapiro } 128764562Sgshapiro list[numitems] = NULL; 128890792Sgshapiro 128990792Sgshapiro /* if not set, set to LogLevel */ 129090792Sgshapiro if (MilterLogLevel == -1) 129190792Sgshapiro MilterLogLevel = LogLevel; 129264562Sgshapiro} 129390792Sgshapiro/* 129464562Sgshapiro** MILTER_PARSE_TIMEOUTS -- parse timeout list 129564562Sgshapiro** 129664562Sgshapiro** Called when reading configuration file. 129764562Sgshapiro** 129864562Sgshapiro** Parameters: 129964562Sgshapiro** spec -- the timeout list. 130064562Sgshapiro** m -- milter to set. 130164562Sgshapiro** 130264562Sgshapiro** Returns: 130364562Sgshapiro** none 130464562Sgshapiro*/ 130564562Sgshapiro 130664562Sgshapirostatic void 130764562Sgshapiromilter_parse_timeouts(spec, m) 130864562Sgshapiro char *spec; 130964562Sgshapiro struct milter *m; 131064562Sgshapiro{ 131164562Sgshapiro char fcode; 131264562Sgshapiro register char *p; 131364562Sgshapiro 131464562Sgshapiro p = spec; 131564562Sgshapiro 131664562Sgshapiro /* now scan through and assign info from the fields */ 131764562Sgshapiro while (*p != '\0') 131864562Sgshapiro { 131964562Sgshapiro char *delimptr; 132064562Sgshapiro 132164562Sgshapiro while (*p != '\0' && 132264562Sgshapiro (*p == ';' || (isascii(*p) && isspace(*p)))) 132364562Sgshapiro p++; 132464562Sgshapiro 132564562Sgshapiro /* p now points to field code */ 132664562Sgshapiro fcode = *p; 132764562Sgshapiro while (*p != '\0' && *p != ':') 132864562Sgshapiro p++; 132964562Sgshapiro if (*p++ != ':') 133064562Sgshapiro { 133164562Sgshapiro syserr("X%s, T=: `:' expected", m->mf_name); 133264562Sgshapiro return; 133364562Sgshapiro } 133464562Sgshapiro while (isascii(*p) && isspace(*p)) 133564562Sgshapiro p++; 133664562Sgshapiro 133764562Sgshapiro /* p now points to the field body */ 133864562Sgshapiro p = munchstring(p, &delimptr, ';'); 133964562Sgshapiro 134066494Sgshapiro /* install the field into the filter struct */ 134164562Sgshapiro switch (fcode) 134264562Sgshapiro { 134382017Sgshapiro case 'C': 134482017Sgshapiro m->mf_timeout[SMFTO_CONNECT] = convtime(p, 's'); 134582017Sgshapiro if (tTd(64, 5)) 134690792Sgshapiro sm_dprintf("X%s: %c=%lu\n", 134790792Sgshapiro m->mf_name, fcode, 134890792Sgshapiro (unsigned long) m->mf_timeout[SMFTO_CONNECT]); 134982017Sgshapiro break; 135082017Sgshapiro 135164562Sgshapiro case 'S': 135264562Sgshapiro m->mf_timeout[SMFTO_WRITE] = convtime(p, 's'); 135364562Sgshapiro if (tTd(64, 5)) 135490792Sgshapiro sm_dprintf("X%s: %c=%lu\n", 135590792Sgshapiro m->mf_name, fcode, 135690792Sgshapiro (unsigned long) m->mf_timeout[SMFTO_WRITE]); 135764562Sgshapiro break; 135864562Sgshapiro 135964562Sgshapiro case 'R': 136064562Sgshapiro m->mf_timeout[SMFTO_READ] = convtime(p, 's'); 136164562Sgshapiro if (tTd(64, 5)) 136290792Sgshapiro sm_dprintf("X%s: %c=%lu\n", 136390792Sgshapiro m->mf_name, fcode, 136490792Sgshapiro (unsigned long) m->mf_timeout[SMFTO_READ]); 136564562Sgshapiro break; 136664562Sgshapiro 136764562Sgshapiro case 'E': 136864562Sgshapiro m->mf_timeout[SMFTO_EOM] = convtime(p, 's'); 136964562Sgshapiro if (tTd(64, 5)) 137090792Sgshapiro sm_dprintf("X%s: %c=%lu\n", 137190792Sgshapiro m->mf_name, fcode, 137290792Sgshapiro (unsigned long) m->mf_timeout[SMFTO_EOM]); 137364562Sgshapiro break; 137464562Sgshapiro 137564562Sgshapiro default: 137664562Sgshapiro if (tTd(64, 5)) 137790792Sgshapiro sm_dprintf("X%s: %c unknown\n", 137890792Sgshapiro m->mf_name, fcode); 137964562Sgshapiro syserr("X%s: unknown filter timeout %c", 138064562Sgshapiro m->mf_name, fcode); 138164562Sgshapiro break; 138264562Sgshapiro } 138364562Sgshapiro p = delimptr; 138464562Sgshapiro } 138564562Sgshapiro} 138690792Sgshapiro/* 138764562Sgshapiro** MILTER_SET_OPTION -- set an individual milter option 138864562Sgshapiro** 138964562Sgshapiro** Parameters: 139064562Sgshapiro** name -- the name of the option. 139164562Sgshapiro** val -- the value of the option. 139264562Sgshapiro** sticky -- if set, don't let other setoptions override 139364562Sgshapiro** this value. 139464562Sgshapiro** 139564562Sgshapiro** Returns: 139664562Sgshapiro** none. 139764562Sgshapiro*/ 139864562Sgshapiro 139964562Sgshapiro/* set if Milter sub-option is stuck */ 140064562Sgshapirostatic BITMAP256 StickyMilterOpt; 140164562Sgshapiro 140264562Sgshapirostatic struct milteropt 140364562Sgshapiro{ 140490792Sgshapiro char *mo_name; /* long name of milter option */ 140590792Sgshapiro unsigned char mo_code; /* code for option */ 140664562Sgshapiro} MilterOptTab[] = 140764562Sgshapiro{ 140864562Sgshapiro# define MO_MACROS_CONNECT 0x01 140964562Sgshapiro { "macros.connect", MO_MACROS_CONNECT }, 141064562Sgshapiro# define MO_MACROS_HELO 0x02 141164562Sgshapiro { "macros.helo", MO_MACROS_HELO }, 141264562Sgshapiro# define MO_MACROS_ENVFROM 0x03 141364562Sgshapiro { "macros.envfrom", MO_MACROS_ENVFROM }, 141464562Sgshapiro# define MO_MACROS_ENVRCPT 0x04 141564562Sgshapiro { "macros.envrcpt", MO_MACROS_ENVRCPT }, 141690792Sgshapiro# define MO_LOGLEVEL 0x05 141790792Sgshapiro { "loglevel", MO_LOGLEVEL }, 141864562Sgshapiro { NULL, 0 }, 141964562Sgshapiro}; 142064562Sgshapiro 142164562Sgshapirovoid 142264562Sgshapiromilter_set_option(name, val, sticky) 142364562Sgshapiro char *name; 142464562Sgshapiro char *val; 142564562Sgshapiro bool sticky; 142664562Sgshapiro{ 142764562Sgshapiro int nummac = 0; 142864562Sgshapiro register struct milteropt *mo; 142964562Sgshapiro char *p; 143064562Sgshapiro char **macros = NULL; 143164562Sgshapiro 143264562Sgshapiro if (tTd(37, 2) || tTd(64, 5)) 143390792Sgshapiro sm_dprintf("milter_set_option(%s = %s)", name, val); 143464562Sgshapiro 143598841Sgshapiro if (name == NULL) 143698841Sgshapiro { 143798841Sgshapiro syserr("milter_set_option: invalid Milter option, must specify suboption"); 143898841Sgshapiro return; 143998841Sgshapiro } 144098841Sgshapiro 144164562Sgshapiro for (mo = MilterOptTab; mo->mo_name != NULL; mo++) 144264562Sgshapiro { 144390792Sgshapiro if (sm_strcasecmp(mo->mo_name, name) == 0) 144464562Sgshapiro break; 144564562Sgshapiro } 144664562Sgshapiro 144764562Sgshapiro if (mo->mo_name == NULL) 144890792Sgshapiro { 144964562Sgshapiro syserr("milter_set_option: invalid Milter option %s", name); 145090792Sgshapiro return; 145190792Sgshapiro } 145264562Sgshapiro 145364562Sgshapiro /* 145464562Sgshapiro ** See if this option is preset for us. 145564562Sgshapiro */ 145664562Sgshapiro 145764562Sgshapiro if (!sticky && bitnset(mo->mo_code, StickyMilterOpt)) 145864562Sgshapiro { 145964562Sgshapiro if (tTd(37, 2) || tTd(64,5)) 146090792Sgshapiro sm_dprintf(" (ignored)\n"); 146164562Sgshapiro return; 146264562Sgshapiro } 146364562Sgshapiro 146464562Sgshapiro if (tTd(37, 2) || tTd(64,5)) 146590792Sgshapiro sm_dprintf("\n"); 146664562Sgshapiro 146764562Sgshapiro switch (mo->mo_code) 146864562Sgshapiro { 146990792Sgshapiro case MO_LOGLEVEL: 147090792Sgshapiro MilterLogLevel = atoi(val); 147190792Sgshapiro break; 147290792Sgshapiro 147364562Sgshapiro case MO_MACROS_CONNECT: 147464562Sgshapiro if (macros == NULL) 147564562Sgshapiro macros = MilterConnectMacros; 147664562Sgshapiro /* FALLTHROUGH */ 147764562Sgshapiro 147864562Sgshapiro case MO_MACROS_HELO: 147964562Sgshapiro if (macros == NULL) 148064562Sgshapiro macros = MilterHeloMacros; 148164562Sgshapiro /* FALLTHROUGH */ 148264562Sgshapiro 148364562Sgshapiro case MO_MACROS_ENVFROM: 148464562Sgshapiro if (macros == NULL) 148564562Sgshapiro macros = MilterEnvFromMacros; 148664562Sgshapiro /* FALLTHROUGH */ 148764562Sgshapiro 148864562Sgshapiro case MO_MACROS_ENVRCPT: 148964562Sgshapiro if (macros == NULL) 149064562Sgshapiro macros = MilterEnvRcptMacros; 149164562Sgshapiro 149264562Sgshapiro p = newstr(val); 149364562Sgshapiro while (*p != '\0') 149464562Sgshapiro { 149564562Sgshapiro char *macro; 149664562Sgshapiro 149764562Sgshapiro /* Skip leading commas, spaces */ 149864562Sgshapiro while (*p != '\0' && 149964562Sgshapiro (*p == ',' || (isascii(*p) && isspace(*p)))) 150064562Sgshapiro p++; 150164562Sgshapiro 150264562Sgshapiro if (*p == '\0') 150364562Sgshapiro break; 150464562Sgshapiro 150564562Sgshapiro /* Find end of macro */ 150664562Sgshapiro macro = p; 150764562Sgshapiro while (*p != '\0' && *p != ',' && 150864562Sgshapiro isascii(*p) && !isspace(*p)) 150964562Sgshapiro p++; 151064562Sgshapiro if (*p != '\0') 151164562Sgshapiro *p++ = '\0'; 151264562Sgshapiro 151364562Sgshapiro if (nummac >= MAXFILTERMACROS) 151464562Sgshapiro { 151564562Sgshapiro syserr("milter_set_option: too many macros in Milter.%s (max %d)", 151664562Sgshapiro name, MAXFILTERMACROS); 151764562Sgshapiro macros[nummac] = NULL; 151864562Sgshapiro break; 151964562Sgshapiro } 152064562Sgshapiro macros[nummac++] = macro; 152164562Sgshapiro } 152264562Sgshapiro macros[nummac] = NULL; 152364562Sgshapiro break; 152464562Sgshapiro 152564562Sgshapiro default: 152664562Sgshapiro syserr("milter_set_option: invalid Milter option %s", name); 152764562Sgshapiro break; 152864562Sgshapiro } 152964562Sgshapiro if (sticky) 153064562Sgshapiro setbitn(mo->mo_code, StickyMilterOpt); 153164562Sgshapiro} 153290792Sgshapiro/* 153390792Sgshapiro** MILTER_REOPEN_DF -- open & truncate the data file (for replbody) 153464562Sgshapiro** 153564562Sgshapiro** Parameters: 153664562Sgshapiro** e -- current envelope. 153764562Sgshapiro** 153864562Sgshapiro** Returns: 153964562Sgshapiro** 0 if succesful, -1 otherwise 154064562Sgshapiro*/ 154164562Sgshapiro 154264562Sgshapirostatic int 154364562Sgshapiromilter_reopen_df(e) 154464562Sgshapiro ENVELOPE *e; 154564562Sgshapiro{ 154664562Sgshapiro char dfname[MAXPATHLEN]; 154764562Sgshapiro 154890792Sgshapiro (void) sm_strlcpy(dfname, queuename(e, DATAFL_LETTER), sizeof dfname); 154964562Sgshapiro 155064562Sgshapiro /* 155190792Sgshapiro ** In SuperSafe == SAFE_REALLY mode, e->e_dfp is a read-only FP so 155264562Sgshapiro ** close and reopen writable (later close and reopen 155364562Sgshapiro ** read only again). 155464562Sgshapiro ** 155590792Sgshapiro ** In SuperSafe != SAFE_REALLY mode, e->e_dfp still points at the 155664562Sgshapiro ** buffered file I/O descriptor, still open for writing 155764562Sgshapiro ** so there isn't as much work to do, just truncate it 155864562Sgshapiro ** and go. 155964562Sgshapiro */ 156064562Sgshapiro 156190792Sgshapiro if (SuperSafe == SAFE_REALLY) 156264562Sgshapiro { 156390792Sgshapiro /* close read-only data file */ 156464562Sgshapiro if (bitset(EF_HAS_DF, e->e_flags) && e->e_dfp != NULL) 156564562Sgshapiro { 156690792Sgshapiro (void) sm_io_close(e->e_dfp, SM_TIME_DEFAULT); 156764562Sgshapiro e->e_flags &= ~EF_HAS_DF; 156864562Sgshapiro } 156964562Sgshapiro 157064562Sgshapiro /* open writable */ 157190792Sgshapiro if ((e->e_dfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, dfname, 157290792Sgshapiro SM_IO_RDWR, NULL)) == NULL) 157364562Sgshapiro { 157490792Sgshapiro MILTER_DF_ERROR("milter_reopen_df: sm_io_open %s: %s"); 157564562Sgshapiro return -1; 157664562Sgshapiro } 157764562Sgshapiro } 157864562Sgshapiro else if (e->e_dfp == NULL) 157964562Sgshapiro { 158064562Sgshapiro /* shouldn't happen */ 158164562Sgshapiro errno = ENOENT; 158264562Sgshapiro MILTER_DF_ERROR("milter_reopen_df: NULL e_dfp (%s: %s)"); 158364562Sgshapiro return -1; 158464562Sgshapiro } 158564562Sgshapiro return 0; 158664562Sgshapiro} 158790792Sgshapiro/* 158890792Sgshapiro** MILTER_RESET_DF -- re-open read-only the data file (for replbody) 158964562Sgshapiro** 159064562Sgshapiro** Parameters: 159164562Sgshapiro** e -- current envelope. 159264562Sgshapiro** 159364562Sgshapiro** Returns: 159464562Sgshapiro** 0 if succesful, -1 otherwise 159564562Sgshapiro*/ 159664562Sgshapiro 159764562Sgshapirostatic int 159864562Sgshapiromilter_reset_df(e) 159964562Sgshapiro ENVELOPE *e; 160064562Sgshapiro{ 160164562Sgshapiro int afd; 160264562Sgshapiro char dfname[MAXPATHLEN]; 160364562Sgshapiro 160490792Sgshapiro (void) sm_strlcpy(dfname, queuename(e, DATAFL_LETTER), sizeof dfname); 160564562Sgshapiro 160690792Sgshapiro if (sm_io_flush(e->e_dfp, SM_TIME_DEFAULT) != 0 || 160790792Sgshapiro sm_io_error(e->e_dfp)) 160864562Sgshapiro { 160964562Sgshapiro MILTER_DF_ERROR("milter_reset_df: error writing/flushing %s: %s"); 161064562Sgshapiro return -1; 161164562Sgshapiro } 161290792Sgshapiro else if (SuperSafe != SAFE_REALLY) 161364562Sgshapiro { 161464562Sgshapiro /* skip next few clauses */ 161564562Sgshapiro /* EMPTY */ 161664562Sgshapiro } 161790792Sgshapiro else if ((afd = sm_io_getinfo(e->e_dfp, SM_IO_WHAT_FD, NULL)) >= 0 161890792Sgshapiro && fsync(afd) < 0) 161964562Sgshapiro { 162064562Sgshapiro MILTER_DF_ERROR("milter_reset_df: error sync'ing %s: %s"); 162164562Sgshapiro return -1; 162264562Sgshapiro } 162390792Sgshapiro else if (sm_io_close(e->e_dfp, SM_TIME_DEFAULT) < 0) 162464562Sgshapiro { 162564562Sgshapiro MILTER_DF_ERROR("milter_reset_df: error closing %s: %s"); 162664562Sgshapiro return -1; 162764562Sgshapiro } 162890792Sgshapiro else if ((e->e_dfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, dfname, 162990792Sgshapiro SM_IO_RDONLY, NULL)) == NULL) 163064562Sgshapiro { 163164562Sgshapiro MILTER_DF_ERROR("milter_reset_df: error reopening %s: %s"); 163264562Sgshapiro return -1; 163364562Sgshapiro } 163464562Sgshapiro else 163564562Sgshapiro e->e_flags |= EF_HAS_DF; 163664562Sgshapiro return 0; 163764562Sgshapiro} 163890792Sgshapiro/* 163964562Sgshapiro** MILTER_CAN_DELRCPTS -- can any milter filters delete recipients? 164064562Sgshapiro** 164164562Sgshapiro** Parameters: 164264562Sgshapiro** none 164364562Sgshapiro** 164464562Sgshapiro** Returns: 164590792Sgshapiro** true if any filter deletes recipients, false otherwise 164664562Sgshapiro*/ 164764562Sgshapiro 164864562Sgshapirobool 164964562Sgshapiromilter_can_delrcpts() 165064562Sgshapiro{ 165190792Sgshapiro bool can = false; 165264562Sgshapiro int i; 165364562Sgshapiro 165464562Sgshapiro if (tTd(64, 10)) 165590792Sgshapiro sm_dprintf("milter_can_delrcpts:"); 165664562Sgshapiro 165764562Sgshapiro for (i = 0; InputFilters[i] != NULL; i++) 165864562Sgshapiro { 165964562Sgshapiro struct milter *m = InputFilters[i]; 166064562Sgshapiro 166164562Sgshapiro if (bitset(SMFIF_DELRCPT, m->mf_fflags)) 166264562Sgshapiro { 166390792Sgshapiro can = true; 166464562Sgshapiro break; 166564562Sgshapiro } 166664562Sgshapiro } 166764562Sgshapiro if (tTd(64, 10)) 166890792Sgshapiro sm_dprintf("%s\n", can ? "true" : "false"); 166964562Sgshapiro 167064562Sgshapiro return can; 167164562Sgshapiro} 167290792Sgshapiro/* 167364562Sgshapiro** MILTER_QUIT_FILTER -- close down a single filter 167464562Sgshapiro** 167564562Sgshapiro** Parameters: 167664562Sgshapiro** m -- milter structure of filter to close down. 167764562Sgshapiro** e -- current envelope. 167864562Sgshapiro** 167964562Sgshapiro** Returns: 168064562Sgshapiro** none 168164562Sgshapiro*/ 168264562Sgshapiro 168364562Sgshapirostatic void 168464562Sgshapiromilter_quit_filter(m, e) 168564562Sgshapiro struct milter *m; 168664562Sgshapiro ENVELOPE *e; 168764562Sgshapiro{ 168864562Sgshapiro if (tTd(64, 10)) 168990792Sgshapiro sm_dprintf("milter_quit_filter(%s)\n", m->mf_name); 169090792Sgshapiro if (MilterLogLevel > 18) 169190792Sgshapiro sm_syslog(LOG_INFO, e->e_id, "Milter (%s): quit filter", 169290792Sgshapiro m->mf_name); 169364562Sgshapiro 169464562Sgshapiro /* Never replace error state */ 169564562Sgshapiro if (m->mf_state == SMFS_ERROR) 169664562Sgshapiro return; 169764562Sgshapiro 169864562Sgshapiro if (m->mf_sock < 0 || 169964562Sgshapiro m->mf_state == SMFS_CLOSED || 170064562Sgshapiro m->mf_state == SMFS_READY) 170164562Sgshapiro { 170264562Sgshapiro m->mf_sock = -1; 170364562Sgshapiro m->mf_state = SMFS_CLOSED; 170464562Sgshapiro return; 170564562Sgshapiro } 170664562Sgshapiro 170764562Sgshapiro (void) milter_write(m, SMFIC_QUIT, (char *) NULL, 0, 170864562Sgshapiro m->mf_timeout[SMFTO_WRITE], e); 170971345Sgshapiro if (m->mf_sock >= 0) 171071345Sgshapiro { 171171345Sgshapiro (void) close(m->mf_sock); 171271345Sgshapiro m->mf_sock = -1; 171371345Sgshapiro } 171464562Sgshapiro if (m->mf_state != SMFS_ERROR) 171564562Sgshapiro m->mf_state = SMFS_CLOSED; 171664562Sgshapiro} 171790792Sgshapiro/* 171864562Sgshapiro** MILTER_ABORT_FILTER -- tell filter to abort current message 171964562Sgshapiro** 172064562Sgshapiro** Parameters: 172164562Sgshapiro** m -- milter structure of filter to abort. 172264562Sgshapiro** e -- current envelope. 172364562Sgshapiro** 172464562Sgshapiro** Returns: 172564562Sgshapiro** none 172664562Sgshapiro*/ 172764562Sgshapiro 172864562Sgshapirostatic void 172964562Sgshapiromilter_abort_filter(m, e) 173064562Sgshapiro struct milter *m; 173164562Sgshapiro ENVELOPE *e; 173264562Sgshapiro{ 173364562Sgshapiro if (tTd(64, 10)) 173490792Sgshapiro sm_dprintf("milter_abort_filter(%s)\n", m->mf_name); 173590792Sgshapiro if (MilterLogLevel > 10) 173690792Sgshapiro sm_syslog(LOG_INFO, e->e_id, "Milter (%s): abort filter", 173790792Sgshapiro m->mf_name); 173864562Sgshapiro 173964562Sgshapiro if (m->mf_sock < 0 || 174064562Sgshapiro m->mf_state != SMFS_INMSG) 174164562Sgshapiro return; 174264562Sgshapiro 174364562Sgshapiro (void) milter_write(m, SMFIC_ABORT, (char *) NULL, 0, 174464562Sgshapiro m->mf_timeout[SMFTO_WRITE], e); 174564562Sgshapiro if (m->mf_state != SMFS_ERROR) 174664562Sgshapiro m->mf_state = SMFS_DONE; 174764562Sgshapiro} 174890792Sgshapiro/* 174964562Sgshapiro** MILTER_SEND_MACROS -- provide macros to the filters 175064562Sgshapiro** 175164562Sgshapiro** Parameters: 175264562Sgshapiro** m -- milter to send macros to. 175364562Sgshapiro** macros -- macros to send for filter smfi_getsymval(). 175464562Sgshapiro** cmd -- which command the macros are associated with. 175564562Sgshapiro** e -- current envelope (for macro access). 175664562Sgshapiro** 175764562Sgshapiro** Returns: 175864562Sgshapiro** none 175964562Sgshapiro*/ 176064562Sgshapiro 176164562Sgshapirostatic void 176264562Sgshapiromilter_send_macros(m, macros, cmd, e) 176364562Sgshapiro struct milter *m; 176464562Sgshapiro char **macros; 176564562Sgshapiro char cmd; 176664562Sgshapiro ENVELOPE *e; 176764562Sgshapiro{ 176864562Sgshapiro int i; 176964562Sgshapiro int mid; 177064562Sgshapiro char *v; 177164562Sgshapiro char *buf, *bp; 177295154Sgshapiro char exp[MAXLINE]; 177364562Sgshapiro ssize_t s; 177464562Sgshapiro 177564562Sgshapiro /* sanity check */ 177664562Sgshapiro if (macros == NULL || macros[0] == NULL) 177764562Sgshapiro return; 177864562Sgshapiro 177964562Sgshapiro /* put together data */ 178064562Sgshapiro s = 1; /* for the command character */ 178164562Sgshapiro for (i = 0; macros[i] != NULL; i++) 178264562Sgshapiro { 178390792Sgshapiro mid = macid(macros[i]); 178471345Sgshapiro if (mid == 0) 178564562Sgshapiro continue; 178664562Sgshapiro v = macvalue(mid, e); 178764562Sgshapiro if (v == NULL) 178864562Sgshapiro continue; 178995154Sgshapiro expand(v, exp, sizeof(exp), e); 179095154Sgshapiro s += strlen(macros[i]) + 1 + strlen(exp) + 1; 179164562Sgshapiro } 179264562Sgshapiro 179390792Sgshapiro if (s < 0) 179490792Sgshapiro return; 179590792Sgshapiro 179690792Sgshapiro buf = (char *) xalloc(s); 179764562Sgshapiro bp = buf; 179864562Sgshapiro *bp++ = cmd; 179964562Sgshapiro for (i = 0; macros[i] != NULL; i++) 180064562Sgshapiro { 180190792Sgshapiro mid = macid(macros[i]); 180271345Sgshapiro if (mid == 0) 180364562Sgshapiro continue; 180464562Sgshapiro v = macvalue(mid, e); 180564562Sgshapiro if (v == NULL) 180664562Sgshapiro continue; 180795154Sgshapiro expand(v, exp, sizeof(exp), e); 180864562Sgshapiro 180964562Sgshapiro if (tTd(64, 10)) 181090792Sgshapiro sm_dprintf("milter_send_macros(%s, %c): %s=%s\n", 181195154Sgshapiro m->mf_name, cmd, macros[i], exp); 181264562Sgshapiro 181390792Sgshapiro (void) sm_strlcpy(bp, macros[i], s - (bp - buf)); 181464562Sgshapiro bp += strlen(bp) + 1; 181595154Sgshapiro (void) sm_strlcpy(bp, exp, s - (bp - buf)); 181664562Sgshapiro bp += strlen(bp) + 1; 181764562Sgshapiro } 181864562Sgshapiro (void) milter_write(m, SMFIC_MACRO, buf, s, 181964562Sgshapiro m->mf_timeout[SMFTO_WRITE], e); 182090792Sgshapiro sm_free(buf); /* XXX */ 182164562Sgshapiro} 182264562Sgshapiro 182390792Sgshapiro/* 182464562Sgshapiro** MILTER_SEND_COMMAND -- send a command and return the response for a filter 182564562Sgshapiro** 182664562Sgshapiro** Parameters: 182764562Sgshapiro** m -- current milter filter 182864562Sgshapiro** command -- command to send. 182964562Sgshapiro** data -- optional command data. 183064562Sgshapiro** sz -- length of buf. 183164562Sgshapiro** e -- current envelope (for e->e_id). 183264562Sgshapiro** state -- return state word. 183364562Sgshapiro** 183464562Sgshapiro** Returns: 183564562Sgshapiro** response string (may be NULL) 183664562Sgshapiro*/ 183764562Sgshapiro 183864562Sgshapirostatic char * 183964562Sgshapiromilter_send_command(m, command, data, sz, e, state) 184064562Sgshapiro struct milter *m; 184164562Sgshapiro char command; 184264562Sgshapiro void *data; 184364562Sgshapiro ssize_t sz; 184464562Sgshapiro ENVELOPE *e; 184564562Sgshapiro char *state; 184664562Sgshapiro{ 184764562Sgshapiro char rcmd; 184864562Sgshapiro ssize_t rlen; 184990792Sgshapiro unsigned long skipflag; 185090792Sgshapiro char *action; 185164562Sgshapiro char *defresponse; 185264562Sgshapiro char *response; 185364562Sgshapiro 185464562Sgshapiro if (tTd(64, 10)) 185590792Sgshapiro sm_dprintf("milter_send_command(%s): cmd %c len %ld\n", 185664562Sgshapiro m->mf_name, (char) command, (long) sz); 185764562Sgshapiro 185864562Sgshapiro /* find skip flag and default failure */ 185964562Sgshapiro switch (command) 186064562Sgshapiro { 186164562Sgshapiro case SMFIC_CONNECT: 186264562Sgshapiro skipflag = SMFIP_NOCONNECT; 186390792Sgshapiro action = "connect"; 186464562Sgshapiro defresponse = "554 Command rejected"; 186564562Sgshapiro break; 186664562Sgshapiro 186764562Sgshapiro case SMFIC_HELO: 186864562Sgshapiro skipflag = SMFIP_NOHELO; 186990792Sgshapiro action = "helo"; 187064562Sgshapiro defresponse = "550 Command rejected"; 187164562Sgshapiro break; 187264562Sgshapiro 187364562Sgshapiro case SMFIC_MAIL: 187464562Sgshapiro skipflag = SMFIP_NOMAIL; 187590792Sgshapiro action = "mail"; 187664562Sgshapiro defresponse = "550 5.7.1 Command rejected"; 187764562Sgshapiro break; 187864562Sgshapiro 187964562Sgshapiro case SMFIC_RCPT: 188064562Sgshapiro skipflag = SMFIP_NORCPT; 188190792Sgshapiro action = "rcpt"; 188264562Sgshapiro defresponse = "550 5.7.1 Command rejected"; 188364562Sgshapiro break; 188464562Sgshapiro 188564562Sgshapiro case SMFIC_HEADER: 188664562Sgshapiro skipflag = SMFIP_NOHDRS; 188790792Sgshapiro action = "header"; 188864562Sgshapiro defresponse = "550 5.7.1 Command rejected"; 188964562Sgshapiro break; 189064562Sgshapiro 189164562Sgshapiro case SMFIC_BODY: 189264562Sgshapiro skipflag = SMFIP_NOBODY; 189390792Sgshapiro action = "body"; 189464562Sgshapiro defresponse = "554 5.7.1 Command rejected"; 189564562Sgshapiro break; 189664562Sgshapiro 189764562Sgshapiro case SMFIC_EOH: 189864562Sgshapiro skipflag = SMFIP_NOEOH; 189990792Sgshapiro action = "eoh"; 190064562Sgshapiro defresponse = "550 5.7.1 Command rejected"; 190164562Sgshapiro break; 190264562Sgshapiro 190364562Sgshapiro case SMFIC_BODYEOB: 190464562Sgshapiro case SMFIC_OPTNEG: 190564562Sgshapiro case SMFIC_MACRO: 190664562Sgshapiro case SMFIC_ABORT: 190764562Sgshapiro case SMFIC_QUIT: 190864562Sgshapiro /* NOTE: not handled by milter_send_command() */ 190964562Sgshapiro /* FALLTHROUGH */ 191064562Sgshapiro 191164562Sgshapiro default: 191264562Sgshapiro skipflag = 0; 191390792Sgshapiro action = "default"; 191464562Sgshapiro defresponse = "550 5.7.1 Command rejected"; 191564562Sgshapiro break; 191664562Sgshapiro } 191764562Sgshapiro 191864562Sgshapiro /* check if filter wants this command */ 191964562Sgshapiro if (skipflag != 0 && 192064562Sgshapiro bitset(skipflag, m->mf_pflags)) 192164562Sgshapiro return NULL; 192264562Sgshapiro 192390792Sgshapiro /* send the command to the filter */ 192464562Sgshapiro (void) milter_write(m, command, data, sz, 192564562Sgshapiro m->mf_timeout[SMFTO_WRITE], e); 192664562Sgshapiro if (m->mf_state == SMFS_ERROR) 192764562Sgshapiro { 1928112810Sgshapiro MILTER_CHECK_ERROR(false, return NULL); 192964562Sgshapiro return NULL; 193064562Sgshapiro } 193164562Sgshapiro 193290792Sgshapiro /* get the response from the filter */ 193364562Sgshapiro response = milter_read(m, &rcmd, &rlen, 193464562Sgshapiro m->mf_timeout[SMFTO_READ], e); 193564562Sgshapiro if (m->mf_state == SMFS_ERROR) 193664562Sgshapiro { 1937112810Sgshapiro MILTER_CHECK_ERROR(false, return NULL); 193864562Sgshapiro return NULL; 193964562Sgshapiro } 194064562Sgshapiro 194164562Sgshapiro if (tTd(64, 10)) 194290792Sgshapiro sm_dprintf("milter_send_command(%s): returned %c\n", 194390792Sgshapiro m->mf_name, (char) rcmd); 194464562Sgshapiro 194564562Sgshapiro switch (rcmd) 194664562Sgshapiro { 194764562Sgshapiro case SMFIR_REPLYCODE: 194864562Sgshapiro MILTER_CHECK_REPLYCODE(defresponse); 194990792Sgshapiro if (MilterLogLevel > 10) 195090792Sgshapiro sm_syslog(LOG_INFO, e->e_id, "milter=%s, action=%s, reject=%s", 195190792Sgshapiro m->mf_name, action, response); 195290792Sgshapiro *state = rcmd; 195390792Sgshapiro break; 195464562Sgshapiro 195564562Sgshapiro case SMFIR_REJECT: 195690792Sgshapiro if (MilterLogLevel > 10) 195790792Sgshapiro sm_syslog(LOG_INFO, e->e_id, "milter=%s, action=%s, reject", 195890792Sgshapiro m->mf_name, action); 195990792Sgshapiro *state = rcmd; 196090792Sgshapiro break; 196190792Sgshapiro 196264562Sgshapiro case SMFIR_DISCARD: 196390792Sgshapiro if (MilterLogLevel > 10) 196490792Sgshapiro sm_syslog(LOG_INFO, e->e_id, "milter=%s, action=%s, discard", 196590792Sgshapiro m->mf_name, action); 196690792Sgshapiro *state = rcmd; 196790792Sgshapiro break; 196890792Sgshapiro 196964562Sgshapiro case SMFIR_TEMPFAIL: 197090792Sgshapiro if (MilterLogLevel > 10) 197190792Sgshapiro sm_syslog(LOG_INFO, e->e_id, "milter=%s, action=%s, tempfail", 197290792Sgshapiro m->mf_name, action); 197364562Sgshapiro *state = rcmd; 197464562Sgshapiro break; 197564562Sgshapiro 197664562Sgshapiro case SMFIR_ACCEPT: 197764562Sgshapiro /* this filter is done with message/connection */ 197873188Sgshapiro if (command == SMFIC_HELO || 197973188Sgshapiro command == SMFIC_CONNECT) 198073188Sgshapiro m->mf_state = SMFS_CLOSABLE; 198173188Sgshapiro else 198273188Sgshapiro m->mf_state = SMFS_DONE; 198390792Sgshapiro if (MilterLogLevel > 10) 198490792Sgshapiro sm_syslog(LOG_INFO, e->e_id, "milter=%s, action=%s, accepted", 198590792Sgshapiro m->mf_name, action); 198664562Sgshapiro break; 198764562Sgshapiro 198864562Sgshapiro case SMFIR_CONTINUE: 198964562Sgshapiro /* if MAIL command is ok, filter is in message state */ 199064562Sgshapiro if (command == SMFIC_MAIL) 199164562Sgshapiro m->mf_state = SMFS_INMSG; 199290792Sgshapiro if (MilterLogLevel > 12) 199390792Sgshapiro sm_syslog(LOG_INFO, e->e_id, "milter=%s, action=%s, continue", 199490792Sgshapiro m->mf_name, action); 199564562Sgshapiro break; 199664562Sgshapiro 199764562Sgshapiro default: 199864562Sgshapiro /* Invalid response to command */ 199990792Sgshapiro if (MilterLogLevel > 0) 200064562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 200190792Sgshapiro "milter_send_command(%s): action=%s returned bogus response %c", 200290792Sgshapiro m->mf_name, action, rcmd); 200390792Sgshapiro milter_error(m, e); 200464562Sgshapiro break; 200564562Sgshapiro } 200664562Sgshapiro 200764562Sgshapiro if (*state != SMFIR_REPLYCODE && 200864562Sgshapiro response != NULL) 200964562Sgshapiro { 201090792Sgshapiro sm_free(response); /* XXX */ 201164562Sgshapiro response = NULL; 201264562Sgshapiro } 201364562Sgshapiro return response; 201464562Sgshapiro} 201564562Sgshapiro 201690792Sgshapiro/* 201764562Sgshapiro** MILTER_COMMAND -- send a command and return the response for each filter 201864562Sgshapiro** 201964562Sgshapiro** Parameters: 202064562Sgshapiro** command -- command to send. 202164562Sgshapiro** data -- optional command data. 202264562Sgshapiro** sz -- length of buf. 202364562Sgshapiro** macros -- macros to send for filter smfi_getsymval(). 202464562Sgshapiro** e -- current envelope (for macro access). 202564562Sgshapiro** state -- return state word. 202664562Sgshapiro** 202764562Sgshapiro** Returns: 202864562Sgshapiro** response string (may be NULL) 202964562Sgshapiro*/ 203064562Sgshapiro 203164562Sgshapirostatic char * 203264562Sgshapiromilter_command(command, data, sz, macros, e, state) 203364562Sgshapiro char command; 203464562Sgshapiro void *data; 203564562Sgshapiro ssize_t sz; 203664562Sgshapiro char **macros; 203764562Sgshapiro ENVELOPE *e; 203864562Sgshapiro char *state; 203964562Sgshapiro{ 204064562Sgshapiro int i; 204164562Sgshapiro char *response = NULL; 204290792Sgshapiro time_t tn = 0; 204364562Sgshapiro 204464562Sgshapiro if (tTd(64, 10)) 204590792Sgshapiro sm_dprintf("milter_command: cmd %c len %ld\n", 204664562Sgshapiro (char) command, (long) sz); 204764562Sgshapiro 204864562Sgshapiro *state = SMFIR_CONTINUE; 204964562Sgshapiro for (i = 0; InputFilters[i] != NULL; i++) 205064562Sgshapiro { 205164562Sgshapiro struct milter *m = InputFilters[i]; 205264562Sgshapiro 205371345Sgshapiro /* previous problem? */ 205471345Sgshapiro if (m->mf_state == SMFS_ERROR) 205571345Sgshapiro { 2056112810Sgshapiro MILTER_CHECK_ERROR(false, continue); 205771345Sgshapiro break; 205871345Sgshapiro } 205971345Sgshapiro 206064562Sgshapiro /* sanity check */ 206164562Sgshapiro if (m->mf_sock < 0 || 206264562Sgshapiro (m->mf_state != SMFS_OPEN && m->mf_state != SMFS_INMSG)) 206364562Sgshapiro continue; 206464562Sgshapiro 206564562Sgshapiro /* send macros (regardless of whether we send command) */ 206664562Sgshapiro if (macros != NULL && macros[0] != NULL) 206764562Sgshapiro { 206864562Sgshapiro milter_send_macros(m, macros, command, e); 206964562Sgshapiro if (m->mf_state == SMFS_ERROR) 207064562Sgshapiro { 2071112810Sgshapiro MILTER_CHECK_ERROR(false, continue); 207264562Sgshapiro break; 207364562Sgshapiro } 207464562Sgshapiro } 207564562Sgshapiro 207690792Sgshapiro if (MilterLogLevel > 21) 207790792Sgshapiro tn = curtime(); 207890792Sgshapiro 207964562Sgshapiro response = milter_send_command(m, command, data, sz, e, state); 208090792Sgshapiro 208190792Sgshapiro if (MilterLogLevel > 21) 208290792Sgshapiro { 208390792Sgshapiro /* log the time it took for the command per filter */ 208490792Sgshapiro sm_syslog(LOG_INFO, e->e_id, 208590792Sgshapiro "Milter (%s): time command (%c), %d", 208690792Sgshapiro m->mf_name, command, (int) (tn - curtime())); 208790792Sgshapiro } 208890792Sgshapiro 208964562Sgshapiro if (*state != SMFIR_CONTINUE) 209064562Sgshapiro break; 209164562Sgshapiro } 209264562Sgshapiro return response; 209364562Sgshapiro} 209490792Sgshapiro/* 209564562Sgshapiro** MILTER_NEGOTIATE -- get version and flags from filter 209664562Sgshapiro** 209764562Sgshapiro** Parameters: 209864562Sgshapiro** m -- milter filter structure. 209964562Sgshapiro** e -- current envelope. 210064562Sgshapiro** 210164562Sgshapiro** Returns: 210264562Sgshapiro** 0 on success, -1 otherwise 210364562Sgshapiro*/ 210464562Sgshapiro 210564562Sgshapirostatic int 210664562Sgshapiromilter_negotiate(m, e) 210764562Sgshapiro struct milter *m; 210864562Sgshapiro ENVELOPE *e; 210964562Sgshapiro{ 211064562Sgshapiro char rcmd; 211164562Sgshapiro mi_int32 fvers; 211264562Sgshapiro mi_int32 fflags; 211364562Sgshapiro mi_int32 pflags; 211464562Sgshapiro char *response; 211564562Sgshapiro ssize_t rlen; 211664562Sgshapiro char data[MILTER_OPTLEN]; 211764562Sgshapiro 211864562Sgshapiro /* sanity check */ 211964562Sgshapiro if (m->mf_sock < 0 || m->mf_state != SMFS_OPEN) 212064562Sgshapiro { 212190792Sgshapiro if (MilterLogLevel > 0) 212264562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 212390792Sgshapiro "Milter (%s): negotiate, impossible state", 212464562Sgshapiro m->mf_name); 212590792Sgshapiro milter_error(m, e); 212664562Sgshapiro return -1; 212764562Sgshapiro } 212864562Sgshapiro 212964562Sgshapiro fvers = htonl(SMFI_VERSION); 213064562Sgshapiro fflags = htonl(SMFI_CURR_ACTS); 213164562Sgshapiro pflags = htonl(SMFI_CURR_PROT); 213264562Sgshapiro (void) memcpy(data, (char *) &fvers, MILTER_LEN_BYTES); 213364562Sgshapiro (void) memcpy(data + MILTER_LEN_BYTES, 213464562Sgshapiro (char *) &fflags, MILTER_LEN_BYTES); 213564562Sgshapiro (void) memcpy(data + (MILTER_LEN_BYTES * 2), 213664562Sgshapiro (char *) &pflags, MILTER_LEN_BYTES); 213764562Sgshapiro (void) milter_write(m, SMFIC_OPTNEG, data, sizeof data, 213864562Sgshapiro m->mf_timeout[SMFTO_WRITE], e); 213964562Sgshapiro 214064562Sgshapiro if (m->mf_state == SMFS_ERROR) 214164562Sgshapiro return -1; 214264562Sgshapiro 214364562Sgshapiro response = milter_read(m, &rcmd, &rlen, m->mf_timeout[SMFTO_READ], e); 214464562Sgshapiro if (m->mf_state == SMFS_ERROR) 214564562Sgshapiro return -1; 214664562Sgshapiro 214764562Sgshapiro if (rcmd != SMFIC_OPTNEG) 214864562Sgshapiro { 214964562Sgshapiro if (tTd(64, 5)) 215090792Sgshapiro sm_dprintf("milter_negotiate(%s): returned %c instead of %c\n", 215164562Sgshapiro m->mf_name, rcmd, SMFIC_OPTNEG); 215290792Sgshapiro if (MilterLogLevel > 0) 215364562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 215490792Sgshapiro "Milter (%s): negotiate: returned %c instead of %c", 215564562Sgshapiro m->mf_name, rcmd, SMFIC_OPTNEG); 215664562Sgshapiro if (response != NULL) 215790792Sgshapiro sm_free(response); /* XXX */ 215890792Sgshapiro milter_error(m, e); 215964562Sgshapiro return -1; 216064562Sgshapiro } 216164562Sgshapiro 216264562Sgshapiro /* Make sure we have enough bytes for the version */ 216364562Sgshapiro if (response == NULL || rlen < MILTER_LEN_BYTES) 216464562Sgshapiro { 216564562Sgshapiro if (tTd(64, 5)) 216690792Sgshapiro sm_dprintf("milter_negotiate(%s): did not return valid info\n", 216764562Sgshapiro m->mf_name); 216890792Sgshapiro if (MilterLogLevel > 0) 216964562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 217090792Sgshapiro "Milter (%s): negotiate: did not return valid info", 217164562Sgshapiro m->mf_name); 217264562Sgshapiro if (response != NULL) 217390792Sgshapiro sm_free(response); /* XXX */ 217490792Sgshapiro milter_error(m, e); 217564562Sgshapiro return -1; 217664562Sgshapiro } 217764562Sgshapiro 217864562Sgshapiro /* extract information */ 217964562Sgshapiro (void) memcpy((char *) &fvers, response, MILTER_LEN_BYTES); 218064562Sgshapiro 218164562Sgshapiro /* Now make sure we have enough for the feature bitmap */ 218264562Sgshapiro if (rlen != MILTER_OPTLEN) 218364562Sgshapiro { 218464562Sgshapiro if (tTd(64, 5)) 218590792Sgshapiro sm_dprintf("milter_negotiate(%s): did not return enough info\n", 218664562Sgshapiro m->mf_name); 218790792Sgshapiro if (MilterLogLevel > 0) 218864562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 218990792Sgshapiro "Milter (%s): negotiate: did not return enough info", 219064562Sgshapiro m->mf_name); 219164562Sgshapiro if (response != NULL) 219290792Sgshapiro sm_free(response); /* XXX */ 219390792Sgshapiro milter_error(m, e); 219464562Sgshapiro return -1; 219564562Sgshapiro } 219664562Sgshapiro 219764562Sgshapiro (void) memcpy((char *) &fflags, response + MILTER_LEN_BYTES, 219864562Sgshapiro MILTER_LEN_BYTES); 219964562Sgshapiro (void) memcpy((char *) &pflags, response + (MILTER_LEN_BYTES * 2), 220064562Sgshapiro MILTER_LEN_BYTES); 220190792Sgshapiro sm_free(response); /* XXX */ 220264562Sgshapiro response = NULL; 220364562Sgshapiro 220464562Sgshapiro m->mf_fvers = ntohl(fvers); 220564562Sgshapiro m->mf_fflags = ntohl(fflags); 220664562Sgshapiro m->mf_pflags = ntohl(pflags); 220764562Sgshapiro 220864562Sgshapiro /* check for version compatibility */ 220964562Sgshapiro if (m->mf_fvers == 1 || 221064562Sgshapiro m->mf_fvers > SMFI_VERSION) 221164562Sgshapiro { 221264562Sgshapiro if (tTd(64, 5)) 221394334Sgshapiro sm_dprintf("milter_negotiate(%s): version %d != MTA milter version %d\n", 221464562Sgshapiro m->mf_name, m->mf_fvers, SMFI_VERSION); 221590792Sgshapiro if (MilterLogLevel > 0) 221664562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 221794334Sgshapiro "Milter (%s): negotiate: version %d != MTA milter version %d", 221864562Sgshapiro m->mf_name, m->mf_fvers, SMFI_VERSION); 221990792Sgshapiro milter_error(m, e); 222064562Sgshapiro return -1; 222164562Sgshapiro } 222264562Sgshapiro 222364562Sgshapiro /* check for filter feature mismatch */ 222464562Sgshapiro if ((m->mf_fflags & SMFI_CURR_ACTS) != m->mf_fflags) 222564562Sgshapiro { 222664562Sgshapiro if (tTd(64, 5)) 222794334Sgshapiro sm_dprintf("milter_negotiate(%s): filter abilities 0x%x != MTA milter abilities 0x%lx\n", 222864562Sgshapiro m->mf_name, m->mf_fflags, 222994334Sgshapiro SMFI_CURR_ACTS); 223090792Sgshapiro if (MilterLogLevel > 0) 223164562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 223294334Sgshapiro "Milter (%s): negotiate: filter abilities 0x%x != MTA milter abilities 0x%lx", 223364562Sgshapiro m->mf_name, m->mf_fflags, 223490792Sgshapiro (unsigned long) SMFI_CURR_ACTS); 223590792Sgshapiro milter_error(m, e); 223664562Sgshapiro return -1; 223764562Sgshapiro } 223864562Sgshapiro 223964562Sgshapiro /* check for protocol feature mismatch */ 224064562Sgshapiro if ((m->mf_pflags & SMFI_CURR_PROT) != m->mf_pflags) 224164562Sgshapiro { 224264562Sgshapiro if (tTd(64, 5)) 224394334Sgshapiro sm_dprintf("milter_negotiate(%s): protocol abilities 0x%x != MTA milter abilities 0x%lx\n", 224464562Sgshapiro m->mf_name, m->mf_pflags, 224590792Sgshapiro (unsigned long) SMFI_CURR_PROT); 224690792Sgshapiro if (MilterLogLevel > 0) 224764562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 224894334Sgshapiro "Milter (%s): negotiate: protocol abilities 0x%x != MTA milter abilities 0x%lx", 224964562Sgshapiro m->mf_name, m->mf_pflags, 225090792Sgshapiro (unsigned long) SMFI_CURR_PROT); 225190792Sgshapiro milter_error(m, e); 225264562Sgshapiro return -1; 225364562Sgshapiro } 225464562Sgshapiro 225564562Sgshapiro if (tTd(64, 5)) 225694334Sgshapiro sm_dprintf("milter_negotiate(%s): version %u, fflags 0x%x, pflags 0x%x\n", 225764562Sgshapiro m->mf_name, m->mf_fvers, m->mf_fflags, m->mf_pflags); 225864562Sgshapiro return 0; 225964562Sgshapiro} 226090792Sgshapiro/* 226164562Sgshapiro** MILTER_PER_CONNECTION_CHECK -- checks on per-connection commands 226264562Sgshapiro** 226364562Sgshapiro** Reduce code duplication by putting these checks in one place 226464562Sgshapiro** 226564562Sgshapiro** Parameters: 226664562Sgshapiro** e -- current envelope. 226764562Sgshapiro** 226864562Sgshapiro** Returns: 226964562Sgshapiro** none 227064562Sgshapiro*/ 227164562Sgshapiro 227264562Sgshapirostatic void 227364562Sgshapiromilter_per_connection_check(e) 227464562Sgshapiro ENVELOPE *e; 227564562Sgshapiro{ 227664562Sgshapiro int i; 227764562Sgshapiro 227864562Sgshapiro /* see if we are done with any of the filters */ 227964562Sgshapiro for (i = 0; InputFilters[i] != NULL; i++) 228064562Sgshapiro { 228164562Sgshapiro struct milter *m = InputFilters[i]; 228264562Sgshapiro 228373188Sgshapiro if (m->mf_state == SMFS_CLOSABLE) 228464562Sgshapiro milter_quit_filter(m, e); 228564562Sgshapiro } 228664562Sgshapiro} 228790792Sgshapiro/* 228864562Sgshapiro** MILTER_ERROR -- Put a milter filter into error state 228964562Sgshapiro** 229064562Sgshapiro** Parameters: 229164562Sgshapiro** m -- the broken filter. 229264562Sgshapiro** 229364562Sgshapiro** Returns: 229464562Sgshapiro** none 229564562Sgshapiro*/ 229664562Sgshapiro 229764562Sgshapirostatic void 229890792Sgshapiromilter_error(m, e) 229964562Sgshapiro struct milter *m; 230090792Sgshapiro ENVELOPE *e; 230164562Sgshapiro{ 230264562Sgshapiro /* 230364562Sgshapiro ** We could send a quit here but 230464562Sgshapiro ** we may have gotten here due to 230564562Sgshapiro ** an I/O error so we don't want 230664562Sgshapiro ** to try to make things worse. 230764562Sgshapiro */ 230864562Sgshapiro 230964562Sgshapiro if (m->mf_sock >= 0) 231064562Sgshapiro { 231164562Sgshapiro (void) close(m->mf_sock); 231264562Sgshapiro m->mf_sock = -1; 231364562Sgshapiro } 231464562Sgshapiro m->mf_state = SMFS_ERROR; 231590792Sgshapiro 231690792Sgshapiro if (MilterLogLevel > 0) 231790792Sgshapiro sm_syslog(LOG_INFO, e->e_id, "Milter (%s): to error state", 231890792Sgshapiro m->mf_name); 231964562Sgshapiro} 232090792Sgshapiro/* 232164562Sgshapiro** MILTER_HEADERS -- send headers to a single milter filter 232264562Sgshapiro** 232364562Sgshapiro** Parameters: 232464562Sgshapiro** m -- current filter. 232564562Sgshapiro** e -- current envelope. 232664562Sgshapiro** state -- return state from response. 232764562Sgshapiro** 232864562Sgshapiro** Returns: 232964562Sgshapiro** response string (may be NULL) 233064562Sgshapiro*/ 233164562Sgshapiro 233264562Sgshapirostatic char * 233364562Sgshapiromilter_headers(m, e, state) 233464562Sgshapiro struct milter *m; 233564562Sgshapiro ENVELOPE *e; 233664562Sgshapiro char *state; 233764562Sgshapiro{ 233864562Sgshapiro char *response = NULL; 233964562Sgshapiro HDR *h; 234064562Sgshapiro 234190792Sgshapiro if (MilterLogLevel > 17) 234290792Sgshapiro sm_syslog(LOG_INFO, e->e_id, "Milter (%s): headers, send", 234390792Sgshapiro m->mf_name); 234490792Sgshapiro 234564562Sgshapiro for (h = e->e_header; h != NULL; h = h->h_link) 234664562Sgshapiro { 234764562Sgshapiro char *buf; 234864562Sgshapiro ssize_t s; 234964562Sgshapiro 235064562Sgshapiro /* don't send over deleted headers */ 235164562Sgshapiro if (h->h_value == NULL) 235264562Sgshapiro { 235364562Sgshapiro /* strip H_USER so not counted in milter_chgheader() */ 235464562Sgshapiro h->h_flags &= ~H_USER; 235564562Sgshapiro continue; 235664562Sgshapiro } 235764562Sgshapiro 235864562Sgshapiro /* skip auto-generated */ 235964562Sgshapiro if (!bitset(H_USER, h->h_flags)) 236064562Sgshapiro continue; 236164562Sgshapiro 236264562Sgshapiro if (tTd(64, 10)) 236390792Sgshapiro sm_dprintf("milter_headers: %s: %s\n", 236464562Sgshapiro h->h_field, h->h_value); 236590792Sgshapiro if (MilterLogLevel > 21) 236690792Sgshapiro sm_syslog(LOG_INFO, e->e_id, "Milter (%s): header, %s", 236790792Sgshapiro m->mf_name, h->h_field); 236864562Sgshapiro 236990792Sgshapiro s = strlen(h->h_field) + 1 + strlen(h->h_value) + 1; 237090792Sgshapiro if (s < 0) 237190792Sgshapiro continue; 237264562Sgshapiro buf = (char *) xalloc(s); 237390792Sgshapiro (void) sm_snprintf(buf, s, "%s%c%s", 237490792Sgshapiro h->h_field, '\0', h->h_value); 237564562Sgshapiro 237664562Sgshapiro /* send it over */ 237764562Sgshapiro response = milter_send_command(m, SMFIC_HEADER, buf, 237864562Sgshapiro s, e, state); 237990792Sgshapiro sm_free(buf); /* XXX */ 238064562Sgshapiro if (m->mf_state == SMFS_ERROR || 238164562Sgshapiro m->mf_state == SMFS_DONE || 238264562Sgshapiro *state != SMFIR_CONTINUE) 238364562Sgshapiro break; 238464562Sgshapiro } 238590792Sgshapiro if (MilterLogLevel > 17) 238690792Sgshapiro sm_syslog(LOG_INFO, e->e_id, "Milter (%s): headers, sent", 238790792Sgshapiro m->mf_name); 238864562Sgshapiro return response; 238964562Sgshapiro} 239090792Sgshapiro/* 239164562Sgshapiro** MILTER_BODY -- send the body to a filter 239264562Sgshapiro** 239364562Sgshapiro** Parameters: 239464562Sgshapiro** m -- current filter. 239564562Sgshapiro** e -- current envelope. 239664562Sgshapiro** state -- return state from response. 239764562Sgshapiro** 239864562Sgshapiro** Returns: 239964562Sgshapiro** response string (may be NULL) 240064562Sgshapiro*/ 240164562Sgshapiro 240264562Sgshapirostatic char * 240364562Sgshapiromilter_body(m, e, state) 240464562Sgshapiro struct milter *m; 240564562Sgshapiro ENVELOPE *e; 240664562Sgshapiro char *state; 240764562Sgshapiro{ 240864562Sgshapiro char bufchar = '\0'; 240964562Sgshapiro char prevchar = '\0'; 241064562Sgshapiro int c; 241164562Sgshapiro char *response = NULL; 241264562Sgshapiro char *bp; 241364562Sgshapiro char buf[MILTER_CHUNK_SIZE]; 241464562Sgshapiro 241564562Sgshapiro if (tTd(64, 10)) 241690792Sgshapiro sm_dprintf("milter_body\n"); 241764562Sgshapiro 241864562Sgshapiro if (bfrewind(e->e_dfp) < 0) 241964562Sgshapiro { 242064562Sgshapiro ExitStat = EX_IOERR; 242164562Sgshapiro *state = SMFIR_TEMPFAIL; 242290792Sgshapiro syserr("milter_body: %s/%cf%s: rewind error", 242390792Sgshapiro qid_printqueue(e->e_qgrp, e->e_qdir), 242490792Sgshapiro DATAFL_LETTER, e->e_id); 242564562Sgshapiro return NULL; 242664562Sgshapiro } 242764562Sgshapiro 242890792Sgshapiro if (MilterLogLevel > 17) 242990792Sgshapiro sm_syslog(LOG_INFO, e->e_id, "Milter (%s): body, send", 243090792Sgshapiro m->mf_name); 243164562Sgshapiro bp = buf; 243290792Sgshapiro while ((c = sm_io_getc(e->e_dfp, SM_TIME_DEFAULT)) != SM_IO_EOF) 243364562Sgshapiro { 243464562Sgshapiro /* Change LF to CRLF */ 243564562Sgshapiro if (c == '\n') 243664562Sgshapiro { 243764562Sgshapiro /* Not a CRLF already? */ 243864562Sgshapiro if (prevchar != '\r') 243964562Sgshapiro { 244064562Sgshapiro /* Room for CR now? */ 244164562Sgshapiro if (bp + 2 > &buf[sizeof buf]) 244264562Sgshapiro { 244364562Sgshapiro /* No room, buffer LF */ 244464562Sgshapiro bufchar = c; 244564562Sgshapiro 244664562Sgshapiro /* and send CR now */ 244764562Sgshapiro c = '\r'; 244864562Sgshapiro } 244964562Sgshapiro else 245064562Sgshapiro { 245164562Sgshapiro /* Room to do it now */ 245264562Sgshapiro *bp++ = '\r'; 245364562Sgshapiro prevchar = '\r'; 245464562Sgshapiro } 245564562Sgshapiro } 245664562Sgshapiro } 245764562Sgshapiro *bp++ = (char) c; 245864562Sgshapiro prevchar = c; 245964562Sgshapiro if (bp >= &buf[sizeof buf]) 246064562Sgshapiro { 246164562Sgshapiro /* send chunk */ 246264562Sgshapiro response = milter_send_command(m, SMFIC_BODY, buf, 246364562Sgshapiro bp - buf, e, state); 246464562Sgshapiro bp = buf; 246564562Sgshapiro if (bufchar != '\0') 246664562Sgshapiro { 246764562Sgshapiro *bp++ = bufchar; 246864562Sgshapiro bufchar = '\0'; 246964562Sgshapiro prevchar = bufchar; 247064562Sgshapiro } 247164562Sgshapiro } 247264562Sgshapiro if (m->mf_state == SMFS_ERROR || 247364562Sgshapiro m->mf_state == SMFS_DONE || 247464562Sgshapiro *state != SMFIR_CONTINUE) 247564562Sgshapiro break; 247664562Sgshapiro } 247764562Sgshapiro 247864562Sgshapiro /* check for read errors */ 247990792Sgshapiro if (sm_io_error(e->e_dfp)) 248064562Sgshapiro { 248164562Sgshapiro ExitStat = EX_IOERR; 248264562Sgshapiro if (*state == SMFIR_CONTINUE || 248364562Sgshapiro *state == SMFIR_ACCEPT) 248464562Sgshapiro { 248564562Sgshapiro *state = SMFIR_TEMPFAIL; 248664562Sgshapiro if (response != NULL) 248764562Sgshapiro { 248890792Sgshapiro sm_free(response); /* XXX */ 248964562Sgshapiro response = NULL; 249064562Sgshapiro } 249164562Sgshapiro } 249290792Sgshapiro syserr("milter_body: %s/%cf%s: read error", 249390792Sgshapiro qid_printqueue(e->e_qgrp, e->e_qdir), 249490792Sgshapiro DATAFL_LETTER, e->e_id); 249564562Sgshapiro return response; 249664562Sgshapiro } 249764562Sgshapiro 249864562Sgshapiro /* send last body chunk */ 249964562Sgshapiro if (bp > buf && 250064562Sgshapiro m->mf_state != SMFS_ERROR && 250164562Sgshapiro m->mf_state != SMFS_DONE && 250264562Sgshapiro *state == SMFIR_CONTINUE) 250364562Sgshapiro { 250464562Sgshapiro /* send chunk */ 250564562Sgshapiro response = milter_send_command(m, SMFIC_BODY, buf, bp - buf, 250664562Sgshapiro e, state); 250764562Sgshapiro bp = buf; 250864562Sgshapiro } 250990792Sgshapiro if (MilterLogLevel > 17) 251090792Sgshapiro sm_syslog(LOG_INFO, e->e_id, "Milter (%s): body, sent", 251190792Sgshapiro m->mf_name); 251264562Sgshapiro return response; 251364562Sgshapiro} 251464562Sgshapiro 251564562Sgshapiro/* 251664562Sgshapiro** Actions 251764562Sgshapiro*/ 251864562Sgshapiro 251990792Sgshapiro/* 252064562Sgshapiro** MILTER_ADDHEADER -- Add the supplied header to the message 252164562Sgshapiro** 252264562Sgshapiro** Parameters: 252364562Sgshapiro** response -- encoded form of header/value. 252464562Sgshapiro** rlen -- length of response. 252564562Sgshapiro** e -- current envelope. 252664562Sgshapiro** 252764562Sgshapiro** Returns: 252864562Sgshapiro** none 252964562Sgshapiro*/ 253064562Sgshapiro 253164562Sgshapirostatic void 253264562Sgshapiromilter_addheader(response, rlen, e) 253364562Sgshapiro char *response; 253464562Sgshapiro ssize_t rlen; 253564562Sgshapiro ENVELOPE *e; 253664562Sgshapiro{ 253764562Sgshapiro char *val; 253871345Sgshapiro HDR *h; 253964562Sgshapiro 254064562Sgshapiro if (tTd(64, 10)) 254190792Sgshapiro sm_dprintf("milter_addheader: "); 254264562Sgshapiro 254364562Sgshapiro /* sanity checks */ 254464562Sgshapiro if (response == NULL) 254564562Sgshapiro { 254664562Sgshapiro if (tTd(64, 10)) 254790792Sgshapiro sm_dprintf("NULL response\n"); 254864562Sgshapiro return; 254964562Sgshapiro } 255064562Sgshapiro 255164562Sgshapiro if (rlen < 2 || strlen(response) + 1 >= (size_t) rlen) 255264562Sgshapiro { 255364562Sgshapiro if (tTd(64, 10)) 255490792Sgshapiro sm_dprintf("didn't follow protocol (total len)\n"); 255564562Sgshapiro return; 255664562Sgshapiro } 255764562Sgshapiro 255864562Sgshapiro /* Find separating NUL */ 255964562Sgshapiro val = response + strlen(response) + 1; 256064562Sgshapiro 256164562Sgshapiro /* another sanity check */ 256264562Sgshapiro if (strlen(response) + strlen(val) + 2 != (size_t) rlen) 256364562Sgshapiro { 256464562Sgshapiro if (tTd(64, 10)) 256590792Sgshapiro sm_dprintf("didn't follow protocol (part len)\n"); 256664562Sgshapiro return; 256764562Sgshapiro } 256864562Sgshapiro 256964562Sgshapiro if (*response == '\0') 257064562Sgshapiro { 257164562Sgshapiro if (tTd(64, 10)) 257290792Sgshapiro sm_dprintf("empty field name\n"); 257364562Sgshapiro return; 257464562Sgshapiro } 257564562Sgshapiro 257671345Sgshapiro for (h = e->e_header; h != NULL; h = h->h_link) 257771345Sgshapiro { 257890792Sgshapiro if (sm_strcasecmp(h->h_field, response) == 0 && 257971345Sgshapiro !bitset(H_USER, h->h_flags) && 258071345Sgshapiro !bitset(H_TRACE, h->h_flags)) 258171345Sgshapiro break; 258271345Sgshapiro } 258371345Sgshapiro 258464562Sgshapiro /* add to e_msgsize */ 258564562Sgshapiro e->e_msgsize += strlen(response) + 2 + strlen(val); 258664562Sgshapiro 258771345Sgshapiro if (h != NULL) 258871345Sgshapiro { 258971345Sgshapiro if (tTd(64, 10)) 259090792Sgshapiro sm_dprintf("Replace default header %s value with %s\n", 259190792Sgshapiro h->h_field, val); 259290792Sgshapiro if (MilterLogLevel > 8) 259390792Sgshapiro sm_syslog(LOG_INFO, e->e_id, 259490792Sgshapiro "Milter change: default header %s value with %s", 259590792Sgshapiro h->h_field, val); 259671345Sgshapiro h->h_value = newstr(val); 259771345Sgshapiro h->h_flags |= H_USER; 259871345Sgshapiro } 259971345Sgshapiro else 260071345Sgshapiro { 260171345Sgshapiro if (tTd(64, 10)) 260290792Sgshapiro sm_dprintf("Add %s: %s\n", response, val); 260390792Sgshapiro if (MilterLogLevel > 8) 260490792Sgshapiro sm_syslog(LOG_INFO, e->e_id, "Milter add: header: %s: %s", 260590792Sgshapiro response, val); 260690792Sgshapiro addheader(newstr(response), val, H_USER, e); 260771345Sgshapiro } 260864562Sgshapiro} 260990792Sgshapiro/* 261064562Sgshapiro** MILTER_CHANGEHEADER -- Change the supplied header in the message 261164562Sgshapiro** 261264562Sgshapiro** Parameters: 261364562Sgshapiro** response -- encoded form of header/index/value. 261464562Sgshapiro** rlen -- length of response. 261564562Sgshapiro** e -- current envelope. 261664562Sgshapiro** 261764562Sgshapiro** Returns: 261864562Sgshapiro** none 261964562Sgshapiro*/ 262064562Sgshapiro 262164562Sgshapirostatic void 262264562Sgshapiromilter_changeheader(response, rlen, e) 262364562Sgshapiro char *response; 262464562Sgshapiro ssize_t rlen; 262564562Sgshapiro ENVELOPE *e; 262664562Sgshapiro{ 262764562Sgshapiro mi_int32 i, index; 262864562Sgshapiro char *field, *val; 262971345Sgshapiro HDR *h, *sysheader; 263064562Sgshapiro 263164562Sgshapiro if (tTd(64, 10)) 263290792Sgshapiro sm_dprintf("milter_changeheader: "); 263364562Sgshapiro 263464562Sgshapiro /* sanity checks */ 263564562Sgshapiro if (response == NULL) 263664562Sgshapiro { 263764562Sgshapiro if (tTd(64, 10)) 263890792Sgshapiro sm_dprintf("NULL response\n"); 263964562Sgshapiro return; 264064562Sgshapiro } 264164562Sgshapiro 264264562Sgshapiro if (rlen < 2 || strlen(response) + 1 >= (size_t) rlen) 264364562Sgshapiro { 264464562Sgshapiro if (tTd(64, 10)) 264590792Sgshapiro sm_dprintf("didn't follow protocol (total len)\n"); 264664562Sgshapiro return; 264764562Sgshapiro } 264864562Sgshapiro 264964562Sgshapiro /* Find separating NUL */ 265064562Sgshapiro (void) memcpy((char *) &i, response, MILTER_LEN_BYTES); 265164562Sgshapiro index = ntohl(i); 265264562Sgshapiro field = response + MILTER_LEN_BYTES; 265364562Sgshapiro val = field + strlen(field) + 1; 265464562Sgshapiro 265564562Sgshapiro /* another sanity check */ 265664562Sgshapiro if (MILTER_LEN_BYTES + strlen(field) + 1 + 265764562Sgshapiro strlen(val) + 1 != (size_t) rlen) 265864562Sgshapiro { 265964562Sgshapiro if (tTd(64, 10)) 266090792Sgshapiro sm_dprintf("didn't follow protocol (part len)\n"); 266164562Sgshapiro return; 266264562Sgshapiro } 266364562Sgshapiro 266464562Sgshapiro if (*field == '\0') 266564562Sgshapiro { 266664562Sgshapiro if (tTd(64, 10)) 266790792Sgshapiro sm_dprintf("empty field name\n"); 266864562Sgshapiro return; 266964562Sgshapiro } 267064562Sgshapiro 267171345Sgshapiro sysheader = NULL; 267264562Sgshapiro for (h = e->e_header; h != NULL; h = h->h_link) 267364562Sgshapiro { 267490792Sgshapiro if (sm_strcasecmp(h->h_field, field) == 0) 267571345Sgshapiro { 267671345Sgshapiro if (bitset(H_USER, h->h_flags) && 267771345Sgshapiro --index <= 0) 267871345Sgshapiro { 267971345Sgshapiro sysheader = NULL; 268071345Sgshapiro break; 268171345Sgshapiro } 268271345Sgshapiro else if (!bitset(H_USER, h->h_flags) && 268371345Sgshapiro !bitset(H_TRACE, h->h_flags)) 268471345Sgshapiro { 268571345Sgshapiro /* 268671345Sgshapiro ** DRUMS msg-fmt draft says can only have 268771345Sgshapiro ** multiple occurences of trace fields, 268871345Sgshapiro ** so make sure we replace any non-trace, 268971345Sgshapiro ** non-user field. 269071345Sgshapiro */ 269171345Sgshapiro 269271345Sgshapiro sysheader = h; 269371345Sgshapiro } 269471345Sgshapiro } 269564562Sgshapiro } 269664562Sgshapiro 269771345Sgshapiro /* if not found as user-provided header at index, use sysheader */ 269864562Sgshapiro if (h == NULL) 269971345Sgshapiro h = sysheader; 270071345Sgshapiro 270171345Sgshapiro if (h == NULL) 270264562Sgshapiro { 270364562Sgshapiro if (*val == '\0') 270464562Sgshapiro { 270564562Sgshapiro if (tTd(64, 10)) 270690792Sgshapiro sm_dprintf("Delete (noop) %s:\n", field); 270764562Sgshapiro } 270864562Sgshapiro else 270964562Sgshapiro { 271064562Sgshapiro /* treat modify value with no existing header as add */ 271164562Sgshapiro if (tTd(64, 10)) 271290792Sgshapiro sm_dprintf("Add %s: %s\n", field, val); 271390792Sgshapiro addheader(newstr(field), val, H_USER, e); 271464562Sgshapiro } 271564562Sgshapiro return; 271664562Sgshapiro } 271764562Sgshapiro 271864562Sgshapiro if (tTd(64, 10)) 271964562Sgshapiro { 272064562Sgshapiro if (*val == '\0') 272164562Sgshapiro { 272290792Sgshapiro sm_dprintf("Delete%s %s: %s\n", 272390792Sgshapiro h == sysheader ? " (default header)" : "", 272490792Sgshapiro field, 272590792Sgshapiro h->h_value == NULL ? "<NULL>" : h->h_value); 272664562Sgshapiro } 272764562Sgshapiro else 272864562Sgshapiro { 272990792Sgshapiro sm_dprintf("Change%s %s: from %s to %s\n", 273090792Sgshapiro h == sysheader ? " (default header)" : "", 273190792Sgshapiro field, 273290792Sgshapiro h->h_value == NULL ? "<NULL>" : h->h_value, 273390792Sgshapiro val); 273464562Sgshapiro } 273564562Sgshapiro } 273664562Sgshapiro 273790792Sgshapiro if (MilterLogLevel > 8) 273890792Sgshapiro { 273990792Sgshapiro if (*val == '\0') 274090792Sgshapiro { 274190792Sgshapiro sm_syslog(LOG_INFO, e->e_id, 274290792Sgshapiro "Milter delete: header %s %s: %s", 274390792Sgshapiro h == sysheader ? " (default header)" : "", 274490792Sgshapiro field, 274590792Sgshapiro h->h_value == NULL ? "<NULL>" : h->h_value); 274690792Sgshapiro } 274790792Sgshapiro else 274890792Sgshapiro { 274990792Sgshapiro sm_syslog(LOG_INFO, e->e_id, 275090792Sgshapiro "Milter change: header %s %s: from %s to %s", 275190792Sgshapiro h == sysheader ? " (default header)" : "", 275290792Sgshapiro field, 275390792Sgshapiro h->h_value == NULL ? "<NULL>" : h->h_value, 275490792Sgshapiro val); 275590792Sgshapiro } 275690792Sgshapiro } 275790792Sgshapiro 275871345Sgshapiro if (h != sysheader && h->h_value != NULL) 275964562Sgshapiro { 276090792Sgshapiro size_t l; 276190792Sgshapiro 276290792Sgshapiro l = strlen(h->h_value); 276390792Sgshapiro if (l > e->e_msgsize) 276490792Sgshapiro e->e_msgsize = 0; 276590792Sgshapiro else 276690792Sgshapiro e->e_msgsize -= l; 276790792Sgshapiro /* rpool, don't free: sm_free(h->h_value); XXX */ 276864562Sgshapiro } 276964562Sgshapiro 277064562Sgshapiro if (*val == '\0') 277164562Sgshapiro { 277264562Sgshapiro /* Remove "Field: " from message size */ 277371345Sgshapiro if (h != sysheader) 277490792Sgshapiro { 277590792Sgshapiro size_t l; 277690792Sgshapiro 277790792Sgshapiro l = strlen(h->h_field) + 2; 277890792Sgshapiro if (l > e->e_msgsize) 277990792Sgshapiro e->e_msgsize = 0; 278090792Sgshapiro else 278190792Sgshapiro e->e_msgsize -= l; 278290792Sgshapiro } 278364562Sgshapiro h->h_value = NULL; 278464562Sgshapiro } 278564562Sgshapiro else 278664562Sgshapiro { 278764562Sgshapiro h->h_value = newstr(val); 278871345Sgshapiro h->h_flags |= H_USER; 278964562Sgshapiro e->e_msgsize += strlen(h->h_value); 279064562Sgshapiro } 279164562Sgshapiro} 279290792Sgshapiro/* 279364562Sgshapiro** MILTER_ADDRCPT -- Add the supplied recipient to the message 279464562Sgshapiro** 279564562Sgshapiro** Parameters: 279664562Sgshapiro** response -- encoded form of recipient address. 279764562Sgshapiro** rlen -- length of response. 279864562Sgshapiro** e -- current envelope. 279964562Sgshapiro** 280064562Sgshapiro** Returns: 280164562Sgshapiro** none 280264562Sgshapiro*/ 280364562Sgshapiro 280464562Sgshapirostatic void 280564562Sgshapiromilter_addrcpt(response, rlen, e) 280664562Sgshapiro char *response; 280764562Sgshapiro ssize_t rlen; 280864562Sgshapiro ENVELOPE *e; 280964562Sgshapiro{ 281064562Sgshapiro if (tTd(64, 10)) 281190792Sgshapiro sm_dprintf("milter_addrcpt: "); 281264562Sgshapiro 281364562Sgshapiro /* sanity checks */ 281464562Sgshapiro if (response == NULL) 281564562Sgshapiro { 281664562Sgshapiro if (tTd(64, 10)) 281790792Sgshapiro sm_dprintf("NULL response\n"); 281864562Sgshapiro return; 281964562Sgshapiro } 282064562Sgshapiro 282164562Sgshapiro if (*response == '\0' || 282264562Sgshapiro strlen(response) + 1 != (size_t) rlen) 282364562Sgshapiro { 282464562Sgshapiro if (tTd(64, 10)) 282590792Sgshapiro sm_dprintf("didn't follow protocol (total len %d != rlen %d)\n", 282690792Sgshapiro (int) strlen(response), (int) (rlen - 1)); 282764562Sgshapiro return; 282864562Sgshapiro } 282964562Sgshapiro 283064562Sgshapiro if (tTd(64, 10)) 283190792Sgshapiro sm_dprintf("%s\n", response); 283290792Sgshapiro if (MilterLogLevel > 8) 283390792Sgshapiro sm_syslog(LOG_INFO, e->e_id, "Milter add: rcpt: %s", response); 283464562Sgshapiro (void) sendtolist(response, NULLADDR, &e->e_sendqueue, 0, e); 283564562Sgshapiro return; 283664562Sgshapiro} 283790792Sgshapiro/* 283864562Sgshapiro** MILTER_DELRCPT -- Delete the supplied recipient from the message 283964562Sgshapiro** 284064562Sgshapiro** Parameters: 284164562Sgshapiro** response -- encoded form of recipient address. 284264562Sgshapiro** rlen -- length of response. 284364562Sgshapiro** e -- current envelope. 284464562Sgshapiro** 284564562Sgshapiro** Returns: 284664562Sgshapiro** none 284764562Sgshapiro*/ 284864562Sgshapiro 284964562Sgshapirostatic void 285064562Sgshapiromilter_delrcpt(response, rlen, e) 285164562Sgshapiro char *response; 285264562Sgshapiro ssize_t rlen; 285364562Sgshapiro ENVELOPE *e; 285464562Sgshapiro{ 285564562Sgshapiro if (tTd(64, 10)) 285690792Sgshapiro sm_dprintf("milter_delrcpt: "); 285764562Sgshapiro 285864562Sgshapiro /* sanity checks */ 285964562Sgshapiro if (response == NULL) 286064562Sgshapiro { 286164562Sgshapiro if (tTd(64, 10)) 286290792Sgshapiro sm_dprintf("NULL response\n"); 286364562Sgshapiro return; 286464562Sgshapiro } 286564562Sgshapiro 286664562Sgshapiro if (*response == '\0' || 286764562Sgshapiro strlen(response) + 1 != (size_t) rlen) 286864562Sgshapiro { 286964562Sgshapiro if (tTd(64, 10)) 287090792Sgshapiro sm_dprintf("didn't follow protocol (total len)\n"); 287164562Sgshapiro return; 287264562Sgshapiro } 287364562Sgshapiro 287464562Sgshapiro if (tTd(64, 10)) 287590792Sgshapiro sm_dprintf("%s\n", response); 287690792Sgshapiro if (MilterLogLevel > 8) 287790792Sgshapiro sm_syslog(LOG_INFO, e->e_id, "Milter delete: rcpt %s", 287890792Sgshapiro response); 287964562Sgshapiro (void) removefromlist(response, &e->e_sendqueue, e); 288064562Sgshapiro return; 288164562Sgshapiro} 288290792Sgshapiro/* 288390792Sgshapiro** MILTER_REPLBODY -- Replace the current data file with new body 288464562Sgshapiro** 288564562Sgshapiro** Parameters: 288664562Sgshapiro** response -- encoded form of new body. 288764562Sgshapiro** rlen -- length of response. 288864562Sgshapiro** newfilter -- if first time called by a new filter 288964562Sgshapiro** e -- current envelope. 289064562Sgshapiro** 289164562Sgshapiro** Returns: 289264562Sgshapiro** 0 upon success, -1 upon failure 289364562Sgshapiro*/ 289464562Sgshapiro 289564562Sgshapirostatic int 289664562Sgshapiromilter_replbody(response, rlen, newfilter, e) 289764562Sgshapiro char *response; 289864562Sgshapiro ssize_t rlen; 289964562Sgshapiro bool newfilter; 290064562Sgshapiro ENVELOPE *e; 290164562Sgshapiro{ 290264562Sgshapiro static char prevchar; 290364562Sgshapiro int i; 290464562Sgshapiro 290564562Sgshapiro if (tTd(64, 10)) 290690792Sgshapiro sm_dprintf("milter_replbody\n"); 290764562Sgshapiro 290890792Sgshapiro /* If a new filter, reset previous character and truncate data file */ 290964562Sgshapiro if (newfilter) 291064562Sgshapiro { 291194334Sgshapiro off_t prevsize; 291264562Sgshapiro char dfname[MAXPATHLEN]; 291364562Sgshapiro 291490792Sgshapiro (void) sm_strlcpy(dfname, queuename(e, DATAFL_LETTER), 291590792Sgshapiro sizeof dfname); 291664562Sgshapiro 291764562Sgshapiro /* Reset prevchar */ 291864562Sgshapiro prevchar = '\0'; 291964562Sgshapiro 292090792Sgshapiro /* Get the current data file information */ 292194334Sgshapiro prevsize = sm_io_getinfo(e->e_dfp, SM_IO_WHAT_SIZE, NULL); 292294334Sgshapiro if (prevsize < 0) 292394334Sgshapiro prevsize = 0; 292464562Sgshapiro 292590792Sgshapiro /* truncate current data file */ 292690792Sgshapiro if (sm_io_getinfo(e->e_dfp, SM_IO_WHAT_ISTYPE, BF_FILE_TYPE)) 292764562Sgshapiro { 292890792Sgshapiro if (sm_io_setinfo(e->e_dfp, SM_BF_TRUNCATE, NULL) < 0) 292990792Sgshapiro { 293090792Sgshapiro MILTER_DF_ERROR("milter_replbody: sm_io truncate %s: %s"); 293190792Sgshapiro return -1; 293290792Sgshapiro } 293364562Sgshapiro } 293464562Sgshapiro else 293564562Sgshapiro { 293690792Sgshapiro int err; 293790792Sgshapiro 293890792Sgshapiro err = sm_io_error(e->e_dfp); 293990792Sgshapiro (void) sm_io_flush(e->e_dfp, SM_TIME_DEFAULT); 294090792Sgshapiro 294190792Sgshapiro /* 294290792Sgshapiro ** Clear error if tried to fflush() 294390792Sgshapiro ** a read-only file pointer and 294490792Sgshapiro ** there wasn't a previous error. 294590792Sgshapiro */ 294690792Sgshapiro 294790792Sgshapiro if (err == 0) 294890792Sgshapiro sm_io_clearerr(e->e_dfp); 294990792Sgshapiro 295090792Sgshapiro /* errno is set implicitly by fseek() before return */ 295190792Sgshapiro err = sm_io_seek(e->e_dfp, SM_TIME_DEFAULT, 295290792Sgshapiro 0, SEEK_SET); 295395154Sgshapiro if (err < 0) 295495154Sgshapiro { 295595154Sgshapiro MILTER_DF_ERROR("milter_replbody: sm_io_seek %s: %s"); 295695154Sgshapiro return -1; 295795154Sgshapiro } 295895154Sgshapiro# if NOFTRUNCATE 295995154Sgshapiro /* XXX: Not much we can do except rewind it */ 296095154Sgshapiro errno = EINVAL; 296195154Sgshapiro MILTER_DF_ERROR("milter_replbody: ftruncate not available on this platform (%s:%s)"); 296295154Sgshapiro return -1; 296390792Sgshapiro# else /* NOFTRUNCATE */ 296490792Sgshapiro err = ftruncate(sm_io_getinfo(e->e_dfp, 296590792Sgshapiro SM_IO_WHAT_FD, NULL), 296690792Sgshapiro 0); 296790792Sgshapiro if (err < 0) 296890792Sgshapiro { 296990792Sgshapiro MILTER_DF_ERROR("milter_replbody: sm_io ftruncate %s: %s"); 297090792Sgshapiro return -1; 297190792Sgshapiro } 297295154Sgshapiro# endif /* NOFTRUNCATE */ 297364562Sgshapiro } 297490792Sgshapiro 297590792Sgshapiro if (prevsize > e->e_msgsize) 297690792Sgshapiro e->e_msgsize = 0; 297790792Sgshapiro else 297890792Sgshapiro e->e_msgsize -= prevsize; 297964562Sgshapiro } 298064562Sgshapiro 298190792Sgshapiro if (newfilter && MilterLogLevel > 8) 298290792Sgshapiro sm_syslog(LOG_INFO, e->e_id, "Milter message: body replaced"); 298390792Sgshapiro 298464562Sgshapiro if (response == NULL) 298564562Sgshapiro { 298664562Sgshapiro /* Flush the buffered '\r' */ 298764562Sgshapiro if (prevchar == '\r') 298864562Sgshapiro { 298990792Sgshapiro (void) sm_io_putc(e->e_dfp, SM_TIME_DEFAULT, prevchar); 299064562Sgshapiro e->e_msgsize++; 299164562Sgshapiro } 299264562Sgshapiro return 0; 299364562Sgshapiro } 299464562Sgshapiro 299564562Sgshapiro for (i = 0; i < rlen; i++) 299664562Sgshapiro { 299764562Sgshapiro /* Buffered char from last chunk */ 299864562Sgshapiro if (i == 0 && prevchar == '\r') 299964562Sgshapiro { 300064562Sgshapiro /* Not CRLF, output prevchar */ 300164562Sgshapiro if (response[i] != '\n') 300264562Sgshapiro { 300390792Sgshapiro (void) sm_io_putc(e->e_dfp, SM_TIME_DEFAULT, 300490792Sgshapiro prevchar); 300564562Sgshapiro e->e_msgsize++; 300664562Sgshapiro } 300764562Sgshapiro prevchar = '\0'; 300864562Sgshapiro } 300964562Sgshapiro 301064562Sgshapiro /* Turn CRLF into LF */ 301164562Sgshapiro if (response[i] == '\r') 301264562Sgshapiro { 301364562Sgshapiro /* check if at end of chunk */ 301464562Sgshapiro if (i + 1 < rlen) 301564562Sgshapiro { 301664562Sgshapiro /* If LF, strip CR */ 301764562Sgshapiro if (response[i + 1] == '\n') 301864562Sgshapiro i++; 301964562Sgshapiro } 302064562Sgshapiro else 302164562Sgshapiro { 302264562Sgshapiro /* check next chunk */ 302364562Sgshapiro prevchar = '\r'; 302464562Sgshapiro continue; 302564562Sgshapiro } 302664562Sgshapiro } 302790792Sgshapiro (void) sm_io_putc(e->e_dfp, SM_TIME_DEFAULT, response[i]); 302864562Sgshapiro e->e_msgsize++; 302964562Sgshapiro } 303064562Sgshapiro return 0; 303164562Sgshapiro} 303264562Sgshapiro 303364562Sgshapiro/* 303464562Sgshapiro** MTA callouts 303564562Sgshapiro*/ 303664562Sgshapiro 303790792Sgshapiro/* 303864562Sgshapiro** MILTER_INIT -- open and negotiate with all of the filters 303964562Sgshapiro** 304064562Sgshapiro** Parameters: 304164562Sgshapiro** e -- current envelope. 304264562Sgshapiro** state -- return state from response. 304364562Sgshapiro** 304464562Sgshapiro** Returns: 304590792Sgshapiro** true iff at least one filter is active 304664562Sgshapiro*/ 304764562Sgshapiro 304864562Sgshapiro/* ARGSUSED */ 304990792Sgshapirobool 305064562Sgshapiromilter_init(e, state) 305164562Sgshapiro ENVELOPE *e; 305264562Sgshapiro char *state; 305364562Sgshapiro{ 305464562Sgshapiro int i; 305564562Sgshapiro 305664562Sgshapiro if (tTd(64, 10)) 305790792Sgshapiro sm_dprintf("milter_init\n"); 305864562Sgshapiro 305964562Sgshapiro *state = SMFIR_CONTINUE; 306090792Sgshapiro if (InputFilters[0] == NULL) 306190792Sgshapiro { 306290792Sgshapiro if (MilterLogLevel > 10) 306390792Sgshapiro sm_syslog(LOG_INFO, e->e_id, 306490792Sgshapiro "Milter: no active filter"); 306590792Sgshapiro return false; 306690792Sgshapiro } 306790792Sgshapiro 306864562Sgshapiro for (i = 0; InputFilters[i] != NULL; i++) 306964562Sgshapiro { 307064562Sgshapiro struct milter *m = InputFilters[i]; 307164562Sgshapiro 307290792Sgshapiro m->mf_sock = milter_open(m, false, e); 307364562Sgshapiro if (m->mf_state == SMFS_ERROR) 307464562Sgshapiro { 3075112810Sgshapiro MILTER_CHECK_ERROR(true, continue); 307664562Sgshapiro break; 307764562Sgshapiro } 307864562Sgshapiro 307964562Sgshapiro if (m->mf_sock < 0 || 308064562Sgshapiro milter_negotiate(m, e) < 0 || 308164562Sgshapiro m->mf_state == SMFS_ERROR) 308264562Sgshapiro { 308364562Sgshapiro if (tTd(64, 5)) 308490792Sgshapiro sm_dprintf("milter_init(%s): failed to %s\n", 308590792Sgshapiro m->mf_name, 308690792Sgshapiro m->mf_sock < 0 ? "open" : 308790792Sgshapiro "negotiate"); 308890792Sgshapiro if (MilterLogLevel > 0) 308990792Sgshapiro sm_syslog(LOG_ERR, e->e_id, 309090792Sgshapiro "Milter (%s): init failed to %s", 309190792Sgshapiro m->mf_name, 309290792Sgshapiro m->mf_sock < 0 ? "open" : 309390792Sgshapiro "negotiate"); 309464562Sgshapiro 309564562Sgshapiro /* if negotation failure, close socket */ 309690792Sgshapiro milter_error(m, e); 3097112810Sgshapiro MILTER_CHECK_ERROR(true, continue); 309864562Sgshapiro } 309990792Sgshapiro if (MilterLogLevel > 9) 310090792Sgshapiro sm_syslog(LOG_INFO, e->e_id, 310190792Sgshapiro "Milter (%s): init success to %s", 310290792Sgshapiro m->mf_name, 310390792Sgshapiro m->mf_sock < 0 ? "open" : "negotiate"); 310464562Sgshapiro } 310564562Sgshapiro 310664562Sgshapiro /* 310764562Sgshapiro ** If something temp/perm failed with one of the filters, 310864562Sgshapiro ** we won't be using any of them, so clear any existing 310964562Sgshapiro ** connections. 311064562Sgshapiro */ 311164562Sgshapiro 311264562Sgshapiro if (*state != SMFIR_CONTINUE) 311364562Sgshapiro milter_quit(e); 311490792Sgshapiro 311590792Sgshapiro return true; 311664562Sgshapiro} 311790792Sgshapiro/* 311864562Sgshapiro** MILTER_CONNECT -- send connection info to milter filters 311964562Sgshapiro** 312064562Sgshapiro** Parameters: 312164562Sgshapiro** hostname -- hostname of remote machine. 312264562Sgshapiro** addr -- address of remote machine. 312364562Sgshapiro** e -- current envelope. 312464562Sgshapiro** state -- return state from response. 312564562Sgshapiro** 312664562Sgshapiro** Returns: 312764562Sgshapiro** response string (may be NULL) 312864562Sgshapiro*/ 312964562Sgshapiro 313064562Sgshapirochar * 313164562Sgshapiromilter_connect(hostname, addr, e, state) 313264562Sgshapiro char *hostname; 313364562Sgshapiro SOCKADDR addr; 313464562Sgshapiro ENVELOPE *e; 313564562Sgshapiro char *state; 313664562Sgshapiro{ 313764562Sgshapiro char family; 313890792Sgshapiro unsigned short port; 313964562Sgshapiro char *buf, *bp; 314064562Sgshapiro char *response; 314164562Sgshapiro char *sockinfo = NULL; 314264562Sgshapiro ssize_t s; 314364562Sgshapiro# if NETINET6 314464562Sgshapiro char buf6[INET6_ADDRSTRLEN]; 314564562Sgshapiro# endif /* NETINET6 */ 314664562Sgshapiro 314764562Sgshapiro if (tTd(64, 10)) 314890792Sgshapiro sm_dprintf("milter_connect(%s)\n", hostname); 314990792Sgshapiro if (MilterLogLevel > 9) 315090792Sgshapiro sm_syslog(LOG_INFO, e->e_id, "Milter: connect to filters"); 315164562Sgshapiro 315264562Sgshapiro /* gather data */ 315364562Sgshapiro switch (addr.sa.sa_family) 315464562Sgshapiro { 315564562Sgshapiro# if NETUNIX 315664562Sgshapiro case AF_UNIX: 315764562Sgshapiro family = SMFIA_UNIX; 315864562Sgshapiro port = htons(0); 315964562Sgshapiro sockinfo = addr.sunix.sun_path; 316064562Sgshapiro break; 316164562Sgshapiro# endif /* NETUNIX */ 316264562Sgshapiro 316364562Sgshapiro# if NETINET 316464562Sgshapiro case AF_INET: 316564562Sgshapiro family = SMFIA_INET; 316694334Sgshapiro port = addr.sin.sin_port; 316764562Sgshapiro sockinfo = (char *) inet_ntoa(addr.sin.sin_addr); 316864562Sgshapiro break; 316964562Sgshapiro# endif /* NETINET */ 317064562Sgshapiro 317164562Sgshapiro# if NETINET6 317264562Sgshapiro case AF_INET6: 317380785Sgshapiro if (IN6_IS_ADDR_V4MAPPED(&addr.sin6.sin6_addr)) 317480785Sgshapiro family = SMFIA_INET; 317580785Sgshapiro else 317680785Sgshapiro family = SMFIA_INET6; 317794334Sgshapiro port = addr.sin6.sin6_port; 317864562Sgshapiro sockinfo = anynet_ntop(&addr.sin6.sin6_addr, buf6, 317964562Sgshapiro sizeof buf6); 318064562Sgshapiro if (sockinfo == NULL) 318164562Sgshapiro sockinfo = ""; 318264562Sgshapiro break; 318364562Sgshapiro# endif /* NETINET6 */ 318464562Sgshapiro 318564562Sgshapiro default: 318664562Sgshapiro family = SMFIA_UNKNOWN; 318764562Sgshapiro break; 318864562Sgshapiro } 318964562Sgshapiro 319064562Sgshapiro s = strlen(hostname) + 1 + sizeof(family); 319164562Sgshapiro if (family != SMFIA_UNKNOWN) 319264562Sgshapiro s += sizeof(port) + strlen(sockinfo) + 1; 319364562Sgshapiro 319490792Sgshapiro buf = (char *) xalloc(s); 319564562Sgshapiro bp = buf; 319664562Sgshapiro 319764562Sgshapiro /* put together data */ 319864562Sgshapiro (void) memcpy(bp, hostname, strlen(hostname)); 319964562Sgshapiro bp += strlen(hostname); 320064562Sgshapiro *bp++ = '\0'; 320164562Sgshapiro (void) memcpy(bp, &family, sizeof family); 320264562Sgshapiro bp += sizeof family; 320364562Sgshapiro if (family != SMFIA_UNKNOWN) 320464562Sgshapiro { 320564562Sgshapiro (void) memcpy(bp, &port, sizeof port); 320664562Sgshapiro bp += sizeof port; 320764562Sgshapiro 320864562Sgshapiro /* include trailing '\0' */ 320964562Sgshapiro (void) memcpy(bp, sockinfo, strlen(sockinfo) + 1); 321064562Sgshapiro } 321164562Sgshapiro 321264562Sgshapiro response = milter_command(SMFIC_CONNECT, buf, s, 321364562Sgshapiro MilterConnectMacros, e, state); 321490792Sgshapiro sm_free(buf); /* XXX */ 321564562Sgshapiro 321664562Sgshapiro /* 321764562Sgshapiro ** If this message connection is done for, 321864562Sgshapiro ** close the filters. 321964562Sgshapiro */ 322064562Sgshapiro 322164562Sgshapiro if (*state != SMFIR_CONTINUE) 322290792Sgshapiro { 322390792Sgshapiro if (MilterLogLevel > 9) 322490792Sgshapiro sm_syslog(LOG_INFO, e->e_id, "Milter: connect, ending"); 322564562Sgshapiro milter_quit(e); 322690792Sgshapiro } 322764562Sgshapiro else 322864562Sgshapiro milter_per_connection_check(e); 322964562Sgshapiro 323064562Sgshapiro /* 323164562Sgshapiro ** SMFIR_REPLYCODE can't work with connect due to 323264562Sgshapiro ** the requirements of SMTP. Therefore, ignore the 323364562Sgshapiro ** reply code text but keep the state it would reflect. 323464562Sgshapiro */ 323564562Sgshapiro 323664562Sgshapiro if (*state == SMFIR_REPLYCODE) 323764562Sgshapiro { 323864562Sgshapiro if (response != NULL && 323964562Sgshapiro *response == '4') 3240110560Sgshapiro { 3241110560Sgshapiro#if _FFR_MILTER_421 3242110560Sgshapiro if (strncmp(response, "421 ", 4) == 0) 3243110560Sgshapiro *state = SMFIR_SHUTDOWN; 3244110560Sgshapiro else 3245110560Sgshapiro#endif /* _FFR_MILTER_421 */ 3246110560Sgshapiro *state = SMFIR_TEMPFAIL; 3247110560Sgshapiro } 324864562Sgshapiro else 324964562Sgshapiro *state = SMFIR_REJECT; 325064562Sgshapiro if (response != NULL) 325164562Sgshapiro { 325290792Sgshapiro sm_free(response); /* XXX */ 325364562Sgshapiro response = NULL; 325464562Sgshapiro } 325564562Sgshapiro } 325664562Sgshapiro return response; 325764562Sgshapiro} 325890792Sgshapiro/* 325964562Sgshapiro** MILTER_HELO -- send SMTP HELO/EHLO command info to milter filters 326064562Sgshapiro** 326164562Sgshapiro** Parameters: 326264562Sgshapiro** helo -- argument to SMTP HELO/EHLO command. 326364562Sgshapiro** e -- current envelope. 326464562Sgshapiro** state -- return state from response. 326564562Sgshapiro** 326664562Sgshapiro** Returns: 326764562Sgshapiro** response string (may be NULL) 326864562Sgshapiro*/ 326964562Sgshapiro 327064562Sgshapirochar * 327164562Sgshapiromilter_helo(helo, e, state) 327264562Sgshapiro char *helo; 327364562Sgshapiro ENVELOPE *e; 327464562Sgshapiro char *state; 327564562Sgshapiro{ 327673188Sgshapiro int i; 327764562Sgshapiro char *response; 327864562Sgshapiro 327964562Sgshapiro if (tTd(64, 10)) 328090792Sgshapiro sm_dprintf("milter_helo(%s)\n", helo); 328164562Sgshapiro 328290792Sgshapiro /* HELO/EHLO can come at any point */ 328373188Sgshapiro for (i = 0; InputFilters[i] != NULL; i++) 328473188Sgshapiro { 328573188Sgshapiro struct milter *m = InputFilters[i]; 328673188Sgshapiro 328773188Sgshapiro switch (m->mf_state) 328873188Sgshapiro { 328973188Sgshapiro case SMFS_INMSG: 329073188Sgshapiro /* abort in message filters */ 329173188Sgshapiro milter_abort_filter(m, e); 329273188Sgshapiro /* FALLTHROUGH */ 329373188Sgshapiro 329473188Sgshapiro case SMFS_DONE: 329573188Sgshapiro /* reset done filters */ 329673188Sgshapiro m->mf_state = SMFS_OPEN; 329773188Sgshapiro break; 329873188Sgshapiro } 329973188Sgshapiro } 330073188Sgshapiro 330164562Sgshapiro response = milter_command(SMFIC_HELO, helo, strlen(helo) + 1, 330264562Sgshapiro MilterHeloMacros, e, state); 330364562Sgshapiro milter_per_connection_check(e); 330464562Sgshapiro return response; 330564562Sgshapiro} 330690792Sgshapiro/* 330764562Sgshapiro** MILTER_ENVFROM -- send SMTP MAIL command info to milter filters 330864562Sgshapiro** 330964562Sgshapiro** Parameters: 331064562Sgshapiro** args -- SMTP MAIL command args (args[0] == sender). 331164562Sgshapiro** e -- current envelope. 331264562Sgshapiro** state -- return state from response. 331364562Sgshapiro** 331464562Sgshapiro** Returns: 331564562Sgshapiro** response string (may be NULL) 331664562Sgshapiro*/ 331764562Sgshapiro 331864562Sgshapirochar * 331964562Sgshapiromilter_envfrom(args, e, state) 332064562Sgshapiro char **args; 332164562Sgshapiro ENVELOPE *e; 332264562Sgshapiro char *state; 332364562Sgshapiro{ 332464562Sgshapiro int i; 332564562Sgshapiro char *buf, *bp; 332664562Sgshapiro char *response; 332764562Sgshapiro ssize_t s; 332864562Sgshapiro 332964562Sgshapiro if (tTd(64, 10)) 333064562Sgshapiro { 333190792Sgshapiro sm_dprintf("milter_envfrom:"); 333264562Sgshapiro for (i = 0; args[i] != NULL; i++) 333390792Sgshapiro sm_dprintf(" %s", args[i]); 333490792Sgshapiro sm_dprintf("\n"); 333564562Sgshapiro } 333664562Sgshapiro 333764562Sgshapiro /* sanity check */ 333864562Sgshapiro if (args[0] == NULL) 333964562Sgshapiro { 334064562Sgshapiro *state = SMFIR_REJECT; 334190792Sgshapiro if (MilterLogLevel > 10) 334290792Sgshapiro sm_syslog(LOG_INFO, e->e_id, 334390792Sgshapiro "Milter: reject, no sender"); 334464562Sgshapiro return NULL; 334564562Sgshapiro } 334664562Sgshapiro 334764562Sgshapiro /* new message, so ... */ 334864562Sgshapiro for (i = 0; InputFilters[i] != NULL; i++) 334964562Sgshapiro { 335064562Sgshapiro struct milter *m = InputFilters[i]; 335164562Sgshapiro 335264562Sgshapiro switch (m->mf_state) 335364562Sgshapiro { 335464562Sgshapiro case SMFS_INMSG: 335564562Sgshapiro /* abort in message filters */ 335664562Sgshapiro milter_abort_filter(m, e); 335764562Sgshapiro /* FALLTHROUGH */ 335864562Sgshapiro 335964562Sgshapiro case SMFS_DONE: 336064562Sgshapiro /* reset done filters */ 336164562Sgshapiro m->mf_state = SMFS_OPEN; 336264562Sgshapiro break; 336364562Sgshapiro } 336464562Sgshapiro } 336564562Sgshapiro 336664562Sgshapiro /* put together data */ 336764562Sgshapiro s = 0; 336864562Sgshapiro for (i = 0; args[i] != NULL; i++) 336964562Sgshapiro s += strlen(args[i]) + 1; 337090792Sgshapiro 337190792Sgshapiro if (s < 0) 337290792Sgshapiro { 337390792Sgshapiro *state = SMFIR_TEMPFAIL; 337490792Sgshapiro return NULL; 337590792Sgshapiro } 337690792Sgshapiro 337790792Sgshapiro buf = (char *) xalloc(s); 337864562Sgshapiro bp = buf; 337964562Sgshapiro for (i = 0; args[i] != NULL; i++) 338064562Sgshapiro { 338190792Sgshapiro (void) sm_strlcpy(bp, args[i], s - (bp - buf)); 338264562Sgshapiro bp += strlen(bp) + 1; 338364562Sgshapiro } 338464562Sgshapiro 338590792Sgshapiro if (MilterLogLevel > 14) 338690792Sgshapiro sm_syslog(LOG_INFO, e->e_id, "Milter: senders: %s", buf); 338790792Sgshapiro 338864562Sgshapiro /* send it over */ 338964562Sgshapiro response = milter_command(SMFIC_MAIL, buf, s, 339064562Sgshapiro MilterEnvFromMacros, e, state); 339190792Sgshapiro sm_free(buf); /* XXX */ 339264562Sgshapiro 339364562Sgshapiro /* 339464562Sgshapiro ** If filter rejects/discards a per message command, 339564562Sgshapiro ** abort the other filters since we are done with the 339664562Sgshapiro ** current message. 339764562Sgshapiro */ 339864562Sgshapiro 339964562Sgshapiro MILTER_CHECK_DONE_MSG(); 340090792Sgshapiro if (MilterLogLevel > 10 && *state == SMFIR_REJECT) 340190792Sgshapiro sm_syslog(LOG_INFO, e->e_id, "Milter: reject, senders"); 340264562Sgshapiro return response; 340364562Sgshapiro} 340490792Sgshapiro/* 340564562Sgshapiro** MILTER_ENVRCPT -- send SMTP RCPT command info to milter filters 340664562Sgshapiro** 340764562Sgshapiro** Parameters: 340864562Sgshapiro** args -- SMTP MAIL command args (args[0] == recipient). 340964562Sgshapiro** e -- current envelope. 341064562Sgshapiro** state -- return state from response. 341164562Sgshapiro** 341264562Sgshapiro** Returns: 341364562Sgshapiro** response string (may be NULL) 341464562Sgshapiro*/ 341564562Sgshapiro 341664562Sgshapirochar * 341764562Sgshapiromilter_envrcpt(args, e, state) 341864562Sgshapiro char **args; 341964562Sgshapiro ENVELOPE *e; 342064562Sgshapiro char *state; 342164562Sgshapiro{ 342264562Sgshapiro int i; 342364562Sgshapiro char *buf, *bp; 342464562Sgshapiro char *response; 342564562Sgshapiro ssize_t s; 342664562Sgshapiro 342764562Sgshapiro if (tTd(64, 10)) 342864562Sgshapiro { 342990792Sgshapiro sm_dprintf("milter_envrcpt:"); 343064562Sgshapiro for (i = 0; args[i] != NULL; i++) 343190792Sgshapiro sm_dprintf(" %s", args[i]); 343290792Sgshapiro sm_dprintf("\n"); 343364562Sgshapiro } 343464562Sgshapiro 343564562Sgshapiro /* sanity check */ 343664562Sgshapiro if (args[0] == NULL) 343764562Sgshapiro { 343864562Sgshapiro *state = SMFIR_REJECT; 343990792Sgshapiro if (MilterLogLevel > 10) 344090792Sgshapiro sm_syslog(LOG_INFO, e->e_id, "Milter: reject, no rcpt"); 344164562Sgshapiro return NULL; 344264562Sgshapiro } 344364562Sgshapiro 344464562Sgshapiro /* put together data */ 344564562Sgshapiro s = 0; 344664562Sgshapiro for (i = 0; args[i] != NULL; i++) 344764562Sgshapiro s += strlen(args[i]) + 1; 344890792Sgshapiro 344990792Sgshapiro if (s < 0) 345090792Sgshapiro { 345190792Sgshapiro *state = SMFIR_TEMPFAIL; 345290792Sgshapiro return NULL; 345390792Sgshapiro } 345490792Sgshapiro 345590792Sgshapiro buf = (char *) xalloc(s); 345664562Sgshapiro bp = buf; 345764562Sgshapiro for (i = 0; args[i] != NULL; i++) 345864562Sgshapiro { 345990792Sgshapiro (void) sm_strlcpy(bp, args[i], s - (bp - buf)); 346064562Sgshapiro bp += strlen(bp) + 1; 346164562Sgshapiro } 346264562Sgshapiro 346390792Sgshapiro if (MilterLogLevel > 14) 346490792Sgshapiro sm_syslog(LOG_INFO, e->e_id, "Milter: rcpts: %s", buf); 346590792Sgshapiro 346664562Sgshapiro /* send it over */ 346764562Sgshapiro response = milter_command(SMFIC_RCPT, buf, s, 346864562Sgshapiro MilterEnvRcptMacros, e, state); 346990792Sgshapiro sm_free(buf); /* XXX */ 347064562Sgshapiro return response; 347164562Sgshapiro} 347290792Sgshapiro/* 347364562Sgshapiro** MILTER_DATA -- send message headers/body and gather final message results 347464562Sgshapiro** 347564562Sgshapiro** Parameters: 347664562Sgshapiro** e -- current envelope. 347764562Sgshapiro** state -- return state from response. 347864562Sgshapiro** 347964562Sgshapiro** Returns: 348064562Sgshapiro** response string (may be NULL) 348164562Sgshapiro** 348264562Sgshapiro** Side effects: 348364562Sgshapiro** - Uses e->e_dfp for access to the body 348464562Sgshapiro** - Can call the various milter action routines to 348564562Sgshapiro** modify the envelope or message. 348664562Sgshapiro*/ 348764562Sgshapiro 348864562Sgshapiro# define MILTER_CHECK_RESULTS() \ 348964562Sgshapiro if (*state == SMFIR_ACCEPT || \ 349064562Sgshapiro m->mf_state == SMFS_DONE || \ 349164562Sgshapiro m->mf_state == SMFS_ERROR) \ 349264562Sgshapiro { \ 349364562Sgshapiro if (m->mf_state != SMFS_ERROR) \ 349464562Sgshapiro m->mf_state = SMFS_DONE; \ 349564562Sgshapiro continue; /* to next filter */ \ 349664562Sgshapiro } \ 349764562Sgshapiro if (*state != SMFIR_CONTINUE) \ 349864562Sgshapiro { \ 349964562Sgshapiro m->mf_state = SMFS_DONE; \ 350064562Sgshapiro goto finishup; \ 350164562Sgshapiro } 350264562Sgshapiro 350364562Sgshapirochar * 350464562Sgshapiromilter_data(e, state) 350564562Sgshapiro ENVELOPE *e; 350664562Sgshapiro char *state; 350764562Sgshapiro{ 350890792Sgshapiro bool replbody = false; /* milter_replbody() called? */ 350990792Sgshapiro bool replfailed = false; /* milter_replbody() failed? */ 351090792Sgshapiro bool rewind = false; /* rewind data file? */ 351190792Sgshapiro bool dfopen = false; /* data file open for writing? */ 351264562Sgshapiro bool newfilter; /* reset on each new filter */ 351364562Sgshapiro char rcmd; 351464562Sgshapiro int i; 351564562Sgshapiro int save_errno; 351664562Sgshapiro char *response = NULL; 351764562Sgshapiro time_t eomsent; 351864562Sgshapiro ssize_t rlen; 351964562Sgshapiro 352064562Sgshapiro if (tTd(64, 10)) 352190792Sgshapiro sm_dprintf("milter_data\n"); 352264562Sgshapiro 352364562Sgshapiro *state = SMFIR_CONTINUE; 352464562Sgshapiro 352564562Sgshapiro /* 352664562Sgshapiro ** XXX: Should actually send body chunks to each filter 352764562Sgshapiro ** a chunk at a time instead of sending the whole body to 352864562Sgshapiro ** each filter in turn. However, only if the filters don't 352964562Sgshapiro ** change the body. 353064562Sgshapiro */ 353164562Sgshapiro 353264562Sgshapiro for (i = 0; InputFilters[i] != NULL; i++) 353364562Sgshapiro { 353464562Sgshapiro struct milter *m = InputFilters[i]; 353564562Sgshapiro 353664562Sgshapiro if (*state != SMFIR_CONTINUE && 353764562Sgshapiro *state != SMFIR_ACCEPT) 353864562Sgshapiro { 353964562Sgshapiro /* 354064562Sgshapiro ** A previous filter has dealt with the message, 354164562Sgshapiro ** safe to stop processing the filters. 354264562Sgshapiro */ 354364562Sgshapiro 354464562Sgshapiro break; 354564562Sgshapiro } 354664562Sgshapiro 354764562Sgshapiro /* Now reset state for later evaluation */ 354864562Sgshapiro *state = SMFIR_CONTINUE; 354990792Sgshapiro newfilter = true; 355064562Sgshapiro 355171345Sgshapiro /* previous problem? */ 355271345Sgshapiro if (m->mf_state == SMFS_ERROR) 355371345Sgshapiro { 3554112810Sgshapiro MILTER_CHECK_ERROR(false, continue); 355571345Sgshapiro break; 355671345Sgshapiro } 355771345Sgshapiro 355864562Sgshapiro /* sanity checks */ 355964562Sgshapiro if (m->mf_sock < 0 || 356064562Sgshapiro (m->mf_state != SMFS_OPEN && m->mf_state != SMFS_INMSG)) 356164562Sgshapiro continue; 356264562Sgshapiro 356364562Sgshapiro m->mf_state = SMFS_INMSG; 356464562Sgshapiro 356564562Sgshapiro /* check if filter wants the headers */ 356664562Sgshapiro if (!bitset(SMFIP_NOHDRS, m->mf_pflags)) 356764562Sgshapiro { 356864562Sgshapiro response = milter_headers(m, e, state); 356964562Sgshapiro MILTER_CHECK_RESULTS(); 357064562Sgshapiro } 357164562Sgshapiro 357264562Sgshapiro /* check if filter wants EOH */ 357364562Sgshapiro if (!bitset(SMFIP_NOEOH, m->mf_pflags)) 357464562Sgshapiro { 357564562Sgshapiro if (tTd(64, 10)) 357690792Sgshapiro sm_dprintf("milter_data: eoh\n"); 357764562Sgshapiro 357864562Sgshapiro /* send it over */ 357964562Sgshapiro response = milter_send_command(m, SMFIC_EOH, NULL, 0, 358064562Sgshapiro e, state); 358164562Sgshapiro MILTER_CHECK_RESULTS(); 358264562Sgshapiro } 358364562Sgshapiro 358464562Sgshapiro /* check if filter wants the body */ 358564562Sgshapiro if (!bitset(SMFIP_NOBODY, m->mf_pflags) && 358664562Sgshapiro e->e_dfp != NULL) 358764562Sgshapiro { 358890792Sgshapiro rewind = true; 358964562Sgshapiro response = milter_body(m, e, state); 359064562Sgshapiro MILTER_CHECK_RESULTS(); 359164562Sgshapiro } 359264562Sgshapiro 359364562Sgshapiro /* send the final body chunk */ 359464562Sgshapiro (void) milter_write(m, SMFIC_BODYEOB, NULL, 0, 359564562Sgshapiro m->mf_timeout[SMFTO_WRITE], e); 359664562Sgshapiro 359764562Sgshapiro /* Get time EOM sent for timeout */ 359864562Sgshapiro eomsent = curtime(); 359964562Sgshapiro 360064562Sgshapiro /* deal with the possibility of multiple responses */ 360164562Sgshapiro while (*state == SMFIR_CONTINUE) 360264562Sgshapiro { 360364562Sgshapiro /* Check total timeout from EOM to final ACK/NAK */ 360464562Sgshapiro if (m->mf_timeout[SMFTO_EOM] > 0 && 360564562Sgshapiro curtime() - eomsent >= m->mf_timeout[SMFTO_EOM]) 360664562Sgshapiro { 360764562Sgshapiro if (tTd(64, 5)) 360890792Sgshapiro sm_dprintf("milter_data(%s): EOM ACK/NAK timeout\n", 360964562Sgshapiro m->mf_name); 361090792Sgshapiro if (MilterLogLevel > 0) 361164562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 361290792Sgshapiro "milter_data(%s): EOM ACK/NAK timeout", 361364562Sgshapiro m->mf_name); 361490792Sgshapiro milter_error(m, e); 3615112810Sgshapiro MILTER_CHECK_ERROR(false, break); 361664562Sgshapiro break; 361764562Sgshapiro } 361864562Sgshapiro 361964562Sgshapiro response = milter_read(m, &rcmd, &rlen, 362064562Sgshapiro m->mf_timeout[SMFTO_READ], e); 362164562Sgshapiro if (m->mf_state == SMFS_ERROR) 362264562Sgshapiro break; 362364562Sgshapiro 362464562Sgshapiro if (tTd(64, 10)) 362590792Sgshapiro sm_dprintf("milter_data(%s): state %c\n", 362690792Sgshapiro m->mf_name, (char) rcmd); 362764562Sgshapiro 362864562Sgshapiro switch (rcmd) 362964562Sgshapiro { 363064562Sgshapiro case SMFIR_REPLYCODE: 363164562Sgshapiro MILTER_CHECK_REPLYCODE("554 5.7.1 Command rejected"); 363290792Sgshapiro if (MilterLogLevel > 12) 363390792Sgshapiro sm_syslog(LOG_INFO, e->e_id, "milter=%s, reject=%s", 363490792Sgshapiro m->mf_name, response); 363564562Sgshapiro *state = rcmd; 363664562Sgshapiro m->mf_state = SMFS_DONE; 363764562Sgshapiro break; 363864562Sgshapiro 363990792Sgshapiro case SMFIR_REJECT: /* log msg at end of function */ 364090792Sgshapiro if (MilterLogLevel > 12) 364190792Sgshapiro sm_syslog(LOG_INFO, e->e_id, "milter=%s, reject", 364290792Sgshapiro m->mf_name); 364390792Sgshapiro *state = rcmd; 364490792Sgshapiro m->mf_state = SMFS_DONE; 364590792Sgshapiro break; 364690792Sgshapiro 364764562Sgshapiro case SMFIR_DISCARD: 364890792Sgshapiro if (MilterLogLevel > 12) 364990792Sgshapiro sm_syslog(LOG_INFO, e->e_id, "milter=%s, discard", 365090792Sgshapiro m->mf_name); 365190792Sgshapiro *state = rcmd; 365290792Sgshapiro m->mf_state = SMFS_DONE; 365390792Sgshapiro break; 365490792Sgshapiro 365564562Sgshapiro case SMFIR_TEMPFAIL: 365690792Sgshapiro if (MilterLogLevel > 12) 365790792Sgshapiro sm_syslog(LOG_INFO, e->e_id, "milter=%s, tempfail", 365890792Sgshapiro m->mf_name); 365964562Sgshapiro *state = rcmd; 366064562Sgshapiro m->mf_state = SMFS_DONE; 366164562Sgshapiro break; 366264562Sgshapiro 366364562Sgshapiro case SMFIR_CONTINUE: 366464562Sgshapiro case SMFIR_ACCEPT: 366564562Sgshapiro /* this filter is done with message */ 366664562Sgshapiro if (replfailed) 366764562Sgshapiro *state = SMFIR_TEMPFAIL; 366864562Sgshapiro else 366964562Sgshapiro *state = SMFIR_ACCEPT; 367064562Sgshapiro m->mf_state = SMFS_DONE; 367164562Sgshapiro break; 367264562Sgshapiro 367364562Sgshapiro case SMFIR_PROGRESS: 367464562Sgshapiro break; 367564562Sgshapiro 367690792Sgshapiro# if _FFR_QUARANTINE 367790792Sgshapiro case SMFIR_QUARANTINE: 367890792Sgshapiro if (!bitset(SMFIF_QUARANTINE, m->mf_fflags)) 367990792Sgshapiro { 368090792Sgshapiro if (MilterLogLevel > 9) 368190792Sgshapiro sm_syslog(LOG_WARNING, e->e_id, 368290792Sgshapiro "milter_data(%s): lied about quarantining, honoring request anyway", 368390792Sgshapiro m->mf_name); 368490792Sgshapiro } 368590792Sgshapiro if (response == NULL) 368690792Sgshapiro response = newstr(""); 368790792Sgshapiro if (MilterLogLevel > 3) 368890792Sgshapiro sm_syslog(LOG_INFO, e->e_id, 368990792Sgshapiro "milter=%s, quarantine=%s", 369090792Sgshapiro m->mf_name, response); 369190792Sgshapiro e->e_quarmsg = sm_rpool_strdup_x(e->e_rpool, 369290792Sgshapiro response); 369390792Sgshapiro macdefine(&e->e_macro, A_PERM, 369490792Sgshapiro macid("{quarantine}"), e->e_quarmsg); 369590792Sgshapiro break; 369690792Sgshapiro# endif /* _FFR_QUARANTINE */ 369790792Sgshapiro 369864562Sgshapiro case SMFIR_ADDHEADER: 369964562Sgshapiro if (!bitset(SMFIF_ADDHDRS, m->mf_fflags)) 370064562Sgshapiro { 370190792Sgshapiro if (MilterLogLevel > 9) 370264562Sgshapiro sm_syslog(LOG_WARNING, e->e_id, 370364562Sgshapiro "milter_data(%s): lied about adding headers, honoring request anyway", 370464562Sgshapiro m->mf_name); 370564562Sgshapiro } 370664562Sgshapiro milter_addheader(response, rlen, e); 370764562Sgshapiro break; 370864562Sgshapiro 370964562Sgshapiro case SMFIR_CHGHEADER: 371064562Sgshapiro if (!bitset(SMFIF_CHGHDRS, m->mf_fflags)) 371164562Sgshapiro { 371290792Sgshapiro if (MilterLogLevel > 9) 371364562Sgshapiro sm_syslog(LOG_WARNING, e->e_id, 371464562Sgshapiro "milter_data(%s): lied about changing headers, honoring request anyway", 371564562Sgshapiro m->mf_name); 371664562Sgshapiro } 371764562Sgshapiro milter_changeheader(response, rlen, e); 371864562Sgshapiro break; 371964562Sgshapiro 372064562Sgshapiro case SMFIR_ADDRCPT: 372164562Sgshapiro if (!bitset(SMFIF_ADDRCPT, m->mf_fflags)) 372264562Sgshapiro { 372390792Sgshapiro if (MilterLogLevel > 9) 372464562Sgshapiro sm_syslog(LOG_WARNING, e->e_id, 372564562Sgshapiro "milter_data(%s) lied about adding recipients, honoring request anyway", 372664562Sgshapiro m->mf_name); 372764562Sgshapiro } 372864562Sgshapiro milter_addrcpt(response, rlen, e); 372964562Sgshapiro break; 373064562Sgshapiro 373164562Sgshapiro case SMFIR_DELRCPT: 373264562Sgshapiro if (!bitset(SMFIF_DELRCPT, m->mf_fflags)) 373364562Sgshapiro { 373490792Sgshapiro if (MilterLogLevel > 9) 373564562Sgshapiro sm_syslog(LOG_WARNING, e->e_id, 373664562Sgshapiro "milter_data(%s): lied about removing recipients, honoring request anyway", 373764562Sgshapiro m->mf_name); 373864562Sgshapiro } 373964562Sgshapiro milter_delrcpt(response, rlen, e); 374064562Sgshapiro break; 374164562Sgshapiro 374264562Sgshapiro case SMFIR_REPLBODY: 374364562Sgshapiro if (!bitset(SMFIF_MODBODY, m->mf_fflags)) 374464562Sgshapiro { 3745110560Sgshapiro if (MilterLogLevel > 9) 374664562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 374764562Sgshapiro "milter_data(%s): lied about replacing body, rejecting request and tempfailing message", 374864562Sgshapiro m->mf_name); 374990792Sgshapiro replfailed = true; 375064562Sgshapiro break; 375164562Sgshapiro } 375264562Sgshapiro 375364562Sgshapiro /* already failed in attempt */ 375464562Sgshapiro if (replfailed) 375564562Sgshapiro break; 375664562Sgshapiro 375764562Sgshapiro if (!dfopen) 375864562Sgshapiro { 375964562Sgshapiro if (milter_reopen_df(e) < 0) 376064562Sgshapiro { 376190792Sgshapiro replfailed = true; 376264562Sgshapiro break; 376364562Sgshapiro } 376490792Sgshapiro dfopen = true; 376590792Sgshapiro rewind = true; 376664562Sgshapiro } 376764562Sgshapiro 376864562Sgshapiro if (milter_replbody(response, rlen, 376964562Sgshapiro newfilter, e) < 0) 377090792Sgshapiro replfailed = true; 377190792Sgshapiro newfilter = false; 377290792Sgshapiro replbody = true; 377364562Sgshapiro break; 377464562Sgshapiro 377564562Sgshapiro default: 377664562Sgshapiro /* Invalid response to command */ 377790792Sgshapiro if (MilterLogLevel > 0) 377864562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 377964562Sgshapiro "milter_data(%s): returned bogus response %c", 378064562Sgshapiro m->mf_name, rcmd); 378190792Sgshapiro milter_error(m, e); 378264562Sgshapiro break; 378364562Sgshapiro } 378490792Sgshapiro if (rcmd != SMFIR_REPLYCODE && response != NULL) 378564562Sgshapiro { 378690792Sgshapiro sm_free(response); /* XXX */ 378764562Sgshapiro response = NULL; 378864562Sgshapiro } 378964562Sgshapiro 379064562Sgshapiro if (m->mf_state == SMFS_ERROR) 379164562Sgshapiro break; 379264562Sgshapiro } 379364562Sgshapiro 379464562Sgshapiro if (replbody && !replfailed) 379564562Sgshapiro { 379664562Sgshapiro /* flush possible buffered character */ 379764562Sgshapiro milter_replbody(NULL, 0, !replbody, e); 379890792Sgshapiro replbody = false; 379964562Sgshapiro } 380064562Sgshapiro 380164562Sgshapiro if (m->mf_state == SMFS_ERROR) 380264562Sgshapiro { 3803112810Sgshapiro MILTER_CHECK_ERROR(false, continue); 380464562Sgshapiro goto finishup; 380564562Sgshapiro } 380664562Sgshapiro } 380764562Sgshapiro 380864562Sgshapirofinishup: 380964562Sgshapiro /* leave things in the expected state if we touched it */ 381064562Sgshapiro if (replfailed) 381164562Sgshapiro { 381264562Sgshapiro if (*state == SMFIR_CONTINUE || 381364562Sgshapiro *state == SMFIR_ACCEPT) 381464562Sgshapiro { 381564562Sgshapiro *state = SMFIR_TEMPFAIL; 381690792Sgshapiro SM_FREE_CLR(response); 381764562Sgshapiro } 381864562Sgshapiro 381964562Sgshapiro if (dfopen) 382064562Sgshapiro { 382190792Sgshapiro (void) sm_io_close(e->e_dfp, SM_TIME_DEFAULT); 382264562Sgshapiro e->e_dfp = NULL; 382364562Sgshapiro e->e_flags &= ~EF_HAS_DF; 382490792Sgshapiro dfopen = false; 382564562Sgshapiro } 382690792Sgshapiro rewind = false; 382764562Sgshapiro } 382864562Sgshapiro 382964562Sgshapiro if ((dfopen && milter_reset_df(e) < 0) || 383064562Sgshapiro (rewind && bfrewind(e->e_dfp) < 0)) 383164562Sgshapiro { 383264562Sgshapiro save_errno = errno; 383364562Sgshapiro ExitStat = EX_IOERR; 383464562Sgshapiro 383564562Sgshapiro /* 383664562Sgshapiro ** If filter told us to keep message but we had 383764562Sgshapiro ** an error, we can't really keep it, tempfail it. 383864562Sgshapiro */ 383964562Sgshapiro 384064562Sgshapiro if (*state == SMFIR_CONTINUE || 384164562Sgshapiro *state == SMFIR_ACCEPT) 384264562Sgshapiro { 384364562Sgshapiro *state = SMFIR_TEMPFAIL; 384490792Sgshapiro SM_FREE_CLR(response); 384564562Sgshapiro } 384664562Sgshapiro 384764562Sgshapiro errno = save_errno; 384890792Sgshapiro syserr("milter_data: %s/%cf%s: read error", 384990792Sgshapiro qid_printqueue(e->e_qgrp, e->e_qdir), 385090792Sgshapiro DATAFL_LETTER, e->e_id); 385164562Sgshapiro } 385290792Sgshapiro 385364562Sgshapiro MILTER_CHECK_DONE_MSG(); 385490792Sgshapiro if (MilterLogLevel > 10 && *state == SMFIR_REJECT) 385590792Sgshapiro sm_syslog(LOG_INFO, e->e_id, "Milter: reject, data"); 385664562Sgshapiro return response; 385764562Sgshapiro} 385890792Sgshapiro/* 385964562Sgshapiro** MILTER_QUIT -- informs the filter(s) we are done and closes connection(s) 386064562Sgshapiro** 386164562Sgshapiro** Parameters: 386264562Sgshapiro** e -- current envelope. 386364562Sgshapiro** 386464562Sgshapiro** Returns: 386564562Sgshapiro** none 386664562Sgshapiro*/ 386764562Sgshapiro 386864562Sgshapirovoid 386964562Sgshapiromilter_quit(e) 387064562Sgshapiro ENVELOPE *e; 387164562Sgshapiro{ 387264562Sgshapiro int i; 387364562Sgshapiro 387464562Sgshapiro if (tTd(64, 10)) 387590792Sgshapiro sm_dprintf("milter_quit(%s)\n", e->e_id); 387664562Sgshapiro 387764562Sgshapiro for (i = 0; InputFilters[i] != NULL; i++) 387864562Sgshapiro milter_quit_filter(InputFilters[i], e); 387964562Sgshapiro} 388090792Sgshapiro/* 388164562Sgshapiro** MILTER_ABORT -- informs the filter(s) that we are aborting current message 388264562Sgshapiro** 388364562Sgshapiro** Parameters: 388464562Sgshapiro** e -- current envelope. 388564562Sgshapiro** 388664562Sgshapiro** Returns: 388764562Sgshapiro** none 388864562Sgshapiro*/ 388964562Sgshapiro 389064562Sgshapirovoid 389164562Sgshapiromilter_abort(e) 389264562Sgshapiro ENVELOPE *e; 389364562Sgshapiro{ 389464562Sgshapiro int i; 389564562Sgshapiro 389664562Sgshapiro if (tTd(64, 10)) 389790792Sgshapiro sm_dprintf("milter_abort\n"); 389864562Sgshapiro 389964562Sgshapiro for (i = 0; InputFilters[i] != NULL; i++) 390064562Sgshapiro { 390164562Sgshapiro struct milter *m = InputFilters[i]; 390264562Sgshapiro 390364562Sgshapiro /* sanity checks */ 390464562Sgshapiro if (m->mf_sock < 0 || m->mf_state != SMFS_INMSG) 390564562Sgshapiro continue; 390664562Sgshapiro 390764562Sgshapiro milter_abort_filter(m, e); 390864562Sgshapiro } 390964562Sgshapiro} 391090792Sgshapiro#endif /* MILTER */ 3911