milter.c revision 94334
164562Sgshapiro/* 294334Sgshapiro * Copyright (c) 1999-2002 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 1394334SgshapiroSM_RCSID("@(#)$Id: milter.c,v 8.194 2002/03/05 00:23:47 gshapiro 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 4864562Sgshapiro# define MILTER_CHECK_ERROR(action) \ 4964562Sgshapiro if (bitnset(SMF_TEMPFAIL, m->mf_flags)) \ 5064562Sgshapiro *state = SMFIR_TEMPFAIL; \ 5164562Sgshapiro else if (bitnset(SMF_REJECT, m->mf_flags)) \ 5264562Sgshapiro *state = SMFIR_REJECT; \ 5364562Sgshapiro else \ 5464562Sgshapiro action; 5564562Sgshapiro 5664562Sgshapiro# define MILTER_CHECK_REPLYCODE(default) \ 5764562Sgshapiro if (response == NULL || \ 5864562Sgshapiro strlen(response) + 1 != (size_t) rlen || \ 5964562Sgshapiro rlen < 3 || \ 6064562Sgshapiro (response[0] != '4' && response[0] != '5') || \ 6164562Sgshapiro !isascii(response[1]) || !isdigit(response[1]) || \ 6264562Sgshapiro !isascii(response[2]) || !isdigit(response[2])) \ 6364562Sgshapiro { \ 6464562Sgshapiro if (response != NULL) \ 6590792Sgshapiro sm_free(response); /* XXX */ \ 6664562Sgshapiro response = newstr(default); \ 6764562Sgshapiro } \ 6864562Sgshapiro else \ 6964562Sgshapiro { \ 7064562Sgshapiro char *ptr = response; \ 7164562Sgshapiro \ 7264562Sgshapiro /* Check for unprotected %'s in the string */ \ 7364562Sgshapiro while (*ptr != '\0') \ 7464562Sgshapiro { \ 7564562Sgshapiro if (*ptr == '%' && *++ptr != '%') \ 7664562Sgshapiro { \ 7790792Sgshapiro sm_free(response); /* XXX */ \ 7864562Sgshapiro response = newstr(default); \ 7964562Sgshapiro break; \ 8064562Sgshapiro } \ 8164562Sgshapiro ptr++; \ 8264562Sgshapiro } \ 8364562Sgshapiro } 8464562Sgshapiro 8564562Sgshapiro# define MILTER_DF_ERROR(msg) \ 8664562Sgshapiro{ \ 8764562Sgshapiro int save_errno = errno; \ 8864562Sgshapiro \ 8964562Sgshapiro if (tTd(64, 5)) \ 9064562Sgshapiro { \ 9190792Sgshapiro sm_dprintf(msg, dfname, sm_errstring(save_errno)); \ 9290792Sgshapiro sm_dprintf("\n"); \ 9364562Sgshapiro } \ 9490792Sgshapiro if (MilterLogLevel > 0) \ 9590792Sgshapiro sm_syslog(LOG_ERR, e->e_id, msg, dfname, sm_errstring(save_errno)); \ 9690792Sgshapiro if (SuperSafe == SAFE_REALLY) \ 9764562Sgshapiro { \ 9864562Sgshapiro if (e->e_dfp != NULL) \ 9964562Sgshapiro { \ 10090792Sgshapiro (void) sm_io_close(e->e_dfp, SM_TIME_DEFAULT); \ 10164562Sgshapiro e->e_dfp = NULL; \ 10264562Sgshapiro } \ 10364562Sgshapiro e->e_flags &= ~EF_HAS_DF; \ 10464562Sgshapiro } \ 10564562Sgshapiro errno = save_errno; \ 10664562Sgshapiro} 10764562Sgshapiro 10864562Sgshapiro/* 10964562Sgshapiro** MILTER_TIMEOUT -- make sure socket is ready in time 11064562Sgshapiro** 11164562Sgshapiro** Parameters: 11264562Sgshapiro** routine -- routine name for debug/logging 11364562Sgshapiro** secs -- number of seconds in timeout 11464562Sgshapiro** write -- waiting to read or write? 11590792Sgshapiro** started -- whether this is part of a previous sequence 11664562Sgshapiro** 11764562Sgshapiro** Assumes 'm' is a milter structure for the current socket. 11864562Sgshapiro*/ 11964562Sgshapiro 12090792Sgshapiro# define MILTER_TIMEOUT(routine, secs, write, started) \ 12164562Sgshapiro{ \ 12264562Sgshapiro int ret; \ 12364562Sgshapiro int save_errno; \ 12464562Sgshapiro fd_set fds; \ 12564562Sgshapiro struct timeval tv; \ 12664562Sgshapiro \ 12790792Sgshapiro if (SM_FD_SETSIZE > 0 && m->mf_sock >= SM_FD_SETSIZE) \ 12864562Sgshapiro { \ 12964562Sgshapiro if (tTd(64, 5)) \ 13090792Sgshapiro sm_dprintf("milter_%s(%s): socket %d is larger than FD_SETSIZE %d\n", \ 13190792Sgshapiro (routine), m->mf_name, m->mf_sock, \ 13290792Sgshapiro SM_FD_SETSIZE); \ 13390792Sgshapiro if (MilterLogLevel > 0) \ 13464562Sgshapiro sm_syslog(LOG_ERR, e->e_id, \ 13590792Sgshapiro "Milter (%s): socket(%s) %d is larger than FD_SETSIZE %d", \ 13690792Sgshapiro m->mf_name, (routine), m->mf_sock, \ 13790792Sgshapiro SM_FD_SETSIZE); \ 13890792Sgshapiro milter_error(m, e); \ 13964562Sgshapiro return NULL; \ 14064562Sgshapiro } \ 14164562Sgshapiro \ 14294334Sgshapiro do \ 14394334Sgshapiro { \ 14494334Sgshapiro FD_ZERO(&fds); \ 14594334Sgshapiro SM_FD_SET(m->mf_sock, &fds); \ 14694334Sgshapiro tv.tv_sec = (secs); \ 14794334Sgshapiro tv.tv_usec = 0; \ 14894334Sgshapiro ret = select(m->mf_sock + 1, \ 14994334Sgshapiro (write) ? NULL : &fds, \ 15094334Sgshapiro (write) ? &fds : NULL, \ 15194334Sgshapiro NULL, &tv); \ 15294334Sgshapiro } while (ret < 0 && errno == EINTR); \ 15364562Sgshapiro \ 15464562Sgshapiro switch (ret) \ 15564562Sgshapiro { \ 15664562Sgshapiro case 0: \ 15764562Sgshapiro if (tTd(64, 5)) \ 15890792Sgshapiro sm_dprintf("milter_%s(%s): timeout\n", (routine), \ 15990792Sgshapiro m->mf_name); \ 16090792Sgshapiro if (MilterLogLevel > 0) \ 16190792Sgshapiro sm_syslog(LOG_ERR, e->e_id, \ 16290792Sgshapiro "Milter (%s): %s %s %s %s", \ 16390792Sgshapiro m->mf_name, "timeout", \ 16490792Sgshapiro started ? "during" : "before", \ 16590792Sgshapiro "data", (routine)); \ 16690792Sgshapiro milter_error(m, e); \ 16764562Sgshapiro return NULL; \ 16864562Sgshapiro \ 16964562Sgshapiro case -1: \ 17064562Sgshapiro save_errno = errno; \ 17164562Sgshapiro if (tTd(64, 5)) \ 17290792Sgshapiro sm_dprintf("milter_%s(%s): select: %s\n", (routine), \ 17390792Sgshapiro m->mf_name, sm_errstring(save_errno)); \ 17490792Sgshapiro if (MilterLogLevel > 0) \ 17590792Sgshapiro { \ 17664562Sgshapiro sm_syslog(LOG_ERR, e->e_id, \ 17790792Sgshapiro "Milter (%s): select(%s): %s", \ 17890792Sgshapiro m->mf_name, (routine), \ 17990792Sgshapiro sm_errstring(save_errno)); \ 18090792Sgshapiro } \ 18190792Sgshapiro milter_error(m, e); \ 18264562Sgshapiro return NULL; \ 18364562Sgshapiro \ 18464562Sgshapiro default: \ 18571345Sgshapiro if (SM_FD_ISSET(m->mf_sock, &fds)) \ 18664562Sgshapiro break; \ 18764562Sgshapiro if (tTd(64, 5)) \ 18890792Sgshapiro sm_dprintf("milter_%s(%s): socket not ready\n", \ 18990792Sgshapiro (routine), m->mf_name); \ 19090792Sgshapiro if (MilterLogLevel > 0) \ 19190792Sgshapiro { \ 19264562Sgshapiro sm_syslog(LOG_ERR, e->e_id, \ 19390792Sgshapiro "Milter (%s): socket(%s) not ready", \ 19490792Sgshapiro m->mf_name, (routine)); \ 19590792Sgshapiro } \ 19690792Sgshapiro milter_error(m, e); \ 19764562Sgshapiro return NULL; \ 19864562Sgshapiro } \ 19964562Sgshapiro} 20064562Sgshapiro 20164562Sgshapiro/* 20264562Sgshapiro** Low level functions 20364562Sgshapiro*/ 20464562Sgshapiro 20590792Sgshapiro/* 20664562Sgshapiro** MILTER_READ -- read from a remote milter filter 20764562Sgshapiro** 20864562Sgshapiro** Parameters: 20964562Sgshapiro** m -- milter to read from. 21064562Sgshapiro** cmd -- return param for command read. 21164562Sgshapiro** rlen -- return length of response string. 21264562Sgshapiro** to -- timeout in seconds. 21364562Sgshapiro** e -- current envelope. 21464562Sgshapiro** 21564562Sgshapiro** Returns: 21664562Sgshapiro** response string (may be NULL) 21764562Sgshapiro*/ 21864562Sgshapiro 21964562Sgshapirostatic char * 22064562Sgshapiromilter_sysread(m, buf, sz, to, e) 22164562Sgshapiro struct milter *m; 22264562Sgshapiro char *buf; 22364562Sgshapiro ssize_t sz; 22464562Sgshapiro time_t to; 22564562Sgshapiro ENVELOPE *e; 22664562Sgshapiro{ 22766494Sgshapiro time_t readstart = 0; 22864562Sgshapiro ssize_t len, curl; 22990792Sgshapiro bool started = false; 23064562Sgshapiro 23164562Sgshapiro curl = 0; 23264562Sgshapiro 23364562Sgshapiro if (to > 0) 23464562Sgshapiro readstart = curtime(); 23564562Sgshapiro 23664562Sgshapiro for (;;) 23764562Sgshapiro { 23864562Sgshapiro if (to > 0) 23964562Sgshapiro { 24064562Sgshapiro time_t now; 24164562Sgshapiro 24264562Sgshapiro now = curtime(); 24364562Sgshapiro if (now - readstart >= to) 24464562Sgshapiro { 24564562Sgshapiro if (tTd(64, 5)) 24690792Sgshapiro sm_dprintf("milter_read (%s): %s %s %s", 24790792Sgshapiro m->mf_name, "timeout", 24890792Sgshapiro started ? "during" : "before", 24990792Sgshapiro "data read"); 25090792Sgshapiro if (MilterLogLevel > 0) 25164562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 25290792Sgshapiro "Milter (%s): %s %s %s", 25390792Sgshapiro m->mf_name, "timeout", 25490792Sgshapiro started ? "during" : "before", 25590792Sgshapiro "data read"); 25690792Sgshapiro milter_error(m, e); 25764562Sgshapiro return NULL; 25864562Sgshapiro } 25964562Sgshapiro to -= now - readstart; 26064562Sgshapiro readstart = now; 26190792Sgshapiro MILTER_TIMEOUT("read", to, false, started); 26264562Sgshapiro } 26364562Sgshapiro 26464562Sgshapiro len = read(m->mf_sock, buf + curl, sz - curl); 26564562Sgshapiro 26664562Sgshapiro if (len < 0) 26764562Sgshapiro { 26864562Sgshapiro int save_errno = errno; 26964562Sgshapiro 27064562Sgshapiro if (tTd(64, 5)) 27190792Sgshapiro sm_dprintf("milter_read(%s): read returned %ld: %s\n", 27264562Sgshapiro m->mf_name, (long) len, 27390792Sgshapiro sm_errstring(save_errno)); 27490792Sgshapiro if (MilterLogLevel > 0) 27564562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 27690792Sgshapiro "Milter (%s): read returned %ld: %s", 27764562Sgshapiro m->mf_name, (long) len, 27890792Sgshapiro sm_errstring(save_errno)); 27990792Sgshapiro milter_error(m, e); 28064562Sgshapiro return NULL; 28164562Sgshapiro } 28264562Sgshapiro 28390792Sgshapiro started = true; 28464562Sgshapiro curl += len; 28573188Sgshapiro if (len == 0 || curl >= sz) 28664562Sgshapiro break; 28764562Sgshapiro 28864562Sgshapiro } 28964562Sgshapiro 29064562Sgshapiro if (curl != sz) 29164562Sgshapiro { 29264562Sgshapiro if (tTd(64, 5)) 29390792Sgshapiro sm_dprintf("milter_read(%s): cmd read returned %ld, expecting %ld\n", 29464562Sgshapiro m->mf_name, (long) curl, (long) sz); 29590792Sgshapiro if (MilterLogLevel > 0) 29664562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 29790792Sgshapiro "milter_read(%s): cmd read returned %ld, expecting %ld", 29864562Sgshapiro m->mf_name, (long) curl, (long) sz); 29990792Sgshapiro milter_error(m, e); 30064562Sgshapiro return NULL; 30164562Sgshapiro } 30264562Sgshapiro return buf; 30364562Sgshapiro} 30464562Sgshapiro 30564562Sgshapirostatic char * 30664562Sgshapiromilter_read(m, cmd, rlen, to, e) 30764562Sgshapiro struct milter *m; 30864562Sgshapiro char *cmd; 30964562Sgshapiro ssize_t *rlen; 31064562Sgshapiro time_t to; 31164562Sgshapiro ENVELOPE *e; 31264562Sgshapiro{ 31366494Sgshapiro time_t readstart = 0; 31464562Sgshapiro ssize_t expl; 31564562Sgshapiro mi_int32 i; 31664562Sgshapiro char *buf; 31764562Sgshapiro char data[MILTER_LEN_BYTES + 1]; 31864562Sgshapiro 31964562Sgshapiro *rlen = 0; 32064562Sgshapiro *cmd = '\0'; 32164562Sgshapiro 32264562Sgshapiro if (to > 0) 32364562Sgshapiro readstart = curtime(); 32464562Sgshapiro 32564562Sgshapiro if (milter_sysread(m, data, sizeof data, to, e) == NULL) 32664562Sgshapiro return NULL; 32764562Sgshapiro 32864562Sgshapiro /* reset timeout */ 32964562Sgshapiro if (to > 0) 33064562Sgshapiro { 33164562Sgshapiro time_t now; 33264562Sgshapiro 33364562Sgshapiro now = curtime(); 33464562Sgshapiro if (now - readstart >= to) 33564562Sgshapiro { 33664562Sgshapiro if (tTd(64, 5)) 33790792Sgshapiro sm_dprintf("milter_read(%s): timeout before data read\n", 33864562Sgshapiro m->mf_name); 33990792Sgshapiro if (MilterLogLevel > 0) 34064562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 34190792Sgshapiro "Milter read(%s): timeout before data read", 34264562Sgshapiro m->mf_name); 34390792Sgshapiro milter_error(m, e); 34464562Sgshapiro return NULL; 34564562Sgshapiro } 34664562Sgshapiro to -= now - readstart; 34764562Sgshapiro } 34864562Sgshapiro 34964562Sgshapiro *cmd = data[MILTER_LEN_BYTES]; 35064562Sgshapiro data[MILTER_LEN_BYTES] = '\0'; 35164562Sgshapiro (void) memcpy(&i, data, MILTER_LEN_BYTES); 35264562Sgshapiro expl = ntohl(i) - 1; 35364562Sgshapiro 35464562Sgshapiro if (tTd(64, 25)) 35590792Sgshapiro sm_dprintf("milter_read(%s): expecting %ld bytes\n", 35664562Sgshapiro m->mf_name, (long) expl); 35764562Sgshapiro 35864562Sgshapiro if (expl < 0) 35964562Sgshapiro { 36064562Sgshapiro if (tTd(64, 5)) 36190792Sgshapiro sm_dprintf("milter_read(%s): read size %ld out of range\n", 36264562Sgshapiro m->mf_name, (long) expl); 36390792Sgshapiro if (MilterLogLevel > 0) 36464562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 36564562Sgshapiro "milter_read(%s): read size %ld out of range", 36664562Sgshapiro m->mf_name, (long) expl); 36790792Sgshapiro milter_error(m, e); 36864562Sgshapiro return NULL; 36964562Sgshapiro } 37064562Sgshapiro 37164562Sgshapiro if (expl == 0) 37264562Sgshapiro return NULL; 37364562Sgshapiro 37490792Sgshapiro buf = (char *) xalloc(expl); 37564562Sgshapiro 37664562Sgshapiro if (milter_sysread(m, buf, expl, to, e) == NULL) 37764562Sgshapiro { 37890792Sgshapiro sm_free(buf); /* XXX */ 37964562Sgshapiro return NULL; 38064562Sgshapiro } 38164562Sgshapiro 38264562Sgshapiro if (tTd(64, 50)) 38390792Sgshapiro sm_dprintf("milter_read(%s): Returning %*s\n", 38464562Sgshapiro m->mf_name, (int) expl, buf); 38564562Sgshapiro *rlen = expl; 38664562Sgshapiro return buf; 38764562Sgshapiro} 38890792Sgshapiro/* 38964562Sgshapiro** MILTER_WRITE -- write to a remote milter filter 39064562Sgshapiro** 39164562Sgshapiro** Parameters: 39264562Sgshapiro** m -- milter to read from. 39364562Sgshapiro** cmd -- command to send. 39464562Sgshapiro** buf -- optional command data. 39564562Sgshapiro** len -- length of buf. 39664562Sgshapiro** to -- timeout in seconds. 39764562Sgshapiro** e -- current envelope. 39864562Sgshapiro** 39964562Sgshapiro** Returns: 40064562Sgshapiro** buf if successful, NULL otherwise 40164562Sgshapiro** Not actually used anywhere but function prototype 40264562Sgshapiro** must match milter_read() 40364562Sgshapiro*/ 40464562Sgshapiro 40564562Sgshapirostatic char * 40664562Sgshapiromilter_write(m, cmd, buf, len, to, e) 40764562Sgshapiro struct milter *m; 40864562Sgshapiro char cmd; 40964562Sgshapiro char *buf; 41064562Sgshapiro ssize_t len; 41164562Sgshapiro time_t to; 41264562Sgshapiro ENVELOPE *e; 41364562Sgshapiro{ 41464562Sgshapiro time_t writestart = (time_t) 0; 41564562Sgshapiro ssize_t sl, i; 41664562Sgshapiro mi_int32 nl; 41764562Sgshapiro char data[MILTER_LEN_BYTES + 1]; 41890792Sgshapiro bool started = false; 41964562Sgshapiro 42064562Sgshapiro if (len < 0 || len > MILTER_CHUNK_SIZE) 42164562Sgshapiro { 42264562Sgshapiro if (tTd(64, 5)) 42390792Sgshapiro sm_dprintf("milter_write(%s): length %ld out of range\n", 42464562Sgshapiro m->mf_name, (long) len); 42590792Sgshapiro if (MilterLogLevel > 0) 42664562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 42764562Sgshapiro "milter_write(%s): length %ld out of range", 42864562Sgshapiro m->mf_name, (long) len); 42990792Sgshapiro milter_error(m, e); 43064562Sgshapiro return NULL; 43164562Sgshapiro } 43264562Sgshapiro 43364562Sgshapiro if (tTd(64, 20)) 43490792Sgshapiro sm_dprintf("milter_write(%s): cmd %c, len %ld\n", 43590792Sgshapiro m->mf_name, cmd, (long) len); 43664562Sgshapiro 43764562Sgshapiro nl = htonl(len + 1); /* add 1 for the cmd char */ 43864562Sgshapiro (void) memcpy(data, (char *) &nl, MILTER_LEN_BYTES); 43964562Sgshapiro data[MILTER_LEN_BYTES] = cmd; 44064562Sgshapiro sl = MILTER_LEN_BYTES + 1; 44164562Sgshapiro 44264562Sgshapiro if (to > 0) 44364562Sgshapiro { 44464562Sgshapiro writestart = curtime(); 44590792Sgshapiro MILTER_TIMEOUT("write", to, true, started); 44664562Sgshapiro } 44764562Sgshapiro 44864562Sgshapiro /* use writev() instead to send the whole stuff at once? */ 44964562Sgshapiro i = write(m->mf_sock, (void *) data, sl); 45064562Sgshapiro if (i != sl) 45164562Sgshapiro { 45264562Sgshapiro int save_errno = errno; 45364562Sgshapiro 45464562Sgshapiro if (tTd(64, 5)) 45590792Sgshapiro sm_dprintf("milter_write (%s): write(%c) returned %ld, expected %ld: %s\n", 45690792Sgshapiro m->mf_name, cmd, (long) i, (long) sl, 45790792Sgshapiro sm_errstring(save_errno)); 45890792Sgshapiro if (MilterLogLevel > 0) 45964562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 46090792Sgshapiro "Milter (%s): write(%c) returned %ld, expected %ld: %s", 46164562Sgshapiro m->mf_name, cmd, (long) i, (long) sl, 46290792Sgshapiro sm_errstring(save_errno)); 46390792Sgshapiro milter_error(m, e); 46464562Sgshapiro return buf; 46564562Sgshapiro } 46664562Sgshapiro 46764562Sgshapiro if (len <= 0 || buf == NULL) 46864562Sgshapiro return buf; 46964562Sgshapiro 47064562Sgshapiro if (tTd(64, 50)) 47190792Sgshapiro sm_dprintf("milter_write(%s): Sending %*s\n", 47290792Sgshapiro m->mf_name, (int) len, buf); 47390792Sgshapiro started = true; 47464562Sgshapiro 47564562Sgshapiro if (to > 0) 47664562Sgshapiro { 47764562Sgshapiro time_t now; 47864562Sgshapiro 47964562Sgshapiro now = curtime(); 48064562Sgshapiro if (now - writestart >= to) 48164562Sgshapiro { 48264562Sgshapiro if (tTd(64, 5)) 48390792Sgshapiro sm_dprintf("milter_write(%s): timeout before data write\n", 48490792Sgshapiro m->mf_name); 48590792Sgshapiro if (MilterLogLevel > 0) 48664562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 48790792Sgshapiro "Milter (%s): timeout before data write", 48864562Sgshapiro m->mf_name); 48990792Sgshapiro milter_error(m, e); 49064562Sgshapiro return NULL; 49164562Sgshapiro } 49264562Sgshapiro else 49364562Sgshapiro { 49464562Sgshapiro to -= now - writestart; 49590792Sgshapiro MILTER_TIMEOUT("write", to, true, started); 49664562Sgshapiro } 49764562Sgshapiro } 49864562Sgshapiro 49964562Sgshapiro i = write(m->mf_sock, (void *) buf, len); 50064562Sgshapiro if (i != len) 50164562Sgshapiro { 50264562Sgshapiro int save_errno = errno; 50364562Sgshapiro 50464562Sgshapiro if (tTd(64, 5)) 50590792Sgshapiro sm_dprintf("milter_write(%s): write(%c) returned %ld, expected %ld: %s\n", 50690792Sgshapiro m->mf_name, cmd, (long) i, (long) sl, 50790792Sgshapiro sm_errstring(save_errno)); 50890792Sgshapiro if (MilterLogLevel > 0) 50964562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 51090792Sgshapiro "Milter (%s): write(%c) returned %ld, expected %ld: %s", 51164562Sgshapiro m->mf_name, cmd, (long) i, (long) len, 51290792Sgshapiro sm_errstring(save_errno)); 51390792Sgshapiro milter_error(m, e); 51464562Sgshapiro return NULL; 51564562Sgshapiro } 51664562Sgshapiro return buf; 51764562Sgshapiro} 51864562Sgshapiro 51964562Sgshapiro/* 52064562Sgshapiro** Utility functions 52164562Sgshapiro*/ 52264562Sgshapiro 52390792Sgshapiro/* 52464562Sgshapiro** MILTER_OPEN -- connect to remote milter filter 52564562Sgshapiro** 52664562Sgshapiro** Parameters: 52764562Sgshapiro** m -- milter to connect to. 52864562Sgshapiro** parseonly -- parse but don't connect. 52964562Sgshapiro** e -- current envelope. 53064562Sgshapiro** 53164562Sgshapiro** Returns: 53264562Sgshapiro** connected socket if sucessful && !parseonly, 53364562Sgshapiro** 0 upon parse success if parseonly, 53464562Sgshapiro** -1 otherwise. 53564562Sgshapiro*/ 53664562Sgshapiro 53780785Sgshapirostatic jmp_buf MilterConnectTimeout; 53880785Sgshapiro 53964562Sgshapirostatic int 54064562Sgshapiromilter_open(m, parseonly, e) 54164562Sgshapiro struct milter *m; 54264562Sgshapiro bool parseonly; 54364562Sgshapiro ENVELOPE *e; 54464562Sgshapiro{ 54564562Sgshapiro int sock = 0; 54664562Sgshapiro SOCKADDR_LEN_T addrlen = 0; 54764562Sgshapiro int addrno = 0; 54864562Sgshapiro int save_errno; 54964562Sgshapiro char *p; 55064562Sgshapiro char *colon; 55164562Sgshapiro char *at; 55264562Sgshapiro struct hostent *hp = NULL; 55364562Sgshapiro SOCKADDR addr; 55464562Sgshapiro 55564562Sgshapiro if (m->mf_conn == NULL || m->mf_conn[0] == '\0') 55664562Sgshapiro { 55764562Sgshapiro if (tTd(64, 5)) 55890792Sgshapiro sm_dprintf("X%s: empty or missing socket information\n", 55990792Sgshapiro m->mf_name); 56064562Sgshapiro if (parseonly) 56164562Sgshapiro syserr("X%s: empty or missing socket information", 56264562Sgshapiro m->mf_name); 56390792Sgshapiro else if (MilterLogLevel > 10) 56464562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 56590792Sgshapiro "Milter (%s): empty or missing socket information", 56664562Sgshapiro m->mf_name); 56790792Sgshapiro milter_error(m, e); 56864562Sgshapiro return -1; 56964562Sgshapiro } 57064562Sgshapiro 57164562Sgshapiro /* protocol:filename or protocol:port@host */ 57294334Sgshapiro memset(&addr, '\0', sizeof addr); 57364562Sgshapiro p = m->mf_conn; 57464562Sgshapiro colon = strchr(p, ':'); 57564562Sgshapiro if (colon != NULL) 57664562Sgshapiro { 57764562Sgshapiro *colon = '\0'; 57864562Sgshapiro 57964562Sgshapiro if (*p == '\0') 58064562Sgshapiro { 58164562Sgshapiro# if NETUNIX 58264562Sgshapiro /* default to AF_UNIX */ 58364562Sgshapiro addr.sa.sa_family = AF_UNIX; 58464562Sgshapiro# else /* NETUNIX */ 58564562Sgshapiro# if NETINET 58664562Sgshapiro /* default to AF_INET */ 58764562Sgshapiro addr.sa.sa_family = AF_INET; 58864562Sgshapiro# else /* NETINET */ 58964562Sgshapiro# if NETINET6 59064562Sgshapiro /* default to AF_INET6 */ 59164562Sgshapiro addr.sa.sa_family = AF_INET6; 59264562Sgshapiro# else /* NETINET6 */ 59364562Sgshapiro /* no protocols available */ 59464562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 59590792Sgshapiro "Milter (%s): no valid socket protocols available", 59664562Sgshapiro m->mf_name); 59790792Sgshapiro milter_error(m, e); 59864562Sgshapiro return -1; 59964562Sgshapiro# endif /* NETINET6 */ 60064562Sgshapiro# endif /* NETINET */ 60164562Sgshapiro# endif /* NETUNIX */ 60264562Sgshapiro } 60364562Sgshapiro# if NETUNIX 60490792Sgshapiro else if (sm_strcasecmp(p, "unix") == 0 || 60590792Sgshapiro sm_strcasecmp(p, "local") == 0) 60664562Sgshapiro addr.sa.sa_family = AF_UNIX; 60764562Sgshapiro# endif /* NETUNIX */ 60864562Sgshapiro# if NETINET 60990792Sgshapiro else if (sm_strcasecmp(p, "inet") == 0) 61064562Sgshapiro addr.sa.sa_family = AF_INET; 61164562Sgshapiro# endif /* NETINET */ 61264562Sgshapiro# if NETINET6 61390792Sgshapiro else if (sm_strcasecmp(p, "inet6") == 0) 61464562Sgshapiro addr.sa.sa_family = AF_INET6; 61564562Sgshapiro# endif /* NETINET6 */ 61664562Sgshapiro else 61764562Sgshapiro { 61864562Sgshapiro# ifdef EPROTONOSUPPORT 61964562Sgshapiro errno = EPROTONOSUPPORT; 62064562Sgshapiro# else /* EPROTONOSUPPORT */ 62164562Sgshapiro errno = EINVAL; 62264562Sgshapiro# endif /* EPROTONOSUPPORT */ 62364562Sgshapiro if (tTd(64, 5)) 62490792Sgshapiro sm_dprintf("X%s: unknown socket type %s\n", 62564562Sgshapiro m->mf_name, p); 62664562Sgshapiro if (parseonly) 62764562Sgshapiro syserr("X%s: unknown socket type %s", 62864562Sgshapiro m->mf_name, p); 62990792Sgshapiro else if (MilterLogLevel > 10) 63064562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 63190792Sgshapiro "Milter (%s): unknown socket type %s", 63264562Sgshapiro m->mf_name, p); 63390792Sgshapiro milter_error(m, e); 63464562Sgshapiro return -1; 63564562Sgshapiro } 63664562Sgshapiro *colon++ = ':'; 63764562Sgshapiro } 63864562Sgshapiro else 63964562Sgshapiro { 64064562Sgshapiro /* default to AF_UNIX */ 64164562Sgshapiro addr.sa.sa_family = AF_UNIX; 64264562Sgshapiro colon = p; 64364562Sgshapiro } 64464562Sgshapiro 64564562Sgshapiro# if NETUNIX 64664562Sgshapiro if (addr.sa.sa_family == AF_UNIX) 64764562Sgshapiro { 64864562Sgshapiro long sff = SFF_SAFEDIRPATH|SFF_OPENASROOT|SFF_NOLINK|SFF_EXECOK; 64964562Sgshapiro 65064562Sgshapiro at = colon; 65164562Sgshapiro if (strlen(colon) >= sizeof addr.sunix.sun_path) 65264562Sgshapiro { 65364562Sgshapiro if (tTd(64, 5)) 65490792Sgshapiro sm_dprintf("X%s: local socket name %s too long\n", 65564562Sgshapiro m->mf_name, colon); 65664562Sgshapiro errno = EINVAL; 65764562Sgshapiro if (parseonly) 65864562Sgshapiro syserr("X%s: local socket name %s too long", 65964562Sgshapiro m->mf_name, colon); 66090792Sgshapiro else if (MilterLogLevel > 10) 66164562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 66290792Sgshapiro "Milter (%s): local socket name %s too long", 66364562Sgshapiro m->mf_name, colon); 66490792Sgshapiro milter_error(m, e); 66564562Sgshapiro return -1; 66664562Sgshapiro } 66764562Sgshapiro errno = safefile(colon, RunAsUid, RunAsGid, RunAsUserName, sff, 66864562Sgshapiro S_IRUSR|S_IWUSR, NULL); 66964562Sgshapiro 67064562Sgshapiro /* if just parsing .cf file, socket doesn't need to exist */ 67164562Sgshapiro if (parseonly && errno == ENOENT) 67264562Sgshapiro { 67364562Sgshapiro if (OpMode == MD_DAEMON || 67464562Sgshapiro OpMode == MD_FGDAEMON) 67590792Sgshapiro (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, 67690792Sgshapiro "WARNING: X%s: local socket name %s missing\n", 67790792Sgshapiro m->mf_name, colon); 67864562Sgshapiro } 67964562Sgshapiro else if (errno != 0) 68064562Sgshapiro { 68164562Sgshapiro /* if not safe, don't create */ 68264562Sgshapiro save_errno = errno; 68364562Sgshapiro if (tTd(64, 5)) 68490792Sgshapiro sm_dprintf("X%s: local socket name %s unsafe\n", 68564562Sgshapiro m->mf_name, colon); 68664562Sgshapiro errno = save_errno; 68764562Sgshapiro if (parseonly) 68864562Sgshapiro { 68964562Sgshapiro if (OpMode == MD_DAEMON || 69064562Sgshapiro OpMode == MD_FGDAEMON || 69164562Sgshapiro OpMode == MD_SMTP) 69264562Sgshapiro syserr("X%s: local socket name %s unsafe", 69364562Sgshapiro m->mf_name, colon); 69464562Sgshapiro } 69590792Sgshapiro else if (MilterLogLevel > 10) 69664562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 69790792Sgshapiro "Milter (%s): local socket name %s unsafe", 69864562Sgshapiro m->mf_name, colon); 69990792Sgshapiro milter_error(m, e); 70064562Sgshapiro return -1; 70164562Sgshapiro } 70264562Sgshapiro 70390792Sgshapiro (void) sm_strlcpy(addr.sunix.sun_path, colon, 70464562Sgshapiro sizeof addr.sunix.sun_path); 70564562Sgshapiro addrlen = sizeof (struct sockaddr_un); 70664562Sgshapiro } 70764562Sgshapiro else 70864562Sgshapiro# endif /* NETUNIX */ 70964562Sgshapiro# if NETINET || NETINET6 71090792Sgshapiro if (false 71164562Sgshapiro# if NETINET 71264562Sgshapiro || addr.sa.sa_family == AF_INET 71364562Sgshapiro# endif /* NETINET */ 71464562Sgshapiro# if NETINET6 71564562Sgshapiro || addr.sa.sa_family == AF_INET6 71664562Sgshapiro# endif /* NETINET6 */ 71764562Sgshapiro ) 71864562Sgshapiro { 71990792Sgshapiro unsigned short port; 72064562Sgshapiro 72164562Sgshapiro /* Parse port@host */ 72264562Sgshapiro at = strchr(colon, '@'); 72364562Sgshapiro if (at == NULL) 72464562Sgshapiro { 72564562Sgshapiro if (tTd(64, 5)) 72690792Sgshapiro sm_dprintf("X%s: bad address %s (expected port@host)\n", 72764562Sgshapiro m->mf_name, colon); 72864562Sgshapiro if (parseonly) 72964562Sgshapiro syserr("X%s: bad address %s (expected port@host)", 73064562Sgshapiro m->mf_name, colon); 73190792Sgshapiro else if (MilterLogLevel > 10) 73264562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 73390792Sgshapiro "Milter (%s): bad address %s (expected port@host)", 73464562Sgshapiro m->mf_name, colon); 73590792Sgshapiro milter_error(m, e); 73664562Sgshapiro return -1; 73764562Sgshapiro } 73864562Sgshapiro *at = '\0'; 73964562Sgshapiro if (isascii(*colon) && isdigit(*colon)) 74090792Sgshapiro port = htons((unsigned short) atoi(colon)); 74164562Sgshapiro else 74264562Sgshapiro { 74364562Sgshapiro# ifdef NO_GETSERVBYNAME 74464562Sgshapiro if (tTd(64, 5)) 74590792Sgshapiro sm_dprintf("X%s: invalid port number %s\n", 74664562Sgshapiro m->mf_name, colon); 74764562Sgshapiro if (parseonly) 74864562Sgshapiro syserr("X%s: invalid port number %s", 74964562Sgshapiro m->mf_name, colon); 75090792Sgshapiro else if (MilterLogLevel > 10) 75164562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 75290792Sgshapiro "Milter (%s): invalid port number %s", 75364562Sgshapiro m->mf_name, colon); 75490792Sgshapiro milter_error(m, e); 75564562Sgshapiro return -1; 75664562Sgshapiro# else /* NO_GETSERVBYNAME */ 75764562Sgshapiro register struct servent *sp; 75864562Sgshapiro 75964562Sgshapiro sp = getservbyname(colon, "tcp"); 76064562Sgshapiro if (sp == NULL) 76164562Sgshapiro { 76264562Sgshapiro save_errno = errno; 76364562Sgshapiro if (tTd(64, 5)) 76490792Sgshapiro sm_dprintf("X%s: unknown port name %s\n", 76564562Sgshapiro m->mf_name, colon); 76664562Sgshapiro errno = save_errno; 76764562Sgshapiro if (parseonly) 76864562Sgshapiro syserr("X%s: unknown port name %s", 76964562Sgshapiro m->mf_name, colon); 77090792Sgshapiro else if (MilterLogLevel > 10) 77164562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 77290792Sgshapiro "Milter (%s): unknown port name %s", 77364562Sgshapiro m->mf_name, colon); 77490792Sgshapiro milter_error(m, e); 77564562Sgshapiro return -1; 77664562Sgshapiro } 77764562Sgshapiro port = sp->s_port; 77864562Sgshapiro# endif /* NO_GETSERVBYNAME */ 77964562Sgshapiro } 78064562Sgshapiro *at++ = '@'; 78164562Sgshapiro if (*at == '[') 78264562Sgshapiro { 78364562Sgshapiro char *end; 78464562Sgshapiro 78564562Sgshapiro end = strchr(at, ']'); 78664562Sgshapiro if (end != NULL) 78764562Sgshapiro { 78890792Sgshapiro bool found = false; 78964562Sgshapiro# if NETINET 79064562Sgshapiro unsigned long hid = INADDR_NONE; 79164562Sgshapiro# endif /* NETINET */ 79264562Sgshapiro# if NETINET6 79364562Sgshapiro struct sockaddr_in6 hid6; 79464562Sgshapiro# endif /* NETINET6 */ 79564562Sgshapiro 79664562Sgshapiro *end = '\0'; 79764562Sgshapiro# if NETINET 79864562Sgshapiro if (addr.sa.sa_family == AF_INET && 79964562Sgshapiro (hid = inet_addr(&at[1])) != INADDR_NONE) 80064562Sgshapiro { 80164562Sgshapiro addr.sin.sin_addr.s_addr = hid; 80264562Sgshapiro addr.sin.sin_port = port; 80390792Sgshapiro found = true; 80464562Sgshapiro } 80564562Sgshapiro# endif /* NETINET */ 80664562Sgshapiro# if NETINET6 80764562Sgshapiro (void) memset(&hid6, '\0', sizeof hid6); 80864562Sgshapiro if (addr.sa.sa_family == AF_INET6 && 80990792Sgshapiro anynet_pton(AF_INET6, &at[1], 81090792Sgshapiro &hid6.sin6_addr) == 1) 81164562Sgshapiro { 81264562Sgshapiro addr.sin6.sin6_addr = hid6.sin6_addr; 81364562Sgshapiro addr.sin6.sin6_port = port; 81490792Sgshapiro found = true; 81564562Sgshapiro } 81664562Sgshapiro# endif /* NETINET6 */ 81764562Sgshapiro *end = ']'; 81864562Sgshapiro if (!found) 81964562Sgshapiro { 82064562Sgshapiro if (tTd(64, 5)) 82190792Sgshapiro sm_dprintf("X%s: Invalid numeric domain spec \"%s\"\n", 82264562Sgshapiro m->mf_name, at); 82364562Sgshapiro if (parseonly) 82464562Sgshapiro syserr("X%s: Invalid numeric domain spec \"%s\"", 82564562Sgshapiro m->mf_name, at); 82690792Sgshapiro else if (MilterLogLevel > 10) 82764562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 82890792Sgshapiro "Milter (%s): Invalid numeric domain spec \"%s\"", 82964562Sgshapiro m->mf_name, at); 83090792Sgshapiro milter_error(m, e); 83164562Sgshapiro return -1; 83264562Sgshapiro } 83364562Sgshapiro } 83464562Sgshapiro else 83564562Sgshapiro { 83664562Sgshapiro if (tTd(64, 5)) 83790792Sgshapiro sm_dprintf("X%s: Invalid numeric domain spec \"%s\"\n", 83864562Sgshapiro m->mf_name, at); 83964562Sgshapiro if (parseonly) 84064562Sgshapiro syserr("X%s: Invalid numeric domain spec \"%s\"", 84164562Sgshapiro m->mf_name, at); 84290792Sgshapiro else if (MilterLogLevel > 10) 84364562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 84490792Sgshapiro "Milter (%s): Invalid numeric domain spec \"%s\"", 84564562Sgshapiro m->mf_name, at); 84690792Sgshapiro milter_error(m, e); 84764562Sgshapiro return -1; 84864562Sgshapiro } 84964562Sgshapiro } 85064562Sgshapiro else 85164562Sgshapiro { 85264562Sgshapiro hp = sm_gethostbyname(at, addr.sa.sa_family); 85364562Sgshapiro if (hp == NULL) 85464562Sgshapiro { 85564562Sgshapiro save_errno = errno; 85664562Sgshapiro if (tTd(64, 5)) 85790792Sgshapiro sm_dprintf("X%s: Unknown host name %s\n", 85890792Sgshapiro m->mf_name, at); 85964562Sgshapiro errno = save_errno; 86064562Sgshapiro if (parseonly) 86164562Sgshapiro syserr("X%s: Unknown host name %s", 86264562Sgshapiro m->mf_name, at); 86390792Sgshapiro else if (MilterLogLevel > 10) 86464562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 86590792Sgshapiro "Milter (%s): Unknown host name %s", 86664562Sgshapiro m->mf_name, at); 86790792Sgshapiro milter_error(m, e); 86864562Sgshapiro return -1; 86964562Sgshapiro } 87064562Sgshapiro addr.sa.sa_family = hp->h_addrtype; 87164562Sgshapiro switch (hp->h_addrtype) 87264562Sgshapiro { 87364562Sgshapiro# if NETINET 87464562Sgshapiro case AF_INET: 87564562Sgshapiro memmove(&addr.sin.sin_addr, 87690792Sgshapiro hp->h_addr, INADDRSZ); 87764562Sgshapiro addr.sin.sin_port = port; 87864562Sgshapiro addrlen = sizeof (struct sockaddr_in); 87964562Sgshapiro addrno = 1; 88064562Sgshapiro break; 88164562Sgshapiro# endif /* NETINET */ 88264562Sgshapiro 88364562Sgshapiro# if NETINET6 88464562Sgshapiro case AF_INET6: 88564562Sgshapiro memmove(&addr.sin6.sin6_addr, 88690792Sgshapiro hp->h_addr, IN6ADDRSZ); 88764562Sgshapiro addr.sin6.sin6_port = port; 88864562Sgshapiro addrlen = sizeof (struct sockaddr_in6); 88964562Sgshapiro addrno = 1; 89064562Sgshapiro break; 89164562Sgshapiro# endif /* NETINET6 */ 89264562Sgshapiro 89364562Sgshapiro default: 89464562Sgshapiro if (tTd(64, 5)) 89590792Sgshapiro sm_dprintf("X%s: Unknown protocol for %s (%d)\n", 89690792Sgshapiro m->mf_name, at, 89790792Sgshapiro hp->h_addrtype); 89864562Sgshapiro if (parseonly) 89964562Sgshapiro syserr("X%s: Unknown protocol for %s (%d)", 90064562Sgshapiro m->mf_name, at, hp->h_addrtype); 90190792Sgshapiro else if (MilterLogLevel > 10) 90264562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 90390792Sgshapiro "Milter (%s): Unknown protocol for %s (%d)", 90464562Sgshapiro m->mf_name, at, 90564562Sgshapiro hp->h_addrtype); 90690792Sgshapiro milter_error(m, e); 90790792Sgshapiro# if NETINET6 90871345Sgshapiro freehostent(hp); 90990792Sgshapiro# endif /* NETINET6 */ 91064562Sgshapiro return -1; 91164562Sgshapiro } 91264562Sgshapiro } 91364562Sgshapiro } 91464562Sgshapiro else 91564562Sgshapiro# endif /* NETINET || NETINET6 */ 91664562Sgshapiro { 91764562Sgshapiro if (tTd(64, 5)) 91890792Sgshapiro sm_dprintf("X%s: unknown socket protocol\n", 91990792Sgshapiro m->mf_name); 92064562Sgshapiro if (parseonly) 92164562Sgshapiro syserr("X%s: unknown socket protocol", m->mf_name); 92290792Sgshapiro else if (MilterLogLevel > 10) 92364562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 92490792Sgshapiro "Milter (%s): unknown socket protocol", 92590792Sgshapiro m->mf_name); 92690792Sgshapiro milter_error(m, e); 92764562Sgshapiro return -1; 92864562Sgshapiro } 92964562Sgshapiro 93064562Sgshapiro /* just parsing through? */ 93164562Sgshapiro if (parseonly) 93264562Sgshapiro { 93364562Sgshapiro m->mf_state = SMFS_READY; 93490792Sgshapiro# if NETINET6 93571345Sgshapiro if (hp != NULL) 93671345Sgshapiro freehostent(hp); 93790792Sgshapiro# endif /* NETINET6 */ 93864562Sgshapiro return 0; 93964562Sgshapiro } 94064562Sgshapiro 94164562Sgshapiro /* sanity check */ 94264562Sgshapiro if (m->mf_state != SMFS_READY && 94364562Sgshapiro m->mf_state != SMFS_CLOSED) 94464562Sgshapiro { 94564562Sgshapiro /* shouldn't happen */ 94664562Sgshapiro if (tTd(64, 1)) 94790792Sgshapiro sm_dprintf("Milter (%s): Trying to open filter in state %c\n", 94890792Sgshapiro m->mf_name, (char) m->mf_state); 94990792Sgshapiro milter_error(m, e); 95090792Sgshapiro# if NETINET6 95171345Sgshapiro if (hp != NULL) 95271345Sgshapiro freehostent(hp); 95390792Sgshapiro# endif /* NETINET6 */ 95464562Sgshapiro return -1; 95564562Sgshapiro } 95664562Sgshapiro 95764562Sgshapiro /* nope, actually connecting */ 95864562Sgshapiro for (;;) 95964562Sgshapiro { 96064562Sgshapiro sock = socket(addr.sa.sa_family, SOCK_STREAM, 0); 96164562Sgshapiro if (sock < 0) 96264562Sgshapiro { 96364562Sgshapiro save_errno = errno; 96464562Sgshapiro if (tTd(64, 5)) 96590792Sgshapiro sm_dprintf("Milter (%s): error creating socket: %s\n", 96690792Sgshapiro m->mf_name, 96790792Sgshapiro sm_errstring(save_errno)); 96890792Sgshapiro if (MilterLogLevel > 0) 96964562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 97090792Sgshapiro "Milter (%s): error creating socket: %s", 97190792Sgshapiro m->mf_name, sm_errstring(save_errno)); 97290792Sgshapiro milter_error(m, e); 97390792Sgshapiro# if NETINET6 97471345Sgshapiro if (hp != NULL) 97571345Sgshapiro freehostent(hp); 97690792Sgshapiro# endif /* NETINET6 */ 97764562Sgshapiro return -1; 97864562Sgshapiro } 97964562Sgshapiro 98080785Sgshapiro if (setjmp(MilterConnectTimeout) == 0) 98180785Sgshapiro { 98290792Sgshapiro SM_EVENT *ev = NULL; 98380785Sgshapiro int i; 98464562Sgshapiro 98580785Sgshapiro if (m->mf_timeout[SMFTO_CONNECT] > 0) 98690792Sgshapiro ev = sm_setevent(m->mf_timeout[SMFTO_CONNECT], 98790792Sgshapiro milter_connect_timeout, 0); 98880785Sgshapiro 98980785Sgshapiro i = connect(sock, (struct sockaddr *) &addr, addrlen); 99080785Sgshapiro save_errno = errno; 99180785Sgshapiro if (ev != NULL) 99290792Sgshapiro sm_clrevent(ev); 99380785Sgshapiro errno = save_errno; 99480785Sgshapiro if (i >= 0) 99580785Sgshapiro break; 99680785Sgshapiro } 99780785Sgshapiro 99864562Sgshapiro /* couldn't connect.... try next address */ 99964562Sgshapiro save_errno = errno; 100066494Sgshapiro p = CurHostName; 100166494Sgshapiro CurHostName = at; 100264562Sgshapiro if (tTd(64, 5)) 100390792Sgshapiro sm_dprintf("milter_open (%s): open %s failed: %s\n", 100490792Sgshapiro m->mf_name, at, sm_errstring(save_errno)); 100590792Sgshapiro if (MilterLogLevel > 13) 100664562Sgshapiro sm_syslog(LOG_INFO, e->e_id, 100790792Sgshapiro "Milter (%s): open %s failed: %s", 100890792Sgshapiro m->mf_name, at, sm_errstring(save_errno)); 100966494Sgshapiro CurHostName = p; 101064562Sgshapiro (void) close(sock); 101164562Sgshapiro 101264562Sgshapiro /* try next address */ 101364562Sgshapiro if (hp != NULL && hp->h_addr_list[addrno] != NULL) 101464562Sgshapiro { 101564562Sgshapiro switch (addr.sa.sa_family) 101664562Sgshapiro { 101764562Sgshapiro# if NETINET 101864562Sgshapiro case AF_INET: 101964562Sgshapiro memmove(&addr.sin.sin_addr, 102064562Sgshapiro hp->h_addr_list[addrno++], 102164562Sgshapiro INADDRSZ); 102264562Sgshapiro break; 102364562Sgshapiro# endif /* NETINET */ 102464562Sgshapiro 102564562Sgshapiro# if NETINET6 102664562Sgshapiro case AF_INET6: 102764562Sgshapiro memmove(&addr.sin6.sin6_addr, 102864562Sgshapiro hp->h_addr_list[addrno++], 102964562Sgshapiro IN6ADDRSZ); 103064562Sgshapiro break; 103164562Sgshapiro# endif /* NETINET6 */ 103264562Sgshapiro 103364562Sgshapiro default: 103464562Sgshapiro if (tTd(64, 5)) 103590792Sgshapiro sm_dprintf("X%s: Unknown protocol for %s (%d)\n", 103690792Sgshapiro m->mf_name, at, 103790792Sgshapiro hp->h_addrtype); 103890792Sgshapiro if (MilterLogLevel > 0) 103964562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 104090792Sgshapiro "Milter (%s): Unknown protocol for %s (%d)", 104164562Sgshapiro m->mf_name, at, 104264562Sgshapiro hp->h_addrtype); 104390792Sgshapiro milter_error(m, e); 104490792Sgshapiro# if NETINET6 104571345Sgshapiro freehostent(hp); 104690792Sgshapiro# endif /* NETINET6 */ 104764562Sgshapiro return -1; 104864562Sgshapiro } 104964562Sgshapiro continue; 105064562Sgshapiro } 105180785Sgshapiro p = CurHostName; 105280785Sgshapiro CurHostName = at; 105364562Sgshapiro if (tTd(64, 5)) 105490792Sgshapiro sm_dprintf("X%s: error connecting to filter: %s\n", 105590792Sgshapiro m->mf_name, sm_errstring(save_errno)); 105690792Sgshapiro if (MilterLogLevel > 0) 105764562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 105890792Sgshapiro "Milter (%s): error connecting to filter: %s", 105990792Sgshapiro m->mf_name, sm_errstring(save_errno)); 106080785Sgshapiro CurHostName = p; 106190792Sgshapiro milter_error(m, e); 106290792Sgshapiro# if NETINET6 106371345Sgshapiro if (hp != NULL) 106471345Sgshapiro freehostent(hp); 106590792Sgshapiro# endif /* NETINET6 */ 106664562Sgshapiro return -1; 106764562Sgshapiro } 106864562Sgshapiro m->mf_state = SMFS_OPEN; 106990792Sgshapiro# if NETINET6 107071345Sgshapiro if (hp != NULL) 107171345Sgshapiro { 107271345Sgshapiro freehostent(hp); 107371345Sgshapiro hp = NULL; 107471345Sgshapiro } 107590792Sgshapiro# endif /* NETINET6 */ 107664562Sgshapiro return sock; 107764562Sgshapiro} 107880785Sgshapiro 107980785Sgshapirostatic void 108080785Sgshapiromilter_connect_timeout() 108180785Sgshapiro{ 108280785Sgshapiro /* 108380785Sgshapiro ** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD 108480785Sgshapiro ** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE 108580785Sgshapiro ** DOING. 108680785Sgshapiro */ 108780785Sgshapiro 108880785Sgshapiro errno = ETIMEDOUT; 108980785Sgshapiro longjmp(MilterConnectTimeout, 1); 109080785Sgshapiro} 109190792Sgshapiro/* 109264562Sgshapiro** MILTER_SETUP -- setup structure for a mail filter 109364562Sgshapiro** 109464562Sgshapiro** Parameters: 109564562Sgshapiro** line -- the options line. 109664562Sgshapiro** 109764562Sgshapiro** Returns: 109864562Sgshapiro** none 109964562Sgshapiro*/ 110064562Sgshapiro 110164562Sgshapirovoid 110264562Sgshapiromilter_setup(line) 110364562Sgshapiro char *line; 110464562Sgshapiro{ 110564562Sgshapiro char fcode; 110664562Sgshapiro register char *p; 110764562Sgshapiro register struct milter *m; 110864562Sgshapiro STAB *s; 110964562Sgshapiro 111066494Sgshapiro /* collect the filter name */ 111164562Sgshapiro for (p = line; 111264562Sgshapiro *p != '\0' && *p != ',' && !(isascii(*p) && isspace(*p)); 111364562Sgshapiro p++) 111464562Sgshapiro continue; 111564562Sgshapiro if (*p != '\0') 111664562Sgshapiro *p++ = '\0'; 111764562Sgshapiro if (line[0] == '\0') 111864562Sgshapiro { 111964562Sgshapiro syserr("name required for mail filter"); 112064562Sgshapiro return; 112164562Sgshapiro } 112290792Sgshapiro m = (struct milter *) xalloc(sizeof *m); 112364562Sgshapiro memset((char *) m, '\0', sizeof *m); 112464562Sgshapiro m->mf_name = newstr(line); 112564562Sgshapiro m->mf_state = SMFS_READY; 112664562Sgshapiro m->mf_sock = -1; 112790792Sgshapiro m->mf_timeout[SMFTO_CONNECT] = (time_t) 300; 112864562Sgshapiro m->mf_timeout[SMFTO_WRITE] = (time_t) 10; 112964562Sgshapiro m->mf_timeout[SMFTO_READ] = (time_t) 10; 113064562Sgshapiro m->mf_timeout[SMFTO_EOM] = (time_t) 300; 113164562Sgshapiro 113264562Sgshapiro /* now scan through and assign info from the fields */ 113364562Sgshapiro while (*p != '\0') 113464562Sgshapiro { 113564562Sgshapiro char *delimptr; 113664562Sgshapiro 113764562Sgshapiro while (*p != '\0' && 113864562Sgshapiro (*p == ',' || (isascii(*p) && isspace(*p)))) 113964562Sgshapiro p++; 114064562Sgshapiro 114164562Sgshapiro /* p now points to field code */ 114264562Sgshapiro fcode = *p; 114364562Sgshapiro while (*p != '\0' && *p != '=' && *p != ',') 114464562Sgshapiro p++; 114564562Sgshapiro if (*p++ != '=') 114664562Sgshapiro { 114764562Sgshapiro syserr("X%s: `=' expected", m->mf_name); 114864562Sgshapiro return; 114964562Sgshapiro } 115064562Sgshapiro while (isascii(*p) && isspace(*p)) 115164562Sgshapiro p++; 115264562Sgshapiro 115364562Sgshapiro /* p now points to the field body */ 115464562Sgshapiro p = munchstring(p, &delimptr, ','); 115564562Sgshapiro 115666494Sgshapiro /* install the field into the filter struct */ 115764562Sgshapiro switch (fcode) 115864562Sgshapiro { 115964562Sgshapiro case 'S': /* socket */ 116064562Sgshapiro if (p == NULL) 116164562Sgshapiro m->mf_conn = NULL; 116264562Sgshapiro else 116364562Sgshapiro m->mf_conn = newstr(p); 116464562Sgshapiro break; 116564562Sgshapiro 116664562Sgshapiro case 'F': /* Milter flags configured on MTA */ 116764562Sgshapiro for (; *p != '\0'; p++) 116864562Sgshapiro { 116964562Sgshapiro if (!(isascii(*p) && isspace(*p))) 117071345Sgshapiro setbitn(bitidx(*p), m->mf_flags); 117164562Sgshapiro } 117264562Sgshapiro break; 117364562Sgshapiro 117464562Sgshapiro case 'T': /* timeouts */ 117564562Sgshapiro milter_parse_timeouts(p, m); 117664562Sgshapiro break; 117764562Sgshapiro 117864562Sgshapiro default: 117964562Sgshapiro syserr("X%s: unknown filter equate %c=", 118064562Sgshapiro m->mf_name, fcode); 118164562Sgshapiro break; 118264562Sgshapiro } 118364562Sgshapiro p = delimptr; 118464562Sgshapiro } 118564562Sgshapiro 118664562Sgshapiro /* early check for errors */ 118790792Sgshapiro (void) milter_open(m, true, CurEnv); 118864562Sgshapiro 118966494Sgshapiro /* enter the filter into the symbol table */ 119064562Sgshapiro s = stab(m->mf_name, ST_MILTER, ST_ENTER); 119164562Sgshapiro if (s->s_milter != NULL) 119264562Sgshapiro syserr("X%s: duplicate filter definition", m->mf_name); 119364562Sgshapiro else 119464562Sgshapiro s->s_milter = m; 119564562Sgshapiro} 119690792Sgshapiro/* 119790792Sgshapiro** MILTER_CONFIG -- parse option list into an array and check config 119864562Sgshapiro** 119964562Sgshapiro** Called when reading configuration file. 120064562Sgshapiro** 120164562Sgshapiro** Parameters: 120264562Sgshapiro** spec -- the filter list. 120364562Sgshapiro** list -- the array to fill in. 120464562Sgshapiro** max -- the maximum number of entries in list. 120564562Sgshapiro** 120664562Sgshapiro** Returns: 120764562Sgshapiro** none 120864562Sgshapiro*/ 120964562Sgshapiro 121064562Sgshapirovoid 121190792Sgshapiromilter_config(spec, list, max) 121264562Sgshapiro char *spec; 121364562Sgshapiro struct milter **list; 121464562Sgshapiro int max; 121564562Sgshapiro{ 121664562Sgshapiro int numitems = 0; 121764562Sgshapiro register char *p; 121864562Sgshapiro 121964562Sgshapiro /* leave one for the NULL signifying the end of the list */ 122064562Sgshapiro max--; 122164562Sgshapiro 122264562Sgshapiro for (p = spec; p != NULL; ) 122364562Sgshapiro { 122464562Sgshapiro STAB *s; 122564562Sgshapiro 122664562Sgshapiro while (isascii(*p) && isspace(*p)) 122764562Sgshapiro p++; 122864562Sgshapiro if (*p == '\0') 122964562Sgshapiro break; 123064562Sgshapiro spec = p; 123164562Sgshapiro 123264562Sgshapiro if (numitems >= max) 123364562Sgshapiro { 123464562Sgshapiro syserr("Too many filters defined, %d max", max); 123564562Sgshapiro if (max > 0) 123664562Sgshapiro list[0] = NULL; 123764562Sgshapiro return; 123864562Sgshapiro } 123990792Sgshapiro#if _FFR_MILTER_PERDAEMON 124090792Sgshapiro p = strpbrk(p, ";,"); 124190792Sgshapiro#else /* _FFR_MILTER_PERDAEMON */ 124264562Sgshapiro p = strpbrk(p, ","); 124390792Sgshapiro#endif /* _FFR_MILTER_PERDAEMON */ 124464562Sgshapiro if (p != NULL) 124564562Sgshapiro *p++ = '\0'; 124664562Sgshapiro 124764562Sgshapiro s = stab(spec, ST_MILTER, ST_FIND); 124864562Sgshapiro if (s == NULL) 124964562Sgshapiro { 125064562Sgshapiro syserr("InputFilter %s not defined", spec); 125164562Sgshapiro ExitStat = EX_CONFIG; 125264562Sgshapiro return; 125364562Sgshapiro } 125464562Sgshapiro list[numitems++] = s->s_milter; 125564562Sgshapiro } 125664562Sgshapiro list[numitems] = NULL; 125790792Sgshapiro 125890792Sgshapiro /* if not set, set to LogLevel */ 125990792Sgshapiro if (MilterLogLevel == -1) 126090792Sgshapiro MilterLogLevel = LogLevel; 126164562Sgshapiro} 126290792Sgshapiro/* 126364562Sgshapiro** MILTER_PARSE_TIMEOUTS -- parse timeout list 126464562Sgshapiro** 126564562Sgshapiro** Called when reading configuration file. 126664562Sgshapiro** 126764562Sgshapiro** Parameters: 126864562Sgshapiro** spec -- the timeout list. 126964562Sgshapiro** m -- milter to set. 127064562Sgshapiro** 127164562Sgshapiro** Returns: 127264562Sgshapiro** none 127364562Sgshapiro*/ 127464562Sgshapiro 127564562Sgshapirostatic void 127664562Sgshapiromilter_parse_timeouts(spec, m) 127764562Sgshapiro char *spec; 127864562Sgshapiro struct milter *m; 127964562Sgshapiro{ 128064562Sgshapiro char fcode; 128164562Sgshapiro register char *p; 128264562Sgshapiro 128364562Sgshapiro p = spec; 128464562Sgshapiro 128564562Sgshapiro /* now scan through and assign info from the fields */ 128664562Sgshapiro while (*p != '\0') 128764562Sgshapiro { 128864562Sgshapiro char *delimptr; 128964562Sgshapiro 129064562Sgshapiro while (*p != '\0' && 129164562Sgshapiro (*p == ';' || (isascii(*p) && isspace(*p)))) 129264562Sgshapiro p++; 129364562Sgshapiro 129464562Sgshapiro /* p now points to field code */ 129564562Sgshapiro fcode = *p; 129664562Sgshapiro while (*p != '\0' && *p != ':') 129764562Sgshapiro p++; 129864562Sgshapiro if (*p++ != ':') 129964562Sgshapiro { 130064562Sgshapiro syserr("X%s, T=: `:' expected", m->mf_name); 130164562Sgshapiro return; 130264562Sgshapiro } 130364562Sgshapiro while (isascii(*p) && isspace(*p)) 130464562Sgshapiro p++; 130564562Sgshapiro 130664562Sgshapiro /* p now points to the field body */ 130764562Sgshapiro p = munchstring(p, &delimptr, ';'); 130864562Sgshapiro 130966494Sgshapiro /* install the field into the filter struct */ 131064562Sgshapiro switch (fcode) 131164562Sgshapiro { 131282017Sgshapiro case 'C': 131382017Sgshapiro m->mf_timeout[SMFTO_CONNECT] = convtime(p, 's'); 131482017Sgshapiro if (tTd(64, 5)) 131590792Sgshapiro sm_dprintf("X%s: %c=%lu\n", 131690792Sgshapiro m->mf_name, fcode, 131790792Sgshapiro (unsigned long) m->mf_timeout[SMFTO_CONNECT]); 131882017Sgshapiro break; 131982017Sgshapiro 132064562Sgshapiro case 'S': 132164562Sgshapiro m->mf_timeout[SMFTO_WRITE] = convtime(p, 's'); 132264562Sgshapiro if (tTd(64, 5)) 132390792Sgshapiro sm_dprintf("X%s: %c=%lu\n", 132490792Sgshapiro m->mf_name, fcode, 132590792Sgshapiro (unsigned long) m->mf_timeout[SMFTO_WRITE]); 132664562Sgshapiro break; 132764562Sgshapiro 132864562Sgshapiro case 'R': 132964562Sgshapiro m->mf_timeout[SMFTO_READ] = convtime(p, 's'); 133064562Sgshapiro if (tTd(64, 5)) 133190792Sgshapiro sm_dprintf("X%s: %c=%lu\n", 133290792Sgshapiro m->mf_name, fcode, 133390792Sgshapiro (unsigned long) m->mf_timeout[SMFTO_READ]); 133464562Sgshapiro break; 133564562Sgshapiro 133664562Sgshapiro case 'E': 133764562Sgshapiro m->mf_timeout[SMFTO_EOM] = convtime(p, 's'); 133864562Sgshapiro if (tTd(64, 5)) 133990792Sgshapiro sm_dprintf("X%s: %c=%lu\n", 134090792Sgshapiro m->mf_name, fcode, 134190792Sgshapiro (unsigned long) m->mf_timeout[SMFTO_EOM]); 134264562Sgshapiro break; 134364562Sgshapiro 134464562Sgshapiro default: 134564562Sgshapiro if (tTd(64, 5)) 134690792Sgshapiro sm_dprintf("X%s: %c unknown\n", 134790792Sgshapiro m->mf_name, fcode); 134864562Sgshapiro syserr("X%s: unknown filter timeout %c", 134964562Sgshapiro m->mf_name, fcode); 135064562Sgshapiro break; 135164562Sgshapiro } 135264562Sgshapiro p = delimptr; 135364562Sgshapiro } 135464562Sgshapiro} 135590792Sgshapiro/* 135664562Sgshapiro** MILTER_SET_OPTION -- set an individual milter option 135764562Sgshapiro** 135864562Sgshapiro** Parameters: 135964562Sgshapiro** name -- the name of the option. 136064562Sgshapiro** val -- the value of the option. 136164562Sgshapiro** sticky -- if set, don't let other setoptions override 136264562Sgshapiro** this value. 136364562Sgshapiro** 136464562Sgshapiro** Returns: 136564562Sgshapiro** none. 136664562Sgshapiro*/ 136764562Sgshapiro 136864562Sgshapiro/* set if Milter sub-option is stuck */ 136964562Sgshapirostatic BITMAP256 StickyMilterOpt; 137064562Sgshapiro 137164562Sgshapirostatic struct milteropt 137264562Sgshapiro{ 137390792Sgshapiro char *mo_name; /* long name of milter option */ 137490792Sgshapiro unsigned char mo_code; /* code for option */ 137564562Sgshapiro} MilterOptTab[] = 137664562Sgshapiro{ 137764562Sgshapiro# define MO_MACROS_CONNECT 0x01 137864562Sgshapiro { "macros.connect", MO_MACROS_CONNECT }, 137964562Sgshapiro# define MO_MACROS_HELO 0x02 138064562Sgshapiro { "macros.helo", MO_MACROS_HELO }, 138164562Sgshapiro# define MO_MACROS_ENVFROM 0x03 138264562Sgshapiro { "macros.envfrom", MO_MACROS_ENVFROM }, 138364562Sgshapiro# define MO_MACROS_ENVRCPT 0x04 138464562Sgshapiro { "macros.envrcpt", MO_MACROS_ENVRCPT }, 138590792Sgshapiro# define MO_LOGLEVEL 0x05 138690792Sgshapiro { "loglevel", MO_LOGLEVEL }, 138764562Sgshapiro { NULL, 0 }, 138864562Sgshapiro}; 138964562Sgshapiro 139064562Sgshapirovoid 139164562Sgshapiromilter_set_option(name, val, sticky) 139264562Sgshapiro char *name; 139364562Sgshapiro char *val; 139464562Sgshapiro bool sticky; 139564562Sgshapiro{ 139664562Sgshapiro int nummac = 0; 139764562Sgshapiro register struct milteropt *mo; 139864562Sgshapiro char *p; 139964562Sgshapiro char **macros = NULL; 140064562Sgshapiro 140164562Sgshapiro if (tTd(37, 2) || tTd(64, 5)) 140290792Sgshapiro sm_dprintf("milter_set_option(%s = %s)", name, val); 140364562Sgshapiro 140464562Sgshapiro for (mo = MilterOptTab; mo->mo_name != NULL; mo++) 140564562Sgshapiro { 140690792Sgshapiro if (sm_strcasecmp(mo->mo_name, name) == 0) 140764562Sgshapiro break; 140864562Sgshapiro } 140964562Sgshapiro 141064562Sgshapiro if (mo->mo_name == NULL) 141190792Sgshapiro { 141264562Sgshapiro syserr("milter_set_option: invalid Milter option %s", name); 141390792Sgshapiro return; 141490792Sgshapiro } 141564562Sgshapiro 141664562Sgshapiro /* 141764562Sgshapiro ** See if this option is preset for us. 141864562Sgshapiro */ 141964562Sgshapiro 142064562Sgshapiro if (!sticky && bitnset(mo->mo_code, StickyMilterOpt)) 142164562Sgshapiro { 142264562Sgshapiro if (tTd(37, 2) || tTd(64,5)) 142390792Sgshapiro sm_dprintf(" (ignored)\n"); 142464562Sgshapiro return; 142564562Sgshapiro } 142664562Sgshapiro 142764562Sgshapiro if (tTd(37, 2) || tTd(64,5)) 142890792Sgshapiro sm_dprintf("\n"); 142964562Sgshapiro 143064562Sgshapiro switch (mo->mo_code) 143164562Sgshapiro { 143290792Sgshapiro case MO_LOGLEVEL: 143390792Sgshapiro MilterLogLevel = atoi(val); 143490792Sgshapiro break; 143590792Sgshapiro 143664562Sgshapiro case MO_MACROS_CONNECT: 143764562Sgshapiro if (macros == NULL) 143864562Sgshapiro macros = MilterConnectMacros; 143964562Sgshapiro /* FALLTHROUGH */ 144064562Sgshapiro 144164562Sgshapiro case MO_MACROS_HELO: 144264562Sgshapiro if (macros == NULL) 144364562Sgshapiro macros = MilterHeloMacros; 144464562Sgshapiro /* FALLTHROUGH */ 144564562Sgshapiro 144664562Sgshapiro case MO_MACROS_ENVFROM: 144764562Sgshapiro if (macros == NULL) 144864562Sgshapiro macros = MilterEnvFromMacros; 144964562Sgshapiro /* FALLTHROUGH */ 145064562Sgshapiro 145164562Sgshapiro case MO_MACROS_ENVRCPT: 145264562Sgshapiro if (macros == NULL) 145364562Sgshapiro macros = MilterEnvRcptMacros; 145464562Sgshapiro 145564562Sgshapiro p = newstr(val); 145664562Sgshapiro while (*p != '\0') 145764562Sgshapiro { 145864562Sgshapiro char *macro; 145964562Sgshapiro 146064562Sgshapiro /* Skip leading commas, spaces */ 146164562Sgshapiro while (*p != '\0' && 146264562Sgshapiro (*p == ',' || (isascii(*p) && isspace(*p)))) 146364562Sgshapiro p++; 146464562Sgshapiro 146564562Sgshapiro if (*p == '\0') 146664562Sgshapiro break; 146764562Sgshapiro 146864562Sgshapiro /* Find end of macro */ 146964562Sgshapiro macro = p; 147064562Sgshapiro while (*p != '\0' && *p != ',' && 147164562Sgshapiro isascii(*p) && !isspace(*p)) 147264562Sgshapiro p++; 147364562Sgshapiro if (*p != '\0') 147464562Sgshapiro *p++ = '\0'; 147564562Sgshapiro 147664562Sgshapiro if (nummac >= MAXFILTERMACROS) 147764562Sgshapiro { 147864562Sgshapiro syserr("milter_set_option: too many macros in Milter.%s (max %d)", 147964562Sgshapiro name, MAXFILTERMACROS); 148064562Sgshapiro macros[nummac] = NULL; 148164562Sgshapiro break; 148264562Sgshapiro } 148364562Sgshapiro macros[nummac++] = macro; 148464562Sgshapiro } 148564562Sgshapiro macros[nummac] = NULL; 148664562Sgshapiro break; 148764562Sgshapiro 148864562Sgshapiro default: 148964562Sgshapiro syserr("milter_set_option: invalid Milter option %s", name); 149064562Sgshapiro break; 149164562Sgshapiro } 149264562Sgshapiro if (sticky) 149364562Sgshapiro setbitn(mo->mo_code, StickyMilterOpt); 149464562Sgshapiro} 149590792Sgshapiro/* 149690792Sgshapiro** MILTER_REOPEN_DF -- open & truncate the data file (for replbody) 149764562Sgshapiro** 149864562Sgshapiro** Parameters: 149964562Sgshapiro** e -- current envelope. 150064562Sgshapiro** 150164562Sgshapiro** Returns: 150264562Sgshapiro** 0 if succesful, -1 otherwise 150364562Sgshapiro*/ 150464562Sgshapiro 150564562Sgshapirostatic int 150664562Sgshapiromilter_reopen_df(e) 150764562Sgshapiro ENVELOPE *e; 150864562Sgshapiro{ 150964562Sgshapiro char dfname[MAXPATHLEN]; 151064562Sgshapiro 151190792Sgshapiro (void) sm_strlcpy(dfname, queuename(e, DATAFL_LETTER), sizeof dfname); 151264562Sgshapiro 151364562Sgshapiro /* 151490792Sgshapiro ** In SuperSafe == SAFE_REALLY mode, e->e_dfp is a read-only FP so 151564562Sgshapiro ** close and reopen writable (later close and reopen 151664562Sgshapiro ** read only again). 151764562Sgshapiro ** 151890792Sgshapiro ** In SuperSafe != SAFE_REALLY mode, e->e_dfp still points at the 151964562Sgshapiro ** buffered file I/O descriptor, still open for writing 152064562Sgshapiro ** so there isn't as much work to do, just truncate it 152164562Sgshapiro ** and go. 152264562Sgshapiro */ 152364562Sgshapiro 152490792Sgshapiro if (SuperSafe == SAFE_REALLY) 152564562Sgshapiro { 152690792Sgshapiro /* close read-only data file */ 152764562Sgshapiro if (bitset(EF_HAS_DF, e->e_flags) && e->e_dfp != NULL) 152864562Sgshapiro { 152990792Sgshapiro (void) sm_io_close(e->e_dfp, SM_TIME_DEFAULT); 153064562Sgshapiro e->e_flags &= ~EF_HAS_DF; 153164562Sgshapiro } 153264562Sgshapiro 153364562Sgshapiro /* open writable */ 153490792Sgshapiro if ((e->e_dfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, dfname, 153590792Sgshapiro SM_IO_RDWR, NULL)) == NULL) 153664562Sgshapiro { 153790792Sgshapiro MILTER_DF_ERROR("milter_reopen_df: sm_io_open %s: %s"); 153864562Sgshapiro return -1; 153964562Sgshapiro } 154064562Sgshapiro } 154164562Sgshapiro else if (e->e_dfp == NULL) 154264562Sgshapiro { 154364562Sgshapiro /* shouldn't happen */ 154464562Sgshapiro errno = ENOENT; 154564562Sgshapiro MILTER_DF_ERROR("milter_reopen_df: NULL e_dfp (%s: %s)"); 154664562Sgshapiro return -1; 154764562Sgshapiro } 154864562Sgshapiro return 0; 154964562Sgshapiro} 155090792Sgshapiro/* 155190792Sgshapiro** MILTER_RESET_DF -- re-open read-only the data file (for replbody) 155264562Sgshapiro** 155364562Sgshapiro** Parameters: 155464562Sgshapiro** e -- current envelope. 155564562Sgshapiro** 155664562Sgshapiro** Returns: 155764562Sgshapiro** 0 if succesful, -1 otherwise 155864562Sgshapiro*/ 155964562Sgshapiro 156064562Sgshapirostatic int 156164562Sgshapiromilter_reset_df(e) 156264562Sgshapiro ENVELOPE *e; 156364562Sgshapiro{ 156464562Sgshapiro int afd; 156564562Sgshapiro char dfname[MAXPATHLEN]; 156664562Sgshapiro 156790792Sgshapiro (void) sm_strlcpy(dfname, queuename(e, DATAFL_LETTER), sizeof dfname); 156864562Sgshapiro 156990792Sgshapiro if (sm_io_flush(e->e_dfp, SM_TIME_DEFAULT) != 0 || 157090792Sgshapiro sm_io_error(e->e_dfp)) 157164562Sgshapiro { 157264562Sgshapiro MILTER_DF_ERROR("milter_reset_df: error writing/flushing %s: %s"); 157364562Sgshapiro return -1; 157464562Sgshapiro } 157590792Sgshapiro else if (SuperSafe != SAFE_REALLY) 157664562Sgshapiro { 157764562Sgshapiro /* skip next few clauses */ 157864562Sgshapiro /* EMPTY */ 157964562Sgshapiro } 158090792Sgshapiro else if ((afd = sm_io_getinfo(e->e_dfp, SM_IO_WHAT_FD, NULL)) >= 0 158190792Sgshapiro && fsync(afd) < 0) 158264562Sgshapiro { 158364562Sgshapiro MILTER_DF_ERROR("milter_reset_df: error sync'ing %s: %s"); 158464562Sgshapiro return -1; 158564562Sgshapiro } 158690792Sgshapiro else if (sm_io_close(e->e_dfp, SM_TIME_DEFAULT) < 0) 158764562Sgshapiro { 158864562Sgshapiro MILTER_DF_ERROR("milter_reset_df: error closing %s: %s"); 158964562Sgshapiro return -1; 159064562Sgshapiro } 159190792Sgshapiro else if ((e->e_dfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, dfname, 159290792Sgshapiro SM_IO_RDONLY, NULL)) == NULL) 159364562Sgshapiro { 159464562Sgshapiro MILTER_DF_ERROR("milter_reset_df: error reopening %s: %s"); 159564562Sgshapiro return -1; 159664562Sgshapiro } 159764562Sgshapiro else 159864562Sgshapiro e->e_flags |= EF_HAS_DF; 159964562Sgshapiro return 0; 160064562Sgshapiro} 160190792Sgshapiro/* 160264562Sgshapiro** MILTER_CAN_DELRCPTS -- can any milter filters delete recipients? 160364562Sgshapiro** 160464562Sgshapiro** Parameters: 160564562Sgshapiro** none 160664562Sgshapiro** 160764562Sgshapiro** Returns: 160890792Sgshapiro** true if any filter deletes recipients, false otherwise 160964562Sgshapiro*/ 161064562Sgshapiro 161164562Sgshapirobool 161264562Sgshapiromilter_can_delrcpts() 161364562Sgshapiro{ 161490792Sgshapiro bool can = false; 161564562Sgshapiro int i; 161664562Sgshapiro 161764562Sgshapiro if (tTd(64, 10)) 161890792Sgshapiro sm_dprintf("milter_can_delrcpts:"); 161964562Sgshapiro 162064562Sgshapiro for (i = 0; InputFilters[i] != NULL; i++) 162164562Sgshapiro { 162264562Sgshapiro struct milter *m = InputFilters[i]; 162364562Sgshapiro 162464562Sgshapiro if (bitset(SMFIF_DELRCPT, m->mf_fflags)) 162564562Sgshapiro { 162690792Sgshapiro can = true; 162764562Sgshapiro break; 162864562Sgshapiro } 162964562Sgshapiro } 163064562Sgshapiro if (tTd(64, 10)) 163190792Sgshapiro sm_dprintf("%s\n", can ? "true" : "false"); 163264562Sgshapiro 163364562Sgshapiro return can; 163464562Sgshapiro} 163590792Sgshapiro/* 163664562Sgshapiro** MILTER_QUIT_FILTER -- close down a single filter 163764562Sgshapiro** 163864562Sgshapiro** Parameters: 163964562Sgshapiro** m -- milter structure of filter to close down. 164064562Sgshapiro** e -- current envelope. 164164562Sgshapiro** 164264562Sgshapiro** Returns: 164364562Sgshapiro** none 164464562Sgshapiro*/ 164564562Sgshapiro 164664562Sgshapirostatic void 164764562Sgshapiromilter_quit_filter(m, e) 164864562Sgshapiro struct milter *m; 164964562Sgshapiro ENVELOPE *e; 165064562Sgshapiro{ 165164562Sgshapiro if (tTd(64, 10)) 165290792Sgshapiro sm_dprintf("milter_quit_filter(%s)\n", m->mf_name); 165390792Sgshapiro if (MilterLogLevel > 18) 165490792Sgshapiro sm_syslog(LOG_INFO, e->e_id, "Milter (%s): quit filter", 165590792Sgshapiro m->mf_name); 165664562Sgshapiro 165764562Sgshapiro /* Never replace error state */ 165864562Sgshapiro if (m->mf_state == SMFS_ERROR) 165964562Sgshapiro return; 166064562Sgshapiro 166164562Sgshapiro if (m->mf_sock < 0 || 166264562Sgshapiro m->mf_state == SMFS_CLOSED || 166364562Sgshapiro m->mf_state == SMFS_READY) 166464562Sgshapiro { 166564562Sgshapiro m->mf_sock = -1; 166664562Sgshapiro m->mf_state = SMFS_CLOSED; 166764562Sgshapiro return; 166864562Sgshapiro } 166964562Sgshapiro 167064562Sgshapiro (void) milter_write(m, SMFIC_QUIT, (char *) NULL, 0, 167164562Sgshapiro m->mf_timeout[SMFTO_WRITE], e); 167271345Sgshapiro if (m->mf_sock >= 0) 167371345Sgshapiro { 167471345Sgshapiro (void) close(m->mf_sock); 167571345Sgshapiro m->mf_sock = -1; 167671345Sgshapiro } 167764562Sgshapiro if (m->mf_state != SMFS_ERROR) 167864562Sgshapiro m->mf_state = SMFS_CLOSED; 167964562Sgshapiro} 168090792Sgshapiro/* 168164562Sgshapiro** MILTER_ABORT_FILTER -- tell filter to abort current message 168264562Sgshapiro** 168364562Sgshapiro** Parameters: 168464562Sgshapiro** m -- milter structure of filter to abort. 168564562Sgshapiro** e -- current envelope. 168664562Sgshapiro** 168764562Sgshapiro** Returns: 168864562Sgshapiro** none 168964562Sgshapiro*/ 169064562Sgshapiro 169164562Sgshapirostatic void 169264562Sgshapiromilter_abort_filter(m, e) 169364562Sgshapiro struct milter *m; 169464562Sgshapiro ENVELOPE *e; 169564562Sgshapiro{ 169664562Sgshapiro if (tTd(64, 10)) 169790792Sgshapiro sm_dprintf("milter_abort_filter(%s)\n", m->mf_name); 169890792Sgshapiro if (MilterLogLevel > 10) 169990792Sgshapiro sm_syslog(LOG_INFO, e->e_id, "Milter (%s): abort filter", 170090792Sgshapiro m->mf_name); 170164562Sgshapiro 170264562Sgshapiro if (m->mf_sock < 0 || 170364562Sgshapiro m->mf_state != SMFS_INMSG) 170464562Sgshapiro return; 170564562Sgshapiro 170664562Sgshapiro (void) milter_write(m, SMFIC_ABORT, (char *) NULL, 0, 170764562Sgshapiro m->mf_timeout[SMFTO_WRITE], e); 170864562Sgshapiro if (m->mf_state != SMFS_ERROR) 170964562Sgshapiro m->mf_state = SMFS_DONE; 171064562Sgshapiro} 171190792Sgshapiro/* 171264562Sgshapiro** MILTER_SEND_MACROS -- provide macros to the filters 171364562Sgshapiro** 171464562Sgshapiro** Parameters: 171564562Sgshapiro** m -- milter to send macros to. 171664562Sgshapiro** macros -- macros to send for filter smfi_getsymval(). 171764562Sgshapiro** cmd -- which command the macros are associated with. 171864562Sgshapiro** e -- current envelope (for macro access). 171964562Sgshapiro** 172064562Sgshapiro** Returns: 172164562Sgshapiro** none 172264562Sgshapiro*/ 172364562Sgshapiro 172464562Sgshapirostatic void 172564562Sgshapiromilter_send_macros(m, macros, cmd, e) 172664562Sgshapiro struct milter *m; 172764562Sgshapiro char **macros; 172864562Sgshapiro char cmd; 172964562Sgshapiro ENVELOPE *e; 173064562Sgshapiro{ 173164562Sgshapiro int i; 173264562Sgshapiro int mid; 173364562Sgshapiro char *v; 173464562Sgshapiro char *buf, *bp; 173564562Sgshapiro ssize_t s; 173664562Sgshapiro 173764562Sgshapiro /* sanity check */ 173864562Sgshapiro if (macros == NULL || macros[0] == NULL) 173964562Sgshapiro return; 174064562Sgshapiro 174164562Sgshapiro /* put together data */ 174264562Sgshapiro s = 1; /* for the command character */ 174364562Sgshapiro for (i = 0; macros[i] != NULL; i++) 174464562Sgshapiro { 174590792Sgshapiro mid = macid(macros[i]); 174671345Sgshapiro if (mid == 0) 174764562Sgshapiro continue; 174864562Sgshapiro v = macvalue(mid, e); 174964562Sgshapiro if (v == NULL) 175064562Sgshapiro continue; 175164562Sgshapiro s += strlen(macros[i]) + 1 + strlen(v) + 1; 175264562Sgshapiro } 175364562Sgshapiro 175490792Sgshapiro if (s < 0) 175590792Sgshapiro return; 175690792Sgshapiro 175790792Sgshapiro buf = (char *) xalloc(s); 175864562Sgshapiro bp = buf; 175964562Sgshapiro *bp++ = cmd; 176064562Sgshapiro for (i = 0; macros[i] != NULL; i++) 176164562Sgshapiro { 176290792Sgshapiro mid = macid(macros[i]); 176371345Sgshapiro if (mid == 0) 176464562Sgshapiro continue; 176564562Sgshapiro v = macvalue(mid, e); 176664562Sgshapiro if (v == NULL) 176764562Sgshapiro continue; 176864562Sgshapiro 176964562Sgshapiro if (tTd(64, 10)) 177090792Sgshapiro sm_dprintf("milter_send_macros(%s, %c): %s=%s\n", 177164562Sgshapiro m->mf_name, cmd, macros[i], v); 177264562Sgshapiro 177390792Sgshapiro (void) sm_strlcpy(bp, macros[i], s - (bp - buf)); 177464562Sgshapiro bp += strlen(bp) + 1; 177590792Sgshapiro (void) sm_strlcpy(bp, v, s - (bp - buf)); 177664562Sgshapiro bp += strlen(bp) + 1; 177764562Sgshapiro } 177864562Sgshapiro (void) milter_write(m, SMFIC_MACRO, buf, s, 177964562Sgshapiro m->mf_timeout[SMFTO_WRITE], e); 178090792Sgshapiro sm_free(buf); /* XXX */ 178164562Sgshapiro} 178264562Sgshapiro 178390792Sgshapiro/* 178464562Sgshapiro** MILTER_SEND_COMMAND -- send a command and return the response for a filter 178564562Sgshapiro** 178664562Sgshapiro** Parameters: 178764562Sgshapiro** m -- current milter filter 178864562Sgshapiro** command -- command to send. 178964562Sgshapiro** data -- optional command data. 179064562Sgshapiro** sz -- length of buf. 179164562Sgshapiro** e -- current envelope (for e->e_id). 179264562Sgshapiro** state -- return state word. 179364562Sgshapiro** 179464562Sgshapiro** Returns: 179564562Sgshapiro** response string (may be NULL) 179664562Sgshapiro*/ 179764562Sgshapiro 179864562Sgshapirostatic char * 179964562Sgshapiromilter_send_command(m, command, data, sz, e, state) 180064562Sgshapiro struct milter *m; 180164562Sgshapiro char command; 180264562Sgshapiro void *data; 180364562Sgshapiro ssize_t sz; 180464562Sgshapiro ENVELOPE *e; 180564562Sgshapiro char *state; 180664562Sgshapiro{ 180764562Sgshapiro char rcmd; 180864562Sgshapiro ssize_t rlen; 180990792Sgshapiro unsigned long skipflag; 181090792Sgshapiro char *action; 181164562Sgshapiro char *defresponse; 181264562Sgshapiro char *response; 181364562Sgshapiro 181464562Sgshapiro if (tTd(64, 10)) 181590792Sgshapiro sm_dprintf("milter_send_command(%s): cmd %c len %ld\n", 181664562Sgshapiro m->mf_name, (char) command, (long) sz); 181764562Sgshapiro 181864562Sgshapiro /* find skip flag and default failure */ 181964562Sgshapiro switch (command) 182064562Sgshapiro { 182164562Sgshapiro case SMFIC_CONNECT: 182264562Sgshapiro skipflag = SMFIP_NOCONNECT; 182390792Sgshapiro action = "connect"; 182464562Sgshapiro defresponse = "554 Command rejected"; 182564562Sgshapiro break; 182664562Sgshapiro 182764562Sgshapiro case SMFIC_HELO: 182864562Sgshapiro skipflag = SMFIP_NOHELO; 182990792Sgshapiro action = "helo"; 183064562Sgshapiro defresponse = "550 Command rejected"; 183164562Sgshapiro break; 183264562Sgshapiro 183364562Sgshapiro case SMFIC_MAIL: 183464562Sgshapiro skipflag = SMFIP_NOMAIL; 183590792Sgshapiro action = "mail"; 183664562Sgshapiro defresponse = "550 5.7.1 Command rejected"; 183764562Sgshapiro break; 183864562Sgshapiro 183964562Sgshapiro case SMFIC_RCPT: 184064562Sgshapiro skipflag = SMFIP_NORCPT; 184190792Sgshapiro action = "rcpt"; 184264562Sgshapiro defresponse = "550 5.7.1 Command rejected"; 184364562Sgshapiro break; 184464562Sgshapiro 184564562Sgshapiro case SMFIC_HEADER: 184664562Sgshapiro skipflag = SMFIP_NOHDRS; 184790792Sgshapiro action = "header"; 184864562Sgshapiro defresponse = "550 5.7.1 Command rejected"; 184964562Sgshapiro break; 185064562Sgshapiro 185164562Sgshapiro case SMFIC_BODY: 185264562Sgshapiro skipflag = SMFIP_NOBODY; 185390792Sgshapiro action = "body"; 185464562Sgshapiro defresponse = "554 5.7.1 Command rejected"; 185564562Sgshapiro break; 185664562Sgshapiro 185764562Sgshapiro case SMFIC_EOH: 185864562Sgshapiro skipflag = SMFIP_NOEOH; 185990792Sgshapiro action = "eoh"; 186064562Sgshapiro defresponse = "550 5.7.1 Command rejected"; 186164562Sgshapiro break; 186264562Sgshapiro 186364562Sgshapiro case SMFIC_BODYEOB: 186464562Sgshapiro case SMFIC_OPTNEG: 186564562Sgshapiro case SMFIC_MACRO: 186664562Sgshapiro case SMFIC_ABORT: 186764562Sgshapiro case SMFIC_QUIT: 186864562Sgshapiro /* NOTE: not handled by milter_send_command() */ 186964562Sgshapiro /* FALLTHROUGH */ 187064562Sgshapiro 187164562Sgshapiro default: 187264562Sgshapiro skipflag = 0; 187390792Sgshapiro action = "default"; 187464562Sgshapiro defresponse = "550 5.7.1 Command rejected"; 187564562Sgshapiro break; 187664562Sgshapiro } 187764562Sgshapiro 187864562Sgshapiro /* check if filter wants this command */ 187964562Sgshapiro if (skipflag != 0 && 188064562Sgshapiro bitset(skipflag, m->mf_pflags)) 188164562Sgshapiro return NULL; 188264562Sgshapiro 188390792Sgshapiro /* send the command to the filter */ 188464562Sgshapiro (void) milter_write(m, command, data, sz, 188564562Sgshapiro m->mf_timeout[SMFTO_WRITE], e); 188664562Sgshapiro if (m->mf_state == SMFS_ERROR) 188764562Sgshapiro { 188894334Sgshapiro MILTER_CHECK_ERROR(return NULL); 188964562Sgshapiro return NULL; 189064562Sgshapiro } 189164562Sgshapiro 189290792Sgshapiro /* get the response from the filter */ 189364562Sgshapiro response = milter_read(m, &rcmd, &rlen, 189464562Sgshapiro m->mf_timeout[SMFTO_READ], e); 189564562Sgshapiro if (m->mf_state == SMFS_ERROR) 189664562Sgshapiro { 189794334Sgshapiro MILTER_CHECK_ERROR(return NULL); 189864562Sgshapiro return NULL; 189964562Sgshapiro } 190064562Sgshapiro 190164562Sgshapiro if (tTd(64, 10)) 190290792Sgshapiro sm_dprintf("milter_send_command(%s): returned %c\n", 190390792Sgshapiro m->mf_name, (char) rcmd); 190464562Sgshapiro 190564562Sgshapiro switch (rcmd) 190664562Sgshapiro { 190764562Sgshapiro case SMFIR_REPLYCODE: 190864562Sgshapiro MILTER_CHECK_REPLYCODE(defresponse); 190990792Sgshapiro if (MilterLogLevel > 10) 191090792Sgshapiro sm_syslog(LOG_INFO, e->e_id, "milter=%s, action=%s, reject=%s", 191190792Sgshapiro m->mf_name, action, response); 191290792Sgshapiro *state = rcmd; 191390792Sgshapiro break; 191464562Sgshapiro 191564562Sgshapiro case SMFIR_REJECT: 191690792Sgshapiro if (MilterLogLevel > 10) 191790792Sgshapiro sm_syslog(LOG_INFO, e->e_id, "milter=%s, action=%s, reject", 191890792Sgshapiro m->mf_name, action); 191990792Sgshapiro *state = rcmd; 192090792Sgshapiro break; 192190792Sgshapiro 192264562Sgshapiro case SMFIR_DISCARD: 192390792Sgshapiro if (MilterLogLevel > 10) 192490792Sgshapiro sm_syslog(LOG_INFO, e->e_id, "milter=%s, action=%s, discard", 192590792Sgshapiro m->mf_name, action); 192690792Sgshapiro *state = rcmd; 192790792Sgshapiro break; 192890792Sgshapiro 192964562Sgshapiro case SMFIR_TEMPFAIL: 193090792Sgshapiro if (MilterLogLevel > 10) 193190792Sgshapiro sm_syslog(LOG_INFO, e->e_id, "milter=%s, action=%s, tempfail", 193290792Sgshapiro m->mf_name, action); 193364562Sgshapiro *state = rcmd; 193464562Sgshapiro break; 193564562Sgshapiro 193664562Sgshapiro case SMFIR_ACCEPT: 193764562Sgshapiro /* this filter is done with message/connection */ 193873188Sgshapiro if (command == SMFIC_HELO || 193973188Sgshapiro command == SMFIC_CONNECT) 194073188Sgshapiro m->mf_state = SMFS_CLOSABLE; 194173188Sgshapiro else 194273188Sgshapiro m->mf_state = SMFS_DONE; 194390792Sgshapiro if (MilterLogLevel > 10) 194490792Sgshapiro sm_syslog(LOG_INFO, e->e_id, "milter=%s, action=%s, accepted", 194590792Sgshapiro m->mf_name, action); 194664562Sgshapiro break; 194764562Sgshapiro 194864562Sgshapiro case SMFIR_CONTINUE: 194964562Sgshapiro /* if MAIL command is ok, filter is in message state */ 195064562Sgshapiro if (command == SMFIC_MAIL) 195164562Sgshapiro m->mf_state = SMFS_INMSG; 195290792Sgshapiro if (MilterLogLevel > 12) 195390792Sgshapiro sm_syslog(LOG_INFO, e->e_id, "milter=%s, action=%s, continue", 195490792Sgshapiro m->mf_name, action); 195564562Sgshapiro break; 195664562Sgshapiro 195764562Sgshapiro default: 195864562Sgshapiro /* Invalid response to command */ 195990792Sgshapiro if (MilterLogLevel > 0) 196064562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 196190792Sgshapiro "milter_send_command(%s): action=%s returned bogus response %c", 196290792Sgshapiro m->mf_name, action, rcmd); 196390792Sgshapiro milter_error(m, e); 196464562Sgshapiro break; 196564562Sgshapiro } 196664562Sgshapiro 196764562Sgshapiro if (*state != SMFIR_REPLYCODE && 196864562Sgshapiro response != NULL) 196964562Sgshapiro { 197090792Sgshapiro sm_free(response); /* XXX */ 197164562Sgshapiro response = NULL; 197264562Sgshapiro } 197364562Sgshapiro return response; 197464562Sgshapiro} 197564562Sgshapiro 197690792Sgshapiro/* 197764562Sgshapiro** MILTER_COMMAND -- send a command and return the response for each filter 197864562Sgshapiro** 197964562Sgshapiro** Parameters: 198064562Sgshapiro** command -- command to send. 198164562Sgshapiro** data -- optional command data. 198264562Sgshapiro** sz -- length of buf. 198364562Sgshapiro** macros -- macros to send for filter smfi_getsymval(). 198464562Sgshapiro** e -- current envelope (for macro access). 198564562Sgshapiro** state -- return state word. 198664562Sgshapiro** 198764562Sgshapiro** Returns: 198864562Sgshapiro** response string (may be NULL) 198964562Sgshapiro*/ 199064562Sgshapiro 199164562Sgshapirostatic char * 199264562Sgshapiromilter_command(command, data, sz, macros, e, state) 199364562Sgshapiro char command; 199464562Sgshapiro void *data; 199564562Sgshapiro ssize_t sz; 199664562Sgshapiro char **macros; 199764562Sgshapiro ENVELOPE *e; 199864562Sgshapiro char *state; 199964562Sgshapiro{ 200064562Sgshapiro int i; 200164562Sgshapiro char *response = NULL; 200290792Sgshapiro time_t tn = 0; 200364562Sgshapiro 200464562Sgshapiro if (tTd(64, 10)) 200590792Sgshapiro sm_dprintf("milter_command: cmd %c len %ld\n", 200664562Sgshapiro (char) command, (long) sz); 200764562Sgshapiro 200864562Sgshapiro *state = SMFIR_CONTINUE; 200964562Sgshapiro for (i = 0; InputFilters[i] != NULL; i++) 201064562Sgshapiro { 201164562Sgshapiro struct milter *m = InputFilters[i]; 201264562Sgshapiro 201371345Sgshapiro /* previous problem? */ 201471345Sgshapiro if (m->mf_state == SMFS_ERROR) 201571345Sgshapiro { 201671345Sgshapiro MILTER_CHECK_ERROR(continue); 201771345Sgshapiro break; 201871345Sgshapiro } 201971345Sgshapiro 202064562Sgshapiro /* sanity check */ 202164562Sgshapiro if (m->mf_sock < 0 || 202264562Sgshapiro (m->mf_state != SMFS_OPEN && m->mf_state != SMFS_INMSG)) 202364562Sgshapiro continue; 202464562Sgshapiro 202564562Sgshapiro /* send macros (regardless of whether we send command) */ 202664562Sgshapiro if (macros != NULL && macros[0] != NULL) 202764562Sgshapiro { 202864562Sgshapiro milter_send_macros(m, macros, command, e); 202964562Sgshapiro if (m->mf_state == SMFS_ERROR) 203064562Sgshapiro { 203164562Sgshapiro MILTER_CHECK_ERROR(continue); 203264562Sgshapiro break; 203364562Sgshapiro } 203464562Sgshapiro } 203564562Sgshapiro 203690792Sgshapiro if (MilterLogLevel > 21) 203790792Sgshapiro tn = curtime(); 203890792Sgshapiro 203964562Sgshapiro response = milter_send_command(m, command, data, sz, e, state); 204090792Sgshapiro 204190792Sgshapiro if (MilterLogLevel > 21) 204290792Sgshapiro { 204390792Sgshapiro /* log the time it took for the command per filter */ 204490792Sgshapiro sm_syslog(LOG_INFO, e->e_id, 204590792Sgshapiro "Milter (%s): time command (%c), %d", 204690792Sgshapiro m->mf_name, command, (int) (tn - curtime())); 204790792Sgshapiro } 204890792Sgshapiro 204964562Sgshapiro if (*state != SMFIR_CONTINUE) 205064562Sgshapiro break; 205164562Sgshapiro } 205264562Sgshapiro return response; 205364562Sgshapiro} 205490792Sgshapiro/* 205564562Sgshapiro** MILTER_NEGOTIATE -- get version and flags from filter 205664562Sgshapiro** 205764562Sgshapiro** Parameters: 205864562Sgshapiro** m -- milter filter structure. 205964562Sgshapiro** e -- current envelope. 206064562Sgshapiro** 206164562Sgshapiro** Returns: 206264562Sgshapiro** 0 on success, -1 otherwise 206364562Sgshapiro*/ 206464562Sgshapiro 206564562Sgshapirostatic int 206664562Sgshapiromilter_negotiate(m, e) 206764562Sgshapiro struct milter *m; 206864562Sgshapiro ENVELOPE *e; 206964562Sgshapiro{ 207064562Sgshapiro char rcmd; 207164562Sgshapiro mi_int32 fvers; 207264562Sgshapiro mi_int32 fflags; 207364562Sgshapiro mi_int32 pflags; 207464562Sgshapiro char *response; 207564562Sgshapiro ssize_t rlen; 207664562Sgshapiro char data[MILTER_OPTLEN]; 207764562Sgshapiro 207864562Sgshapiro /* sanity check */ 207964562Sgshapiro if (m->mf_sock < 0 || m->mf_state != SMFS_OPEN) 208064562Sgshapiro { 208190792Sgshapiro if (MilterLogLevel > 0) 208264562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 208390792Sgshapiro "Milter (%s): negotiate, impossible state", 208464562Sgshapiro m->mf_name); 208590792Sgshapiro milter_error(m, e); 208664562Sgshapiro return -1; 208764562Sgshapiro } 208864562Sgshapiro 208964562Sgshapiro fvers = htonl(SMFI_VERSION); 209064562Sgshapiro fflags = htonl(SMFI_CURR_ACTS); 209164562Sgshapiro pflags = htonl(SMFI_CURR_PROT); 209264562Sgshapiro (void) memcpy(data, (char *) &fvers, MILTER_LEN_BYTES); 209364562Sgshapiro (void) memcpy(data + MILTER_LEN_BYTES, 209464562Sgshapiro (char *) &fflags, MILTER_LEN_BYTES); 209564562Sgshapiro (void) memcpy(data + (MILTER_LEN_BYTES * 2), 209664562Sgshapiro (char *) &pflags, MILTER_LEN_BYTES); 209764562Sgshapiro (void) milter_write(m, SMFIC_OPTNEG, data, sizeof data, 209864562Sgshapiro m->mf_timeout[SMFTO_WRITE], e); 209964562Sgshapiro 210064562Sgshapiro if (m->mf_state == SMFS_ERROR) 210164562Sgshapiro return -1; 210264562Sgshapiro 210364562Sgshapiro response = milter_read(m, &rcmd, &rlen, m->mf_timeout[SMFTO_READ], e); 210464562Sgshapiro if (m->mf_state == SMFS_ERROR) 210564562Sgshapiro return -1; 210664562Sgshapiro 210764562Sgshapiro if (rcmd != SMFIC_OPTNEG) 210864562Sgshapiro { 210964562Sgshapiro if (tTd(64, 5)) 211090792Sgshapiro sm_dprintf("milter_negotiate(%s): returned %c instead of %c\n", 211164562Sgshapiro m->mf_name, rcmd, SMFIC_OPTNEG); 211290792Sgshapiro if (MilterLogLevel > 0) 211364562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 211490792Sgshapiro "Milter (%s): negotiate: returned %c instead of %c", 211564562Sgshapiro m->mf_name, rcmd, SMFIC_OPTNEG); 211664562Sgshapiro if (response != NULL) 211790792Sgshapiro sm_free(response); /* XXX */ 211890792Sgshapiro milter_error(m, e); 211964562Sgshapiro return -1; 212064562Sgshapiro } 212164562Sgshapiro 212264562Sgshapiro /* Make sure we have enough bytes for the version */ 212364562Sgshapiro if (response == NULL || rlen < MILTER_LEN_BYTES) 212464562Sgshapiro { 212564562Sgshapiro if (tTd(64, 5)) 212690792Sgshapiro sm_dprintf("milter_negotiate(%s): did not return valid info\n", 212764562Sgshapiro m->mf_name); 212890792Sgshapiro if (MilterLogLevel > 0) 212964562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 213090792Sgshapiro "Milter (%s): negotiate: did not return valid info", 213164562Sgshapiro m->mf_name); 213264562Sgshapiro if (response != NULL) 213390792Sgshapiro sm_free(response); /* XXX */ 213490792Sgshapiro milter_error(m, e); 213564562Sgshapiro return -1; 213664562Sgshapiro } 213764562Sgshapiro 213864562Sgshapiro /* extract information */ 213964562Sgshapiro (void) memcpy((char *) &fvers, response, MILTER_LEN_BYTES); 214064562Sgshapiro 214164562Sgshapiro /* Now make sure we have enough for the feature bitmap */ 214264562Sgshapiro if (rlen != MILTER_OPTLEN) 214364562Sgshapiro { 214464562Sgshapiro if (tTd(64, 5)) 214590792Sgshapiro sm_dprintf("milter_negotiate(%s): did not return enough info\n", 214664562Sgshapiro m->mf_name); 214790792Sgshapiro if (MilterLogLevel > 0) 214864562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 214990792Sgshapiro "Milter (%s): negotiate: did not return enough info", 215064562Sgshapiro m->mf_name); 215164562Sgshapiro if (response != NULL) 215290792Sgshapiro sm_free(response); /* XXX */ 215390792Sgshapiro milter_error(m, e); 215464562Sgshapiro return -1; 215564562Sgshapiro } 215664562Sgshapiro 215764562Sgshapiro (void) memcpy((char *) &fflags, response + MILTER_LEN_BYTES, 215864562Sgshapiro MILTER_LEN_BYTES); 215964562Sgshapiro (void) memcpy((char *) &pflags, response + (MILTER_LEN_BYTES * 2), 216064562Sgshapiro MILTER_LEN_BYTES); 216190792Sgshapiro sm_free(response); /* XXX */ 216264562Sgshapiro response = NULL; 216364562Sgshapiro 216464562Sgshapiro m->mf_fvers = ntohl(fvers); 216564562Sgshapiro m->mf_fflags = ntohl(fflags); 216664562Sgshapiro m->mf_pflags = ntohl(pflags); 216764562Sgshapiro 216864562Sgshapiro /* check for version compatibility */ 216964562Sgshapiro if (m->mf_fvers == 1 || 217064562Sgshapiro m->mf_fvers > SMFI_VERSION) 217164562Sgshapiro { 217264562Sgshapiro if (tTd(64, 5)) 217394334Sgshapiro sm_dprintf("milter_negotiate(%s): version %d != MTA milter version %d\n", 217464562Sgshapiro m->mf_name, m->mf_fvers, SMFI_VERSION); 217590792Sgshapiro if (MilterLogLevel > 0) 217664562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 217794334Sgshapiro "Milter (%s): negotiate: version %d != MTA milter version %d", 217864562Sgshapiro m->mf_name, m->mf_fvers, SMFI_VERSION); 217990792Sgshapiro milter_error(m, e); 218064562Sgshapiro return -1; 218164562Sgshapiro } 218264562Sgshapiro 218364562Sgshapiro /* check for filter feature mismatch */ 218464562Sgshapiro if ((m->mf_fflags & SMFI_CURR_ACTS) != m->mf_fflags) 218564562Sgshapiro { 218664562Sgshapiro if (tTd(64, 5)) 218794334Sgshapiro sm_dprintf("milter_negotiate(%s): filter abilities 0x%x != MTA milter abilities 0x%lx\n", 218864562Sgshapiro m->mf_name, m->mf_fflags, 218994334Sgshapiro SMFI_CURR_ACTS); 219090792Sgshapiro if (MilterLogLevel > 0) 219164562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 219294334Sgshapiro "Milter (%s): negotiate: filter abilities 0x%x != MTA milter abilities 0x%lx", 219364562Sgshapiro m->mf_name, m->mf_fflags, 219490792Sgshapiro (unsigned long) SMFI_CURR_ACTS); 219590792Sgshapiro milter_error(m, e); 219664562Sgshapiro return -1; 219764562Sgshapiro } 219864562Sgshapiro 219964562Sgshapiro /* check for protocol feature mismatch */ 220064562Sgshapiro if ((m->mf_pflags & SMFI_CURR_PROT) != m->mf_pflags) 220164562Sgshapiro { 220264562Sgshapiro if (tTd(64, 5)) 220394334Sgshapiro sm_dprintf("milter_negotiate(%s): protocol abilities 0x%x != MTA milter abilities 0x%lx\n", 220464562Sgshapiro m->mf_name, m->mf_pflags, 220590792Sgshapiro (unsigned long) SMFI_CURR_PROT); 220690792Sgshapiro if (MilterLogLevel > 0) 220764562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 220894334Sgshapiro "Milter (%s): negotiate: protocol abilities 0x%x != MTA milter abilities 0x%lx", 220964562Sgshapiro m->mf_name, m->mf_pflags, 221090792Sgshapiro (unsigned long) SMFI_CURR_PROT); 221190792Sgshapiro milter_error(m, e); 221264562Sgshapiro return -1; 221364562Sgshapiro } 221464562Sgshapiro 221564562Sgshapiro if (tTd(64, 5)) 221694334Sgshapiro sm_dprintf("milter_negotiate(%s): version %u, fflags 0x%x, pflags 0x%x\n", 221764562Sgshapiro m->mf_name, m->mf_fvers, m->mf_fflags, m->mf_pflags); 221864562Sgshapiro return 0; 221964562Sgshapiro} 222090792Sgshapiro/* 222164562Sgshapiro** MILTER_PER_CONNECTION_CHECK -- checks on per-connection commands 222264562Sgshapiro** 222364562Sgshapiro** Reduce code duplication by putting these checks in one place 222464562Sgshapiro** 222564562Sgshapiro** Parameters: 222664562Sgshapiro** e -- current envelope. 222764562Sgshapiro** 222864562Sgshapiro** Returns: 222964562Sgshapiro** none 223064562Sgshapiro*/ 223164562Sgshapiro 223264562Sgshapirostatic void 223364562Sgshapiromilter_per_connection_check(e) 223464562Sgshapiro ENVELOPE *e; 223564562Sgshapiro{ 223664562Sgshapiro int i; 223764562Sgshapiro 223864562Sgshapiro /* see if we are done with any of the filters */ 223964562Sgshapiro for (i = 0; InputFilters[i] != NULL; i++) 224064562Sgshapiro { 224164562Sgshapiro struct milter *m = InputFilters[i]; 224264562Sgshapiro 224373188Sgshapiro if (m->mf_state == SMFS_CLOSABLE) 224464562Sgshapiro milter_quit_filter(m, e); 224564562Sgshapiro } 224664562Sgshapiro} 224790792Sgshapiro/* 224864562Sgshapiro** MILTER_ERROR -- Put a milter filter into error state 224964562Sgshapiro** 225064562Sgshapiro** Parameters: 225164562Sgshapiro** m -- the broken filter. 225264562Sgshapiro** 225364562Sgshapiro** Returns: 225464562Sgshapiro** none 225564562Sgshapiro*/ 225664562Sgshapiro 225764562Sgshapirostatic void 225890792Sgshapiromilter_error(m, e) 225964562Sgshapiro struct milter *m; 226090792Sgshapiro ENVELOPE *e; 226164562Sgshapiro{ 226264562Sgshapiro /* 226364562Sgshapiro ** We could send a quit here but 226464562Sgshapiro ** we may have gotten here due to 226564562Sgshapiro ** an I/O error so we don't want 226664562Sgshapiro ** to try to make things worse. 226764562Sgshapiro */ 226864562Sgshapiro 226964562Sgshapiro if (m->mf_sock >= 0) 227064562Sgshapiro { 227164562Sgshapiro (void) close(m->mf_sock); 227264562Sgshapiro m->mf_sock = -1; 227364562Sgshapiro } 227464562Sgshapiro m->mf_state = SMFS_ERROR; 227590792Sgshapiro 227690792Sgshapiro if (MilterLogLevel > 0) 227790792Sgshapiro sm_syslog(LOG_INFO, e->e_id, "Milter (%s): to error state", 227890792Sgshapiro m->mf_name); 227964562Sgshapiro} 228090792Sgshapiro/* 228164562Sgshapiro** MILTER_HEADERS -- send headers to a single milter filter 228264562Sgshapiro** 228364562Sgshapiro** Parameters: 228464562Sgshapiro** m -- current filter. 228564562Sgshapiro** e -- current envelope. 228664562Sgshapiro** state -- return state from response. 228764562Sgshapiro** 228864562Sgshapiro** Returns: 228964562Sgshapiro** response string (may be NULL) 229064562Sgshapiro*/ 229164562Sgshapiro 229264562Sgshapirostatic char * 229364562Sgshapiromilter_headers(m, e, state) 229464562Sgshapiro struct milter *m; 229564562Sgshapiro ENVELOPE *e; 229664562Sgshapiro char *state; 229764562Sgshapiro{ 229864562Sgshapiro char *response = NULL; 229964562Sgshapiro HDR *h; 230064562Sgshapiro 230190792Sgshapiro if (MilterLogLevel > 17) 230290792Sgshapiro sm_syslog(LOG_INFO, e->e_id, "Milter (%s): headers, send", 230390792Sgshapiro m->mf_name); 230490792Sgshapiro 230564562Sgshapiro for (h = e->e_header; h != NULL; h = h->h_link) 230664562Sgshapiro { 230764562Sgshapiro char *buf; 230864562Sgshapiro ssize_t s; 230964562Sgshapiro 231064562Sgshapiro /* don't send over deleted headers */ 231164562Sgshapiro if (h->h_value == NULL) 231264562Sgshapiro { 231364562Sgshapiro /* strip H_USER so not counted in milter_chgheader() */ 231464562Sgshapiro h->h_flags &= ~H_USER; 231564562Sgshapiro continue; 231664562Sgshapiro } 231764562Sgshapiro 231864562Sgshapiro /* skip auto-generated */ 231964562Sgshapiro if (!bitset(H_USER, h->h_flags)) 232064562Sgshapiro continue; 232164562Sgshapiro 232264562Sgshapiro if (tTd(64, 10)) 232390792Sgshapiro sm_dprintf("milter_headers: %s: %s\n", 232464562Sgshapiro h->h_field, h->h_value); 232590792Sgshapiro if (MilterLogLevel > 21) 232690792Sgshapiro sm_syslog(LOG_INFO, e->e_id, "Milter (%s): header, %s", 232790792Sgshapiro m->mf_name, h->h_field); 232864562Sgshapiro 232990792Sgshapiro s = strlen(h->h_field) + 1 + strlen(h->h_value) + 1; 233090792Sgshapiro if (s < 0) 233190792Sgshapiro continue; 233264562Sgshapiro buf = (char *) xalloc(s); 233390792Sgshapiro (void) sm_snprintf(buf, s, "%s%c%s", 233490792Sgshapiro h->h_field, '\0', h->h_value); 233564562Sgshapiro 233664562Sgshapiro /* send it over */ 233764562Sgshapiro response = milter_send_command(m, SMFIC_HEADER, buf, 233864562Sgshapiro s, e, state); 233990792Sgshapiro sm_free(buf); /* XXX */ 234064562Sgshapiro if (m->mf_state == SMFS_ERROR || 234164562Sgshapiro m->mf_state == SMFS_DONE || 234264562Sgshapiro *state != SMFIR_CONTINUE) 234364562Sgshapiro break; 234464562Sgshapiro } 234590792Sgshapiro if (MilterLogLevel > 17) 234690792Sgshapiro sm_syslog(LOG_INFO, e->e_id, "Milter (%s): headers, sent", 234790792Sgshapiro m->mf_name); 234864562Sgshapiro return response; 234964562Sgshapiro} 235090792Sgshapiro/* 235164562Sgshapiro** MILTER_BODY -- send the body to a filter 235264562Sgshapiro** 235364562Sgshapiro** Parameters: 235464562Sgshapiro** m -- current filter. 235564562Sgshapiro** e -- current envelope. 235664562Sgshapiro** state -- return state from response. 235764562Sgshapiro** 235864562Sgshapiro** Returns: 235964562Sgshapiro** response string (may be NULL) 236064562Sgshapiro*/ 236164562Sgshapiro 236264562Sgshapirostatic char * 236364562Sgshapiromilter_body(m, e, state) 236464562Sgshapiro struct milter *m; 236564562Sgshapiro ENVELOPE *e; 236664562Sgshapiro char *state; 236764562Sgshapiro{ 236864562Sgshapiro char bufchar = '\0'; 236964562Sgshapiro char prevchar = '\0'; 237064562Sgshapiro int c; 237164562Sgshapiro char *response = NULL; 237264562Sgshapiro char *bp; 237364562Sgshapiro char buf[MILTER_CHUNK_SIZE]; 237464562Sgshapiro 237564562Sgshapiro if (tTd(64, 10)) 237690792Sgshapiro sm_dprintf("milter_body\n"); 237764562Sgshapiro 237864562Sgshapiro if (bfrewind(e->e_dfp) < 0) 237964562Sgshapiro { 238064562Sgshapiro ExitStat = EX_IOERR; 238164562Sgshapiro *state = SMFIR_TEMPFAIL; 238290792Sgshapiro syserr("milter_body: %s/%cf%s: rewind error", 238390792Sgshapiro qid_printqueue(e->e_qgrp, e->e_qdir), 238490792Sgshapiro DATAFL_LETTER, e->e_id); 238564562Sgshapiro return NULL; 238664562Sgshapiro } 238764562Sgshapiro 238890792Sgshapiro if (MilterLogLevel > 17) 238990792Sgshapiro sm_syslog(LOG_INFO, e->e_id, "Milter (%s): body, send", 239090792Sgshapiro m->mf_name); 239164562Sgshapiro bp = buf; 239290792Sgshapiro while ((c = sm_io_getc(e->e_dfp, SM_TIME_DEFAULT)) != SM_IO_EOF) 239364562Sgshapiro { 239464562Sgshapiro /* Change LF to CRLF */ 239564562Sgshapiro if (c == '\n') 239664562Sgshapiro { 239764562Sgshapiro /* Not a CRLF already? */ 239864562Sgshapiro if (prevchar != '\r') 239964562Sgshapiro { 240064562Sgshapiro /* Room for CR now? */ 240164562Sgshapiro if (bp + 2 > &buf[sizeof buf]) 240264562Sgshapiro { 240364562Sgshapiro /* No room, buffer LF */ 240464562Sgshapiro bufchar = c; 240564562Sgshapiro 240664562Sgshapiro /* and send CR now */ 240764562Sgshapiro c = '\r'; 240864562Sgshapiro } 240964562Sgshapiro else 241064562Sgshapiro { 241164562Sgshapiro /* Room to do it now */ 241264562Sgshapiro *bp++ = '\r'; 241364562Sgshapiro prevchar = '\r'; 241464562Sgshapiro } 241564562Sgshapiro } 241664562Sgshapiro } 241764562Sgshapiro *bp++ = (char) c; 241864562Sgshapiro prevchar = c; 241964562Sgshapiro if (bp >= &buf[sizeof buf]) 242064562Sgshapiro { 242164562Sgshapiro /* send chunk */ 242264562Sgshapiro response = milter_send_command(m, SMFIC_BODY, buf, 242364562Sgshapiro bp - buf, e, state); 242464562Sgshapiro bp = buf; 242564562Sgshapiro if (bufchar != '\0') 242664562Sgshapiro { 242764562Sgshapiro *bp++ = bufchar; 242864562Sgshapiro bufchar = '\0'; 242964562Sgshapiro prevchar = bufchar; 243064562Sgshapiro } 243164562Sgshapiro } 243264562Sgshapiro if (m->mf_state == SMFS_ERROR || 243364562Sgshapiro m->mf_state == SMFS_DONE || 243464562Sgshapiro *state != SMFIR_CONTINUE) 243564562Sgshapiro break; 243664562Sgshapiro } 243764562Sgshapiro 243864562Sgshapiro /* check for read errors */ 243990792Sgshapiro if (sm_io_error(e->e_dfp)) 244064562Sgshapiro { 244164562Sgshapiro ExitStat = EX_IOERR; 244264562Sgshapiro if (*state == SMFIR_CONTINUE || 244364562Sgshapiro *state == SMFIR_ACCEPT) 244464562Sgshapiro { 244564562Sgshapiro *state = SMFIR_TEMPFAIL; 244664562Sgshapiro if (response != NULL) 244764562Sgshapiro { 244890792Sgshapiro sm_free(response); /* XXX */ 244964562Sgshapiro response = NULL; 245064562Sgshapiro } 245164562Sgshapiro } 245290792Sgshapiro syserr("milter_body: %s/%cf%s: read error", 245390792Sgshapiro qid_printqueue(e->e_qgrp, e->e_qdir), 245490792Sgshapiro DATAFL_LETTER, e->e_id); 245564562Sgshapiro return response; 245664562Sgshapiro } 245764562Sgshapiro 245864562Sgshapiro /* send last body chunk */ 245964562Sgshapiro if (bp > buf && 246064562Sgshapiro m->mf_state != SMFS_ERROR && 246164562Sgshapiro m->mf_state != SMFS_DONE && 246264562Sgshapiro *state == SMFIR_CONTINUE) 246364562Sgshapiro { 246464562Sgshapiro /* send chunk */ 246564562Sgshapiro response = milter_send_command(m, SMFIC_BODY, buf, bp - buf, 246664562Sgshapiro e, state); 246764562Sgshapiro bp = buf; 246864562Sgshapiro } 246990792Sgshapiro if (MilterLogLevel > 17) 247090792Sgshapiro sm_syslog(LOG_INFO, e->e_id, "Milter (%s): body, sent", 247190792Sgshapiro m->mf_name); 247264562Sgshapiro return response; 247364562Sgshapiro} 247464562Sgshapiro 247564562Sgshapiro/* 247664562Sgshapiro** Actions 247764562Sgshapiro*/ 247864562Sgshapiro 247990792Sgshapiro/* 248064562Sgshapiro** MILTER_ADDHEADER -- Add the supplied header to the message 248164562Sgshapiro** 248264562Sgshapiro** Parameters: 248364562Sgshapiro** response -- encoded form of header/value. 248464562Sgshapiro** rlen -- length of response. 248564562Sgshapiro** e -- current envelope. 248664562Sgshapiro** 248764562Sgshapiro** Returns: 248864562Sgshapiro** none 248964562Sgshapiro*/ 249064562Sgshapiro 249164562Sgshapirostatic void 249264562Sgshapiromilter_addheader(response, rlen, e) 249364562Sgshapiro char *response; 249464562Sgshapiro ssize_t rlen; 249564562Sgshapiro ENVELOPE *e; 249664562Sgshapiro{ 249764562Sgshapiro char *val; 249871345Sgshapiro HDR *h; 249964562Sgshapiro 250064562Sgshapiro if (tTd(64, 10)) 250190792Sgshapiro sm_dprintf("milter_addheader: "); 250264562Sgshapiro 250364562Sgshapiro /* sanity checks */ 250464562Sgshapiro if (response == NULL) 250564562Sgshapiro { 250664562Sgshapiro if (tTd(64, 10)) 250790792Sgshapiro sm_dprintf("NULL response\n"); 250864562Sgshapiro return; 250964562Sgshapiro } 251064562Sgshapiro 251164562Sgshapiro if (rlen < 2 || strlen(response) + 1 >= (size_t) rlen) 251264562Sgshapiro { 251364562Sgshapiro if (tTd(64, 10)) 251490792Sgshapiro sm_dprintf("didn't follow protocol (total len)\n"); 251564562Sgshapiro return; 251664562Sgshapiro } 251764562Sgshapiro 251864562Sgshapiro /* Find separating NUL */ 251964562Sgshapiro val = response + strlen(response) + 1; 252064562Sgshapiro 252164562Sgshapiro /* another sanity check */ 252264562Sgshapiro if (strlen(response) + strlen(val) + 2 != (size_t) rlen) 252364562Sgshapiro { 252464562Sgshapiro if (tTd(64, 10)) 252590792Sgshapiro sm_dprintf("didn't follow protocol (part len)\n"); 252664562Sgshapiro return; 252764562Sgshapiro } 252864562Sgshapiro 252964562Sgshapiro if (*response == '\0') 253064562Sgshapiro { 253164562Sgshapiro if (tTd(64, 10)) 253290792Sgshapiro sm_dprintf("empty field name\n"); 253364562Sgshapiro return; 253464562Sgshapiro } 253564562Sgshapiro 253671345Sgshapiro for (h = e->e_header; h != NULL; h = h->h_link) 253771345Sgshapiro { 253890792Sgshapiro if (sm_strcasecmp(h->h_field, response) == 0 && 253971345Sgshapiro !bitset(H_USER, h->h_flags) && 254071345Sgshapiro !bitset(H_TRACE, h->h_flags)) 254171345Sgshapiro break; 254271345Sgshapiro } 254371345Sgshapiro 254464562Sgshapiro /* add to e_msgsize */ 254564562Sgshapiro e->e_msgsize += strlen(response) + 2 + strlen(val); 254664562Sgshapiro 254771345Sgshapiro if (h != NULL) 254871345Sgshapiro { 254971345Sgshapiro if (tTd(64, 10)) 255090792Sgshapiro sm_dprintf("Replace default header %s value with %s\n", 255190792Sgshapiro h->h_field, val); 255290792Sgshapiro if (MilterLogLevel > 8) 255390792Sgshapiro sm_syslog(LOG_INFO, e->e_id, 255490792Sgshapiro "Milter change: default header %s value with %s", 255590792Sgshapiro h->h_field, val); 255671345Sgshapiro h->h_value = newstr(val); 255771345Sgshapiro h->h_flags |= H_USER; 255871345Sgshapiro } 255971345Sgshapiro else 256071345Sgshapiro { 256171345Sgshapiro if (tTd(64, 10)) 256290792Sgshapiro sm_dprintf("Add %s: %s\n", response, val); 256390792Sgshapiro if (MilterLogLevel > 8) 256490792Sgshapiro sm_syslog(LOG_INFO, e->e_id, "Milter add: header: %s: %s", 256590792Sgshapiro response, val); 256690792Sgshapiro addheader(newstr(response), val, H_USER, e); 256771345Sgshapiro } 256864562Sgshapiro} 256990792Sgshapiro/* 257064562Sgshapiro** MILTER_CHANGEHEADER -- Change the supplied header in the message 257164562Sgshapiro** 257264562Sgshapiro** Parameters: 257364562Sgshapiro** response -- encoded form of header/index/value. 257464562Sgshapiro** rlen -- length of response. 257564562Sgshapiro** e -- current envelope. 257664562Sgshapiro** 257764562Sgshapiro** Returns: 257864562Sgshapiro** none 257964562Sgshapiro*/ 258064562Sgshapiro 258164562Sgshapirostatic void 258264562Sgshapiromilter_changeheader(response, rlen, e) 258364562Sgshapiro char *response; 258464562Sgshapiro ssize_t rlen; 258564562Sgshapiro ENVELOPE *e; 258664562Sgshapiro{ 258764562Sgshapiro mi_int32 i, index; 258864562Sgshapiro char *field, *val; 258971345Sgshapiro HDR *h, *sysheader; 259064562Sgshapiro 259164562Sgshapiro if (tTd(64, 10)) 259290792Sgshapiro sm_dprintf("milter_changeheader: "); 259364562Sgshapiro 259464562Sgshapiro /* sanity checks */ 259564562Sgshapiro if (response == NULL) 259664562Sgshapiro { 259764562Sgshapiro if (tTd(64, 10)) 259890792Sgshapiro sm_dprintf("NULL response\n"); 259964562Sgshapiro return; 260064562Sgshapiro } 260164562Sgshapiro 260264562Sgshapiro if (rlen < 2 || strlen(response) + 1 >= (size_t) rlen) 260364562Sgshapiro { 260464562Sgshapiro if (tTd(64, 10)) 260590792Sgshapiro sm_dprintf("didn't follow protocol (total len)\n"); 260664562Sgshapiro return; 260764562Sgshapiro } 260864562Sgshapiro 260964562Sgshapiro /* Find separating NUL */ 261064562Sgshapiro (void) memcpy((char *) &i, response, MILTER_LEN_BYTES); 261164562Sgshapiro index = ntohl(i); 261264562Sgshapiro field = response + MILTER_LEN_BYTES; 261364562Sgshapiro val = field + strlen(field) + 1; 261464562Sgshapiro 261564562Sgshapiro /* another sanity check */ 261664562Sgshapiro if (MILTER_LEN_BYTES + strlen(field) + 1 + 261764562Sgshapiro strlen(val) + 1 != (size_t) rlen) 261864562Sgshapiro { 261964562Sgshapiro if (tTd(64, 10)) 262090792Sgshapiro sm_dprintf("didn't follow protocol (part len)\n"); 262164562Sgshapiro return; 262264562Sgshapiro } 262364562Sgshapiro 262464562Sgshapiro if (*field == '\0') 262564562Sgshapiro { 262664562Sgshapiro if (tTd(64, 10)) 262790792Sgshapiro sm_dprintf("empty field name\n"); 262864562Sgshapiro return; 262964562Sgshapiro } 263064562Sgshapiro 263171345Sgshapiro sysheader = NULL; 263264562Sgshapiro for (h = e->e_header; h != NULL; h = h->h_link) 263364562Sgshapiro { 263490792Sgshapiro if (sm_strcasecmp(h->h_field, field) == 0) 263571345Sgshapiro { 263671345Sgshapiro if (bitset(H_USER, h->h_flags) && 263771345Sgshapiro --index <= 0) 263871345Sgshapiro { 263971345Sgshapiro sysheader = NULL; 264071345Sgshapiro break; 264171345Sgshapiro } 264271345Sgshapiro else if (!bitset(H_USER, h->h_flags) && 264371345Sgshapiro !bitset(H_TRACE, h->h_flags)) 264471345Sgshapiro { 264571345Sgshapiro /* 264671345Sgshapiro ** DRUMS msg-fmt draft says can only have 264771345Sgshapiro ** multiple occurences of trace fields, 264871345Sgshapiro ** so make sure we replace any non-trace, 264971345Sgshapiro ** non-user field. 265071345Sgshapiro */ 265171345Sgshapiro 265271345Sgshapiro sysheader = h; 265371345Sgshapiro } 265471345Sgshapiro } 265564562Sgshapiro } 265664562Sgshapiro 265771345Sgshapiro /* if not found as user-provided header at index, use sysheader */ 265864562Sgshapiro if (h == NULL) 265971345Sgshapiro h = sysheader; 266071345Sgshapiro 266171345Sgshapiro if (h == NULL) 266264562Sgshapiro { 266364562Sgshapiro if (*val == '\0') 266464562Sgshapiro { 266564562Sgshapiro if (tTd(64, 10)) 266690792Sgshapiro sm_dprintf("Delete (noop) %s:\n", field); 266764562Sgshapiro } 266864562Sgshapiro else 266964562Sgshapiro { 267064562Sgshapiro /* treat modify value with no existing header as add */ 267164562Sgshapiro if (tTd(64, 10)) 267290792Sgshapiro sm_dprintf("Add %s: %s\n", field, val); 267390792Sgshapiro addheader(newstr(field), val, H_USER, e); 267464562Sgshapiro } 267564562Sgshapiro return; 267664562Sgshapiro } 267764562Sgshapiro 267864562Sgshapiro if (tTd(64, 10)) 267964562Sgshapiro { 268064562Sgshapiro if (*val == '\0') 268164562Sgshapiro { 268290792Sgshapiro sm_dprintf("Delete%s %s: %s\n", 268390792Sgshapiro h == sysheader ? " (default header)" : "", 268490792Sgshapiro field, 268590792Sgshapiro h->h_value == NULL ? "<NULL>" : h->h_value); 268664562Sgshapiro } 268764562Sgshapiro else 268864562Sgshapiro { 268990792Sgshapiro sm_dprintf("Change%s %s: from %s to %s\n", 269090792Sgshapiro h == sysheader ? " (default header)" : "", 269190792Sgshapiro field, 269290792Sgshapiro h->h_value == NULL ? "<NULL>" : h->h_value, 269390792Sgshapiro val); 269464562Sgshapiro } 269564562Sgshapiro } 269664562Sgshapiro 269790792Sgshapiro if (MilterLogLevel > 8) 269890792Sgshapiro { 269990792Sgshapiro if (*val == '\0') 270090792Sgshapiro { 270190792Sgshapiro sm_syslog(LOG_INFO, e->e_id, 270290792Sgshapiro "Milter delete: header %s %s: %s", 270390792Sgshapiro h == sysheader ? " (default header)" : "", 270490792Sgshapiro field, 270590792Sgshapiro h->h_value == NULL ? "<NULL>" : h->h_value); 270690792Sgshapiro } 270790792Sgshapiro else 270890792Sgshapiro { 270990792Sgshapiro sm_syslog(LOG_INFO, e->e_id, 271090792Sgshapiro "Milter change: header %s %s: from %s to %s", 271190792Sgshapiro h == sysheader ? " (default header)" : "", 271290792Sgshapiro field, 271390792Sgshapiro h->h_value == NULL ? "<NULL>" : h->h_value, 271490792Sgshapiro val); 271590792Sgshapiro } 271690792Sgshapiro } 271790792Sgshapiro 271871345Sgshapiro if (h != sysheader && h->h_value != NULL) 271964562Sgshapiro { 272090792Sgshapiro size_t l; 272190792Sgshapiro 272290792Sgshapiro l = strlen(h->h_value); 272390792Sgshapiro if (l > e->e_msgsize) 272490792Sgshapiro e->e_msgsize = 0; 272590792Sgshapiro else 272690792Sgshapiro e->e_msgsize -= l; 272790792Sgshapiro /* rpool, don't free: sm_free(h->h_value); XXX */ 272864562Sgshapiro } 272964562Sgshapiro 273064562Sgshapiro if (*val == '\0') 273164562Sgshapiro { 273264562Sgshapiro /* Remove "Field: " from message size */ 273371345Sgshapiro if (h != sysheader) 273490792Sgshapiro { 273590792Sgshapiro size_t l; 273690792Sgshapiro 273790792Sgshapiro l = strlen(h->h_field) + 2; 273890792Sgshapiro if (l > e->e_msgsize) 273990792Sgshapiro e->e_msgsize = 0; 274090792Sgshapiro else 274190792Sgshapiro e->e_msgsize -= l; 274290792Sgshapiro } 274364562Sgshapiro h->h_value = NULL; 274464562Sgshapiro } 274564562Sgshapiro else 274664562Sgshapiro { 274764562Sgshapiro h->h_value = newstr(val); 274871345Sgshapiro h->h_flags |= H_USER; 274964562Sgshapiro e->e_msgsize += strlen(h->h_value); 275064562Sgshapiro } 275164562Sgshapiro} 275290792Sgshapiro/* 275364562Sgshapiro** MILTER_ADDRCPT -- Add the supplied recipient to the message 275464562Sgshapiro** 275564562Sgshapiro** Parameters: 275664562Sgshapiro** response -- encoded form of recipient address. 275764562Sgshapiro** rlen -- length of response. 275864562Sgshapiro** e -- current envelope. 275964562Sgshapiro** 276064562Sgshapiro** Returns: 276164562Sgshapiro** none 276264562Sgshapiro*/ 276364562Sgshapiro 276464562Sgshapirostatic void 276564562Sgshapiromilter_addrcpt(response, rlen, e) 276664562Sgshapiro char *response; 276764562Sgshapiro ssize_t rlen; 276864562Sgshapiro ENVELOPE *e; 276964562Sgshapiro{ 277064562Sgshapiro if (tTd(64, 10)) 277190792Sgshapiro sm_dprintf("milter_addrcpt: "); 277264562Sgshapiro 277364562Sgshapiro /* sanity checks */ 277464562Sgshapiro if (response == NULL) 277564562Sgshapiro { 277664562Sgshapiro if (tTd(64, 10)) 277790792Sgshapiro sm_dprintf("NULL response\n"); 277864562Sgshapiro return; 277964562Sgshapiro } 278064562Sgshapiro 278164562Sgshapiro if (*response == '\0' || 278264562Sgshapiro strlen(response) + 1 != (size_t) rlen) 278364562Sgshapiro { 278464562Sgshapiro if (tTd(64, 10)) 278590792Sgshapiro sm_dprintf("didn't follow protocol (total len %d != rlen %d)\n", 278690792Sgshapiro (int) strlen(response), (int) (rlen - 1)); 278764562Sgshapiro return; 278864562Sgshapiro } 278964562Sgshapiro 279064562Sgshapiro if (tTd(64, 10)) 279190792Sgshapiro sm_dprintf("%s\n", response); 279290792Sgshapiro if (MilterLogLevel > 8) 279390792Sgshapiro sm_syslog(LOG_INFO, e->e_id, "Milter add: rcpt: %s", response); 279464562Sgshapiro (void) sendtolist(response, NULLADDR, &e->e_sendqueue, 0, e); 279564562Sgshapiro return; 279664562Sgshapiro} 279790792Sgshapiro/* 279864562Sgshapiro** MILTER_DELRCPT -- Delete the supplied recipient from the message 279964562Sgshapiro** 280064562Sgshapiro** Parameters: 280164562Sgshapiro** response -- encoded form of recipient address. 280264562Sgshapiro** rlen -- length of response. 280364562Sgshapiro** e -- current envelope. 280464562Sgshapiro** 280564562Sgshapiro** Returns: 280664562Sgshapiro** none 280764562Sgshapiro*/ 280864562Sgshapiro 280964562Sgshapirostatic void 281064562Sgshapiromilter_delrcpt(response, rlen, e) 281164562Sgshapiro char *response; 281264562Sgshapiro ssize_t rlen; 281364562Sgshapiro ENVELOPE *e; 281464562Sgshapiro{ 281564562Sgshapiro if (tTd(64, 10)) 281690792Sgshapiro sm_dprintf("milter_delrcpt: "); 281764562Sgshapiro 281864562Sgshapiro /* sanity checks */ 281964562Sgshapiro if (response == NULL) 282064562Sgshapiro { 282164562Sgshapiro if (tTd(64, 10)) 282290792Sgshapiro sm_dprintf("NULL response\n"); 282364562Sgshapiro return; 282464562Sgshapiro } 282564562Sgshapiro 282664562Sgshapiro if (*response == '\0' || 282764562Sgshapiro strlen(response) + 1 != (size_t) rlen) 282864562Sgshapiro { 282964562Sgshapiro if (tTd(64, 10)) 283090792Sgshapiro sm_dprintf("didn't follow protocol (total len)\n"); 283164562Sgshapiro return; 283264562Sgshapiro } 283364562Sgshapiro 283464562Sgshapiro if (tTd(64, 10)) 283590792Sgshapiro sm_dprintf("%s\n", response); 283690792Sgshapiro if (MilterLogLevel > 8) 283790792Sgshapiro sm_syslog(LOG_INFO, e->e_id, "Milter delete: rcpt %s", 283890792Sgshapiro response); 283964562Sgshapiro (void) removefromlist(response, &e->e_sendqueue, e); 284064562Sgshapiro return; 284164562Sgshapiro} 284290792Sgshapiro/* 284390792Sgshapiro** MILTER_REPLBODY -- Replace the current data file with new body 284464562Sgshapiro** 284564562Sgshapiro** Parameters: 284664562Sgshapiro** response -- encoded form of new body. 284764562Sgshapiro** rlen -- length of response. 284864562Sgshapiro** newfilter -- if first time called by a new filter 284964562Sgshapiro** e -- current envelope. 285064562Sgshapiro** 285164562Sgshapiro** Returns: 285264562Sgshapiro** 0 upon success, -1 upon failure 285364562Sgshapiro*/ 285464562Sgshapiro 285564562Sgshapirostatic int 285664562Sgshapiromilter_replbody(response, rlen, newfilter, e) 285764562Sgshapiro char *response; 285864562Sgshapiro ssize_t rlen; 285964562Sgshapiro bool newfilter; 286064562Sgshapiro ENVELOPE *e; 286164562Sgshapiro{ 286264562Sgshapiro static char prevchar; 286364562Sgshapiro int i; 286464562Sgshapiro 286564562Sgshapiro if (tTd(64, 10)) 286690792Sgshapiro sm_dprintf("milter_replbody\n"); 286764562Sgshapiro 286890792Sgshapiro /* If a new filter, reset previous character and truncate data file */ 286964562Sgshapiro if (newfilter) 287064562Sgshapiro { 287194334Sgshapiro off_t prevsize; 287264562Sgshapiro char dfname[MAXPATHLEN]; 287364562Sgshapiro 287490792Sgshapiro (void) sm_strlcpy(dfname, queuename(e, DATAFL_LETTER), 287590792Sgshapiro sizeof dfname); 287664562Sgshapiro 287764562Sgshapiro /* Reset prevchar */ 287864562Sgshapiro prevchar = '\0'; 287964562Sgshapiro 288090792Sgshapiro /* Get the current data file information */ 288194334Sgshapiro prevsize = sm_io_getinfo(e->e_dfp, SM_IO_WHAT_SIZE, NULL); 288294334Sgshapiro if (prevsize < 0) 288394334Sgshapiro prevsize = 0; 288464562Sgshapiro 288590792Sgshapiro /* truncate current data file */ 288690792Sgshapiro if (sm_io_getinfo(e->e_dfp, SM_IO_WHAT_ISTYPE, BF_FILE_TYPE)) 288764562Sgshapiro { 288890792Sgshapiro if (sm_io_setinfo(e->e_dfp, SM_BF_TRUNCATE, NULL) < 0) 288990792Sgshapiro { 289090792Sgshapiro MILTER_DF_ERROR("milter_replbody: sm_io truncate %s: %s"); 289190792Sgshapiro return -1; 289290792Sgshapiro } 289364562Sgshapiro } 289464562Sgshapiro else 289564562Sgshapiro { 289690792Sgshapiro int err; 289790792Sgshapiro 289890792Sgshapiro# if NOFTRUNCATE 289990792Sgshapiro /* XXX: Not much we can do except rewind it */ 290090792Sgshapiro err = sm_io_error(e->e_dfp); 290190792Sgshapiro (void) sm_io_flush(e->e_dfp, SM_TIME_DEFAULT); 290290792Sgshapiro 290390792Sgshapiro /* 290490792Sgshapiro ** Clear error if tried to fflush() 290590792Sgshapiro ** a read-only file pointer and 290690792Sgshapiro ** there wasn't a previous error. 290790792Sgshapiro */ 290890792Sgshapiro 290990792Sgshapiro if (err == 0) 291090792Sgshapiro sm_io_clearerr(e->e_dfp); 291190792Sgshapiro 291290792Sgshapiro /* errno is set implicitly by fseek() before return */ 291390792Sgshapiro err = sm_io_seek(e->e_dfp, SM_TIME_DEFAULT, 291490792Sgshapiro 0, SEEK_SET); 291590792Sgshapiro# else /* NOFTRUNCATE */ 291690792Sgshapiro err = ftruncate(sm_io_getinfo(e->e_dfp, 291790792Sgshapiro SM_IO_WHAT_FD, NULL), 291890792Sgshapiro 0); 291990792Sgshapiro# endif /* NOFTRUNCATE */ 292090792Sgshapiro if (err < 0) 292190792Sgshapiro { 292290792Sgshapiro MILTER_DF_ERROR("milter_replbody: sm_io ftruncate %s: %s"); 292390792Sgshapiro return -1; 292490792Sgshapiro } 292564562Sgshapiro } 292690792Sgshapiro 292790792Sgshapiro if (prevsize > e->e_msgsize) 292890792Sgshapiro e->e_msgsize = 0; 292990792Sgshapiro else 293090792Sgshapiro e->e_msgsize -= prevsize; 293164562Sgshapiro } 293264562Sgshapiro 293390792Sgshapiro if (newfilter && MilterLogLevel > 8) 293490792Sgshapiro sm_syslog(LOG_INFO, e->e_id, "Milter message: body replaced"); 293590792Sgshapiro 293664562Sgshapiro if (response == NULL) 293764562Sgshapiro { 293864562Sgshapiro /* Flush the buffered '\r' */ 293964562Sgshapiro if (prevchar == '\r') 294064562Sgshapiro { 294190792Sgshapiro (void) sm_io_putc(e->e_dfp, SM_TIME_DEFAULT, prevchar); 294264562Sgshapiro e->e_msgsize++; 294364562Sgshapiro } 294464562Sgshapiro return 0; 294564562Sgshapiro } 294664562Sgshapiro 294764562Sgshapiro for (i = 0; i < rlen; i++) 294864562Sgshapiro { 294964562Sgshapiro /* Buffered char from last chunk */ 295064562Sgshapiro if (i == 0 && prevchar == '\r') 295164562Sgshapiro { 295264562Sgshapiro /* Not CRLF, output prevchar */ 295364562Sgshapiro if (response[i] != '\n') 295464562Sgshapiro { 295590792Sgshapiro (void) sm_io_putc(e->e_dfp, SM_TIME_DEFAULT, 295690792Sgshapiro prevchar); 295764562Sgshapiro e->e_msgsize++; 295864562Sgshapiro } 295964562Sgshapiro prevchar = '\0'; 296064562Sgshapiro } 296164562Sgshapiro 296264562Sgshapiro /* Turn CRLF into LF */ 296364562Sgshapiro if (response[i] == '\r') 296464562Sgshapiro { 296564562Sgshapiro /* check if at end of chunk */ 296664562Sgshapiro if (i + 1 < rlen) 296764562Sgshapiro { 296864562Sgshapiro /* If LF, strip CR */ 296964562Sgshapiro if (response[i + 1] == '\n') 297064562Sgshapiro i++; 297164562Sgshapiro } 297264562Sgshapiro else 297364562Sgshapiro { 297464562Sgshapiro /* check next chunk */ 297564562Sgshapiro prevchar = '\r'; 297664562Sgshapiro continue; 297764562Sgshapiro } 297864562Sgshapiro } 297990792Sgshapiro (void) sm_io_putc(e->e_dfp, SM_TIME_DEFAULT, response[i]); 298064562Sgshapiro e->e_msgsize++; 298164562Sgshapiro } 298264562Sgshapiro return 0; 298364562Sgshapiro} 298464562Sgshapiro 298564562Sgshapiro/* 298664562Sgshapiro** MTA callouts 298764562Sgshapiro*/ 298864562Sgshapiro 298990792Sgshapiro/* 299064562Sgshapiro** MILTER_INIT -- open and negotiate with all of the filters 299164562Sgshapiro** 299264562Sgshapiro** Parameters: 299364562Sgshapiro** e -- current envelope. 299464562Sgshapiro** state -- return state from response. 299564562Sgshapiro** 299664562Sgshapiro** Returns: 299790792Sgshapiro** true iff at least one filter is active 299864562Sgshapiro*/ 299964562Sgshapiro 300064562Sgshapiro/* ARGSUSED */ 300190792Sgshapirobool 300264562Sgshapiromilter_init(e, state) 300364562Sgshapiro ENVELOPE *e; 300464562Sgshapiro char *state; 300564562Sgshapiro{ 300664562Sgshapiro int i; 300764562Sgshapiro 300864562Sgshapiro if (tTd(64, 10)) 300990792Sgshapiro sm_dprintf("milter_init\n"); 301064562Sgshapiro 301164562Sgshapiro *state = SMFIR_CONTINUE; 301290792Sgshapiro if (InputFilters[0] == NULL) 301390792Sgshapiro { 301490792Sgshapiro if (MilterLogLevel > 10) 301590792Sgshapiro sm_syslog(LOG_INFO, e->e_id, 301690792Sgshapiro "Milter: no active filter"); 301790792Sgshapiro return false; 301890792Sgshapiro } 301990792Sgshapiro 302064562Sgshapiro for (i = 0; InputFilters[i] != NULL; i++) 302164562Sgshapiro { 302264562Sgshapiro struct milter *m = InputFilters[i]; 302364562Sgshapiro 302490792Sgshapiro m->mf_sock = milter_open(m, false, e); 302564562Sgshapiro if (m->mf_state == SMFS_ERROR) 302664562Sgshapiro { 302764562Sgshapiro MILTER_CHECK_ERROR(continue); 302864562Sgshapiro break; 302964562Sgshapiro } 303064562Sgshapiro 303164562Sgshapiro if (m->mf_sock < 0 || 303264562Sgshapiro milter_negotiate(m, e) < 0 || 303364562Sgshapiro m->mf_state == SMFS_ERROR) 303464562Sgshapiro { 303564562Sgshapiro if (tTd(64, 5)) 303690792Sgshapiro sm_dprintf("milter_init(%s): failed to %s\n", 303790792Sgshapiro m->mf_name, 303890792Sgshapiro m->mf_sock < 0 ? "open" : 303990792Sgshapiro "negotiate"); 304090792Sgshapiro if (MilterLogLevel > 0) 304190792Sgshapiro sm_syslog(LOG_ERR, e->e_id, 304290792Sgshapiro "Milter (%s): init failed to %s", 304390792Sgshapiro m->mf_name, 304490792Sgshapiro m->mf_sock < 0 ? "open" : 304590792Sgshapiro "negotiate"); 304664562Sgshapiro 304764562Sgshapiro /* if negotation failure, close socket */ 304890792Sgshapiro milter_error(m, e); 304971345Sgshapiro MILTER_CHECK_ERROR(continue); 305064562Sgshapiro } 305190792Sgshapiro if (MilterLogLevel > 9) 305290792Sgshapiro sm_syslog(LOG_INFO, e->e_id, 305390792Sgshapiro "Milter (%s): init success to %s", 305490792Sgshapiro m->mf_name, 305590792Sgshapiro m->mf_sock < 0 ? "open" : "negotiate"); 305664562Sgshapiro } 305764562Sgshapiro 305864562Sgshapiro /* 305964562Sgshapiro ** If something temp/perm failed with one of the filters, 306064562Sgshapiro ** we won't be using any of them, so clear any existing 306164562Sgshapiro ** connections. 306264562Sgshapiro */ 306364562Sgshapiro 306464562Sgshapiro if (*state != SMFIR_CONTINUE) 306564562Sgshapiro milter_quit(e); 306690792Sgshapiro 306790792Sgshapiro return true; 306864562Sgshapiro} 306990792Sgshapiro/* 307064562Sgshapiro** MILTER_CONNECT -- send connection info to milter filters 307164562Sgshapiro** 307264562Sgshapiro** Parameters: 307364562Sgshapiro** hostname -- hostname of remote machine. 307464562Sgshapiro** addr -- address of remote machine. 307564562Sgshapiro** e -- current envelope. 307664562Sgshapiro** state -- return state from response. 307764562Sgshapiro** 307864562Sgshapiro** Returns: 307964562Sgshapiro** response string (may be NULL) 308064562Sgshapiro*/ 308164562Sgshapiro 308264562Sgshapirochar * 308364562Sgshapiromilter_connect(hostname, addr, e, state) 308464562Sgshapiro char *hostname; 308564562Sgshapiro SOCKADDR addr; 308664562Sgshapiro ENVELOPE *e; 308764562Sgshapiro char *state; 308864562Sgshapiro{ 308964562Sgshapiro char family; 309090792Sgshapiro unsigned short port; 309164562Sgshapiro char *buf, *bp; 309264562Sgshapiro char *response; 309364562Sgshapiro char *sockinfo = NULL; 309464562Sgshapiro ssize_t s; 309564562Sgshapiro# if NETINET6 309664562Sgshapiro char buf6[INET6_ADDRSTRLEN]; 309764562Sgshapiro# endif /* NETINET6 */ 309864562Sgshapiro 309964562Sgshapiro if (tTd(64, 10)) 310090792Sgshapiro sm_dprintf("milter_connect(%s)\n", hostname); 310190792Sgshapiro if (MilterLogLevel > 9) 310290792Sgshapiro sm_syslog(LOG_INFO, e->e_id, "Milter: connect to filters"); 310364562Sgshapiro 310464562Sgshapiro /* gather data */ 310564562Sgshapiro switch (addr.sa.sa_family) 310664562Sgshapiro { 310764562Sgshapiro# if NETUNIX 310864562Sgshapiro case AF_UNIX: 310964562Sgshapiro family = SMFIA_UNIX; 311064562Sgshapiro port = htons(0); 311164562Sgshapiro sockinfo = addr.sunix.sun_path; 311264562Sgshapiro break; 311364562Sgshapiro# endif /* NETUNIX */ 311464562Sgshapiro 311564562Sgshapiro# if NETINET 311664562Sgshapiro case AF_INET: 311764562Sgshapiro family = SMFIA_INET; 311894334Sgshapiro port = addr.sin.sin_port; 311964562Sgshapiro sockinfo = (char *) inet_ntoa(addr.sin.sin_addr); 312064562Sgshapiro break; 312164562Sgshapiro# endif /* NETINET */ 312264562Sgshapiro 312364562Sgshapiro# if NETINET6 312464562Sgshapiro case AF_INET6: 312580785Sgshapiro if (IN6_IS_ADDR_V4MAPPED(&addr.sin6.sin6_addr)) 312680785Sgshapiro family = SMFIA_INET; 312780785Sgshapiro else 312880785Sgshapiro family = SMFIA_INET6; 312994334Sgshapiro port = addr.sin6.sin6_port; 313064562Sgshapiro sockinfo = anynet_ntop(&addr.sin6.sin6_addr, buf6, 313164562Sgshapiro sizeof buf6); 313264562Sgshapiro if (sockinfo == NULL) 313364562Sgshapiro sockinfo = ""; 313464562Sgshapiro break; 313564562Sgshapiro# endif /* NETINET6 */ 313664562Sgshapiro 313764562Sgshapiro default: 313864562Sgshapiro family = SMFIA_UNKNOWN; 313964562Sgshapiro break; 314064562Sgshapiro } 314164562Sgshapiro 314264562Sgshapiro s = strlen(hostname) + 1 + sizeof(family); 314364562Sgshapiro if (family != SMFIA_UNKNOWN) 314464562Sgshapiro s += sizeof(port) + strlen(sockinfo) + 1; 314564562Sgshapiro 314690792Sgshapiro buf = (char *) xalloc(s); 314764562Sgshapiro bp = buf; 314864562Sgshapiro 314964562Sgshapiro /* put together data */ 315064562Sgshapiro (void) memcpy(bp, hostname, strlen(hostname)); 315164562Sgshapiro bp += strlen(hostname); 315264562Sgshapiro *bp++ = '\0'; 315364562Sgshapiro (void) memcpy(bp, &family, sizeof family); 315464562Sgshapiro bp += sizeof family; 315564562Sgshapiro if (family != SMFIA_UNKNOWN) 315664562Sgshapiro { 315764562Sgshapiro (void) memcpy(bp, &port, sizeof port); 315864562Sgshapiro bp += sizeof port; 315964562Sgshapiro 316064562Sgshapiro /* include trailing '\0' */ 316164562Sgshapiro (void) memcpy(bp, sockinfo, strlen(sockinfo) + 1); 316264562Sgshapiro } 316364562Sgshapiro 316464562Sgshapiro response = milter_command(SMFIC_CONNECT, buf, s, 316564562Sgshapiro MilterConnectMacros, e, state); 316690792Sgshapiro sm_free(buf); /* XXX */ 316764562Sgshapiro 316864562Sgshapiro /* 316964562Sgshapiro ** If this message connection is done for, 317064562Sgshapiro ** close the filters. 317164562Sgshapiro */ 317264562Sgshapiro 317364562Sgshapiro if (*state != SMFIR_CONTINUE) 317490792Sgshapiro { 317590792Sgshapiro if (MilterLogLevel > 9) 317690792Sgshapiro sm_syslog(LOG_INFO, e->e_id, "Milter: connect, ending"); 317764562Sgshapiro milter_quit(e); 317890792Sgshapiro } 317964562Sgshapiro else 318064562Sgshapiro milter_per_connection_check(e); 318164562Sgshapiro 318264562Sgshapiro /* 318364562Sgshapiro ** SMFIR_REPLYCODE can't work with connect due to 318464562Sgshapiro ** the requirements of SMTP. Therefore, ignore the 318564562Sgshapiro ** reply code text but keep the state it would reflect. 318664562Sgshapiro */ 318764562Sgshapiro 318864562Sgshapiro if (*state == SMFIR_REPLYCODE) 318964562Sgshapiro { 319064562Sgshapiro if (response != NULL && 319164562Sgshapiro *response == '4') 319264562Sgshapiro *state = SMFIR_TEMPFAIL; 319364562Sgshapiro else 319464562Sgshapiro *state = SMFIR_REJECT; 319564562Sgshapiro if (response != NULL) 319664562Sgshapiro { 319790792Sgshapiro sm_free(response); /* XXX */ 319864562Sgshapiro response = NULL; 319964562Sgshapiro } 320064562Sgshapiro } 320164562Sgshapiro return response; 320264562Sgshapiro} 320390792Sgshapiro/* 320464562Sgshapiro** MILTER_HELO -- send SMTP HELO/EHLO command info to milter filters 320564562Sgshapiro** 320664562Sgshapiro** Parameters: 320764562Sgshapiro** helo -- argument to SMTP HELO/EHLO command. 320864562Sgshapiro** e -- current envelope. 320964562Sgshapiro** state -- return state from response. 321064562Sgshapiro** 321164562Sgshapiro** Returns: 321264562Sgshapiro** response string (may be NULL) 321364562Sgshapiro*/ 321464562Sgshapiro 321564562Sgshapirochar * 321664562Sgshapiromilter_helo(helo, e, state) 321764562Sgshapiro char *helo; 321864562Sgshapiro ENVELOPE *e; 321964562Sgshapiro char *state; 322064562Sgshapiro{ 322173188Sgshapiro int i; 322264562Sgshapiro char *response; 322364562Sgshapiro 322464562Sgshapiro if (tTd(64, 10)) 322590792Sgshapiro sm_dprintf("milter_helo(%s)\n", helo); 322664562Sgshapiro 322790792Sgshapiro /* HELO/EHLO can come at any point */ 322873188Sgshapiro for (i = 0; InputFilters[i] != NULL; i++) 322973188Sgshapiro { 323073188Sgshapiro struct milter *m = InputFilters[i]; 323173188Sgshapiro 323273188Sgshapiro switch (m->mf_state) 323373188Sgshapiro { 323473188Sgshapiro case SMFS_INMSG: 323573188Sgshapiro /* abort in message filters */ 323673188Sgshapiro milter_abort_filter(m, e); 323773188Sgshapiro /* FALLTHROUGH */ 323873188Sgshapiro 323973188Sgshapiro case SMFS_DONE: 324073188Sgshapiro /* reset done filters */ 324173188Sgshapiro m->mf_state = SMFS_OPEN; 324273188Sgshapiro break; 324373188Sgshapiro } 324473188Sgshapiro } 324573188Sgshapiro 324664562Sgshapiro response = milter_command(SMFIC_HELO, helo, strlen(helo) + 1, 324764562Sgshapiro MilterHeloMacros, e, state); 324864562Sgshapiro milter_per_connection_check(e); 324964562Sgshapiro return response; 325064562Sgshapiro} 325190792Sgshapiro/* 325264562Sgshapiro** MILTER_ENVFROM -- send SMTP MAIL command info to milter filters 325364562Sgshapiro** 325464562Sgshapiro** Parameters: 325564562Sgshapiro** args -- SMTP MAIL command args (args[0] == sender). 325664562Sgshapiro** e -- current envelope. 325764562Sgshapiro** state -- return state from response. 325864562Sgshapiro** 325964562Sgshapiro** Returns: 326064562Sgshapiro** response string (may be NULL) 326164562Sgshapiro*/ 326264562Sgshapiro 326364562Sgshapirochar * 326464562Sgshapiromilter_envfrom(args, e, state) 326564562Sgshapiro char **args; 326664562Sgshapiro ENVELOPE *e; 326764562Sgshapiro char *state; 326864562Sgshapiro{ 326964562Sgshapiro int i; 327064562Sgshapiro char *buf, *bp; 327164562Sgshapiro char *response; 327264562Sgshapiro ssize_t s; 327364562Sgshapiro 327464562Sgshapiro if (tTd(64, 10)) 327564562Sgshapiro { 327690792Sgshapiro sm_dprintf("milter_envfrom:"); 327764562Sgshapiro for (i = 0; args[i] != NULL; i++) 327890792Sgshapiro sm_dprintf(" %s", args[i]); 327990792Sgshapiro sm_dprintf("\n"); 328064562Sgshapiro } 328164562Sgshapiro 328264562Sgshapiro /* sanity check */ 328364562Sgshapiro if (args[0] == NULL) 328464562Sgshapiro { 328564562Sgshapiro *state = SMFIR_REJECT; 328690792Sgshapiro if (MilterLogLevel > 10) 328790792Sgshapiro sm_syslog(LOG_INFO, e->e_id, 328890792Sgshapiro "Milter: reject, no sender"); 328964562Sgshapiro return NULL; 329064562Sgshapiro } 329164562Sgshapiro 329264562Sgshapiro /* new message, so ... */ 329364562Sgshapiro for (i = 0; InputFilters[i] != NULL; i++) 329464562Sgshapiro { 329564562Sgshapiro struct milter *m = InputFilters[i]; 329664562Sgshapiro 329764562Sgshapiro switch (m->mf_state) 329864562Sgshapiro { 329964562Sgshapiro case SMFS_INMSG: 330064562Sgshapiro /* abort in message filters */ 330164562Sgshapiro milter_abort_filter(m, e); 330264562Sgshapiro /* FALLTHROUGH */ 330364562Sgshapiro 330464562Sgshapiro case SMFS_DONE: 330564562Sgshapiro /* reset done filters */ 330664562Sgshapiro m->mf_state = SMFS_OPEN; 330764562Sgshapiro break; 330864562Sgshapiro } 330964562Sgshapiro } 331064562Sgshapiro 331164562Sgshapiro /* put together data */ 331264562Sgshapiro s = 0; 331364562Sgshapiro for (i = 0; args[i] != NULL; i++) 331464562Sgshapiro s += strlen(args[i]) + 1; 331590792Sgshapiro 331690792Sgshapiro if (s < 0) 331790792Sgshapiro { 331890792Sgshapiro *state = SMFIR_TEMPFAIL; 331990792Sgshapiro return NULL; 332090792Sgshapiro } 332190792Sgshapiro 332290792Sgshapiro buf = (char *) xalloc(s); 332364562Sgshapiro bp = buf; 332464562Sgshapiro for (i = 0; args[i] != NULL; i++) 332564562Sgshapiro { 332690792Sgshapiro (void) sm_strlcpy(bp, args[i], s - (bp - buf)); 332764562Sgshapiro bp += strlen(bp) + 1; 332864562Sgshapiro } 332964562Sgshapiro 333090792Sgshapiro if (MilterLogLevel > 14) 333190792Sgshapiro sm_syslog(LOG_INFO, e->e_id, "Milter: senders: %s", buf); 333290792Sgshapiro 333364562Sgshapiro /* send it over */ 333464562Sgshapiro response = milter_command(SMFIC_MAIL, buf, s, 333564562Sgshapiro MilterEnvFromMacros, e, state); 333690792Sgshapiro sm_free(buf); /* XXX */ 333764562Sgshapiro 333864562Sgshapiro /* 333964562Sgshapiro ** If filter rejects/discards a per message command, 334064562Sgshapiro ** abort the other filters since we are done with the 334164562Sgshapiro ** current message. 334264562Sgshapiro */ 334364562Sgshapiro 334464562Sgshapiro MILTER_CHECK_DONE_MSG(); 334590792Sgshapiro if (MilterLogLevel > 10 && *state == SMFIR_REJECT) 334690792Sgshapiro sm_syslog(LOG_INFO, e->e_id, "Milter: reject, senders"); 334764562Sgshapiro return response; 334864562Sgshapiro} 334990792Sgshapiro/* 335064562Sgshapiro** MILTER_ENVRCPT -- send SMTP RCPT command info to milter filters 335164562Sgshapiro** 335264562Sgshapiro** Parameters: 335364562Sgshapiro** args -- SMTP MAIL command args (args[0] == recipient). 335464562Sgshapiro** e -- current envelope. 335564562Sgshapiro** state -- return state from response. 335664562Sgshapiro** 335764562Sgshapiro** Returns: 335864562Sgshapiro** response string (may be NULL) 335964562Sgshapiro*/ 336064562Sgshapiro 336164562Sgshapirochar * 336264562Sgshapiromilter_envrcpt(args, e, state) 336364562Sgshapiro char **args; 336464562Sgshapiro ENVELOPE *e; 336564562Sgshapiro char *state; 336664562Sgshapiro{ 336764562Sgshapiro int i; 336864562Sgshapiro char *buf, *bp; 336964562Sgshapiro char *response; 337064562Sgshapiro ssize_t s; 337164562Sgshapiro 337264562Sgshapiro if (tTd(64, 10)) 337364562Sgshapiro { 337490792Sgshapiro sm_dprintf("milter_envrcpt:"); 337564562Sgshapiro for (i = 0; args[i] != NULL; i++) 337690792Sgshapiro sm_dprintf(" %s", args[i]); 337790792Sgshapiro sm_dprintf("\n"); 337864562Sgshapiro } 337964562Sgshapiro 338064562Sgshapiro /* sanity check */ 338164562Sgshapiro if (args[0] == NULL) 338264562Sgshapiro { 338364562Sgshapiro *state = SMFIR_REJECT; 338490792Sgshapiro if (MilterLogLevel > 10) 338590792Sgshapiro sm_syslog(LOG_INFO, e->e_id, "Milter: reject, no rcpt"); 338664562Sgshapiro return NULL; 338764562Sgshapiro } 338864562Sgshapiro 338964562Sgshapiro /* put together data */ 339064562Sgshapiro s = 0; 339164562Sgshapiro for (i = 0; args[i] != NULL; i++) 339264562Sgshapiro s += strlen(args[i]) + 1; 339390792Sgshapiro 339490792Sgshapiro if (s < 0) 339590792Sgshapiro { 339690792Sgshapiro *state = SMFIR_TEMPFAIL; 339790792Sgshapiro return NULL; 339890792Sgshapiro } 339990792Sgshapiro 340090792Sgshapiro buf = (char *) xalloc(s); 340164562Sgshapiro bp = buf; 340264562Sgshapiro for (i = 0; args[i] != NULL; i++) 340364562Sgshapiro { 340490792Sgshapiro (void) sm_strlcpy(bp, args[i], s - (bp - buf)); 340564562Sgshapiro bp += strlen(bp) + 1; 340664562Sgshapiro } 340764562Sgshapiro 340890792Sgshapiro if (MilterLogLevel > 14) 340990792Sgshapiro sm_syslog(LOG_INFO, e->e_id, "Milter: rcpts: %s", buf); 341090792Sgshapiro 341164562Sgshapiro /* send it over */ 341264562Sgshapiro response = milter_command(SMFIC_RCPT, buf, s, 341364562Sgshapiro MilterEnvRcptMacros, e, state); 341490792Sgshapiro sm_free(buf); /* XXX */ 341564562Sgshapiro return response; 341664562Sgshapiro} 341790792Sgshapiro/* 341864562Sgshapiro** MILTER_DATA -- send message headers/body and gather final message results 341964562Sgshapiro** 342064562Sgshapiro** Parameters: 342164562Sgshapiro** e -- current envelope. 342264562Sgshapiro** state -- return state from response. 342364562Sgshapiro** 342464562Sgshapiro** Returns: 342564562Sgshapiro** response string (may be NULL) 342664562Sgshapiro** 342764562Sgshapiro** Side effects: 342864562Sgshapiro** - Uses e->e_dfp for access to the body 342964562Sgshapiro** - Can call the various milter action routines to 343064562Sgshapiro** modify the envelope or message. 343164562Sgshapiro*/ 343264562Sgshapiro 343364562Sgshapiro# define MILTER_CHECK_RESULTS() \ 343464562Sgshapiro if (*state == SMFIR_ACCEPT || \ 343564562Sgshapiro m->mf_state == SMFS_DONE || \ 343664562Sgshapiro m->mf_state == SMFS_ERROR) \ 343764562Sgshapiro { \ 343864562Sgshapiro if (m->mf_state != SMFS_ERROR) \ 343964562Sgshapiro m->mf_state = SMFS_DONE; \ 344064562Sgshapiro continue; /* to next filter */ \ 344164562Sgshapiro } \ 344264562Sgshapiro if (*state != SMFIR_CONTINUE) \ 344364562Sgshapiro { \ 344464562Sgshapiro m->mf_state = SMFS_DONE; \ 344564562Sgshapiro goto finishup; \ 344664562Sgshapiro } 344764562Sgshapiro 344864562Sgshapirochar * 344964562Sgshapiromilter_data(e, state) 345064562Sgshapiro ENVELOPE *e; 345164562Sgshapiro char *state; 345264562Sgshapiro{ 345390792Sgshapiro bool replbody = false; /* milter_replbody() called? */ 345490792Sgshapiro bool replfailed = false; /* milter_replbody() failed? */ 345590792Sgshapiro bool rewind = false; /* rewind data file? */ 345690792Sgshapiro bool dfopen = false; /* data file open for writing? */ 345764562Sgshapiro bool newfilter; /* reset on each new filter */ 345864562Sgshapiro char rcmd; 345964562Sgshapiro int i; 346064562Sgshapiro int save_errno; 346164562Sgshapiro char *response = NULL; 346264562Sgshapiro time_t eomsent; 346364562Sgshapiro ssize_t rlen; 346464562Sgshapiro 346564562Sgshapiro if (tTd(64, 10)) 346690792Sgshapiro sm_dprintf("milter_data\n"); 346764562Sgshapiro 346864562Sgshapiro *state = SMFIR_CONTINUE; 346964562Sgshapiro 347064562Sgshapiro /* 347164562Sgshapiro ** XXX: Should actually send body chunks to each filter 347264562Sgshapiro ** a chunk at a time instead of sending the whole body to 347364562Sgshapiro ** each filter in turn. However, only if the filters don't 347464562Sgshapiro ** change the body. 347564562Sgshapiro */ 347664562Sgshapiro 347764562Sgshapiro for (i = 0; InputFilters[i] != NULL; i++) 347864562Sgshapiro { 347964562Sgshapiro struct milter *m = InputFilters[i]; 348064562Sgshapiro 348164562Sgshapiro if (*state != SMFIR_CONTINUE && 348264562Sgshapiro *state != SMFIR_ACCEPT) 348364562Sgshapiro { 348464562Sgshapiro /* 348564562Sgshapiro ** A previous filter has dealt with the message, 348664562Sgshapiro ** safe to stop processing the filters. 348764562Sgshapiro */ 348864562Sgshapiro 348964562Sgshapiro break; 349064562Sgshapiro } 349164562Sgshapiro 349264562Sgshapiro /* Now reset state for later evaluation */ 349364562Sgshapiro *state = SMFIR_CONTINUE; 349490792Sgshapiro newfilter = true; 349564562Sgshapiro 349671345Sgshapiro /* previous problem? */ 349771345Sgshapiro if (m->mf_state == SMFS_ERROR) 349871345Sgshapiro { 349971345Sgshapiro MILTER_CHECK_ERROR(continue); 350071345Sgshapiro break; 350171345Sgshapiro } 350271345Sgshapiro 350364562Sgshapiro /* sanity checks */ 350464562Sgshapiro if (m->mf_sock < 0 || 350564562Sgshapiro (m->mf_state != SMFS_OPEN && m->mf_state != SMFS_INMSG)) 350664562Sgshapiro continue; 350764562Sgshapiro 350864562Sgshapiro m->mf_state = SMFS_INMSG; 350964562Sgshapiro 351064562Sgshapiro /* check if filter wants the headers */ 351164562Sgshapiro if (!bitset(SMFIP_NOHDRS, m->mf_pflags)) 351264562Sgshapiro { 351364562Sgshapiro response = milter_headers(m, e, state); 351464562Sgshapiro MILTER_CHECK_RESULTS(); 351564562Sgshapiro } 351664562Sgshapiro 351764562Sgshapiro /* check if filter wants EOH */ 351864562Sgshapiro if (!bitset(SMFIP_NOEOH, m->mf_pflags)) 351964562Sgshapiro { 352064562Sgshapiro if (tTd(64, 10)) 352190792Sgshapiro sm_dprintf("milter_data: eoh\n"); 352264562Sgshapiro 352364562Sgshapiro /* send it over */ 352464562Sgshapiro response = milter_send_command(m, SMFIC_EOH, NULL, 0, 352564562Sgshapiro e, state); 352664562Sgshapiro MILTER_CHECK_RESULTS(); 352764562Sgshapiro } 352864562Sgshapiro 352964562Sgshapiro /* check if filter wants the body */ 353064562Sgshapiro if (!bitset(SMFIP_NOBODY, m->mf_pflags) && 353164562Sgshapiro e->e_dfp != NULL) 353264562Sgshapiro { 353390792Sgshapiro rewind = true; 353464562Sgshapiro response = milter_body(m, e, state); 353564562Sgshapiro MILTER_CHECK_RESULTS(); 353664562Sgshapiro } 353764562Sgshapiro 353864562Sgshapiro /* send the final body chunk */ 353964562Sgshapiro (void) milter_write(m, SMFIC_BODYEOB, NULL, 0, 354064562Sgshapiro m->mf_timeout[SMFTO_WRITE], e); 354164562Sgshapiro 354264562Sgshapiro /* Get time EOM sent for timeout */ 354364562Sgshapiro eomsent = curtime(); 354464562Sgshapiro 354564562Sgshapiro /* deal with the possibility of multiple responses */ 354664562Sgshapiro while (*state == SMFIR_CONTINUE) 354764562Sgshapiro { 354864562Sgshapiro /* Check total timeout from EOM to final ACK/NAK */ 354964562Sgshapiro if (m->mf_timeout[SMFTO_EOM] > 0 && 355064562Sgshapiro curtime() - eomsent >= m->mf_timeout[SMFTO_EOM]) 355164562Sgshapiro { 355264562Sgshapiro if (tTd(64, 5)) 355390792Sgshapiro sm_dprintf("milter_data(%s): EOM ACK/NAK timeout\n", 355464562Sgshapiro m->mf_name); 355590792Sgshapiro if (MilterLogLevel > 0) 355664562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 355790792Sgshapiro "milter_data(%s): EOM ACK/NAK timeout", 355864562Sgshapiro m->mf_name); 355990792Sgshapiro milter_error(m, e); 356094334Sgshapiro MILTER_CHECK_ERROR(break); 356164562Sgshapiro break; 356264562Sgshapiro } 356364562Sgshapiro 356464562Sgshapiro response = milter_read(m, &rcmd, &rlen, 356564562Sgshapiro m->mf_timeout[SMFTO_READ], e); 356664562Sgshapiro if (m->mf_state == SMFS_ERROR) 356764562Sgshapiro break; 356864562Sgshapiro 356964562Sgshapiro if (tTd(64, 10)) 357090792Sgshapiro sm_dprintf("milter_data(%s): state %c\n", 357190792Sgshapiro m->mf_name, (char) rcmd); 357264562Sgshapiro 357364562Sgshapiro switch (rcmd) 357464562Sgshapiro { 357564562Sgshapiro case SMFIR_REPLYCODE: 357664562Sgshapiro MILTER_CHECK_REPLYCODE("554 5.7.1 Command rejected"); 357790792Sgshapiro if (MilterLogLevel > 12) 357890792Sgshapiro sm_syslog(LOG_INFO, e->e_id, "milter=%s, reject=%s", 357990792Sgshapiro m->mf_name, response); 358064562Sgshapiro *state = rcmd; 358164562Sgshapiro m->mf_state = SMFS_DONE; 358264562Sgshapiro break; 358364562Sgshapiro 358490792Sgshapiro case SMFIR_REJECT: /* log msg at end of function */ 358590792Sgshapiro if (MilterLogLevel > 12) 358690792Sgshapiro sm_syslog(LOG_INFO, e->e_id, "milter=%s, reject", 358790792Sgshapiro m->mf_name); 358890792Sgshapiro *state = rcmd; 358990792Sgshapiro m->mf_state = SMFS_DONE; 359090792Sgshapiro break; 359190792Sgshapiro 359264562Sgshapiro case SMFIR_DISCARD: 359390792Sgshapiro if (MilterLogLevel > 12) 359490792Sgshapiro sm_syslog(LOG_INFO, e->e_id, "milter=%s, discard", 359590792Sgshapiro m->mf_name); 359690792Sgshapiro *state = rcmd; 359790792Sgshapiro m->mf_state = SMFS_DONE; 359890792Sgshapiro break; 359990792Sgshapiro 360064562Sgshapiro case SMFIR_TEMPFAIL: 360190792Sgshapiro if (MilterLogLevel > 12) 360290792Sgshapiro sm_syslog(LOG_INFO, e->e_id, "milter=%s, tempfail", 360390792Sgshapiro m->mf_name); 360464562Sgshapiro *state = rcmd; 360564562Sgshapiro m->mf_state = SMFS_DONE; 360664562Sgshapiro break; 360764562Sgshapiro 360864562Sgshapiro case SMFIR_CONTINUE: 360964562Sgshapiro case SMFIR_ACCEPT: 361064562Sgshapiro /* this filter is done with message */ 361164562Sgshapiro if (replfailed) 361264562Sgshapiro *state = SMFIR_TEMPFAIL; 361364562Sgshapiro else 361464562Sgshapiro *state = SMFIR_ACCEPT; 361564562Sgshapiro m->mf_state = SMFS_DONE; 361664562Sgshapiro break; 361764562Sgshapiro 361864562Sgshapiro case SMFIR_PROGRESS: 361964562Sgshapiro break; 362064562Sgshapiro 362190792Sgshapiro# if _FFR_QUARANTINE 362290792Sgshapiro case SMFIR_QUARANTINE: 362390792Sgshapiro if (!bitset(SMFIF_QUARANTINE, m->mf_fflags)) 362490792Sgshapiro { 362590792Sgshapiro if (MilterLogLevel > 9) 362690792Sgshapiro sm_syslog(LOG_WARNING, e->e_id, 362790792Sgshapiro "milter_data(%s): lied about quarantining, honoring request anyway", 362890792Sgshapiro m->mf_name); 362990792Sgshapiro } 363090792Sgshapiro if (response == NULL) 363190792Sgshapiro response = newstr(""); 363290792Sgshapiro if (MilterLogLevel > 3) 363390792Sgshapiro sm_syslog(LOG_INFO, e->e_id, 363490792Sgshapiro "milter=%s, quarantine=%s", 363590792Sgshapiro m->mf_name, response); 363690792Sgshapiro e->e_quarmsg = sm_rpool_strdup_x(e->e_rpool, 363790792Sgshapiro response); 363890792Sgshapiro macdefine(&e->e_macro, A_PERM, 363990792Sgshapiro macid("{quarantine}"), e->e_quarmsg); 364090792Sgshapiro break; 364190792Sgshapiro# endif /* _FFR_QUARANTINE */ 364290792Sgshapiro 364364562Sgshapiro case SMFIR_ADDHEADER: 364464562Sgshapiro if (!bitset(SMFIF_ADDHDRS, m->mf_fflags)) 364564562Sgshapiro { 364690792Sgshapiro if (MilterLogLevel > 9) 364764562Sgshapiro sm_syslog(LOG_WARNING, e->e_id, 364864562Sgshapiro "milter_data(%s): lied about adding headers, honoring request anyway", 364964562Sgshapiro m->mf_name); 365064562Sgshapiro } 365164562Sgshapiro milter_addheader(response, rlen, e); 365264562Sgshapiro break; 365364562Sgshapiro 365464562Sgshapiro case SMFIR_CHGHEADER: 365564562Sgshapiro if (!bitset(SMFIF_CHGHDRS, m->mf_fflags)) 365664562Sgshapiro { 365790792Sgshapiro if (MilterLogLevel > 9) 365864562Sgshapiro sm_syslog(LOG_WARNING, e->e_id, 365964562Sgshapiro "milter_data(%s): lied about changing headers, honoring request anyway", 366064562Sgshapiro m->mf_name); 366164562Sgshapiro } 366264562Sgshapiro milter_changeheader(response, rlen, e); 366364562Sgshapiro break; 366464562Sgshapiro 366564562Sgshapiro case SMFIR_ADDRCPT: 366664562Sgshapiro if (!bitset(SMFIF_ADDRCPT, m->mf_fflags)) 366764562Sgshapiro { 366890792Sgshapiro if (MilterLogLevel > 9) 366964562Sgshapiro sm_syslog(LOG_WARNING, e->e_id, 367064562Sgshapiro "milter_data(%s) lied about adding recipients, honoring request anyway", 367164562Sgshapiro m->mf_name); 367264562Sgshapiro } 367364562Sgshapiro milter_addrcpt(response, rlen, e); 367464562Sgshapiro break; 367564562Sgshapiro 367664562Sgshapiro case SMFIR_DELRCPT: 367764562Sgshapiro if (!bitset(SMFIF_DELRCPT, m->mf_fflags)) 367864562Sgshapiro { 367990792Sgshapiro if (MilterLogLevel > 9) 368064562Sgshapiro sm_syslog(LOG_WARNING, e->e_id, 368164562Sgshapiro "milter_data(%s): lied about removing recipients, honoring request anyway", 368264562Sgshapiro m->mf_name); 368364562Sgshapiro } 368464562Sgshapiro milter_delrcpt(response, rlen, e); 368564562Sgshapiro break; 368664562Sgshapiro 368764562Sgshapiro case SMFIR_REPLBODY: 368864562Sgshapiro if (!bitset(SMFIF_MODBODY, m->mf_fflags)) 368964562Sgshapiro { 369090792Sgshapiro if (MilterLogLevel > 0) 369164562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 369264562Sgshapiro "milter_data(%s): lied about replacing body, rejecting request and tempfailing message", 369364562Sgshapiro m->mf_name); 369490792Sgshapiro replfailed = true; 369564562Sgshapiro break; 369664562Sgshapiro } 369764562Sgshapiro 369864562Sgshapiro /* already failed in attempt */ 369964562Sgshapiro if (replfailed) 370064562Sgshapiro break; 370164562Sgshapiro 370264562Sgshapiro if (!dfopen) 370364562Sgshapiro { 370464562Sgshapiro if (milter_reopen_df(e) < 0) 370564562Sgshapiro { 370690792Sgshapiro replfailed = true; 370764562Sgshapiro break; 370864562Sgshapiro } 370990792Sgshapiro dfopen = true; 371090792Sgshapiro rewind = true; 371164562Sgshapiro } 371264562Sgshapiro 371364562Sgshapiro if (milter_replbody(response, rlen, 371464562Sgshapiro newfilter, e) < 0) 371590792Sgshapiro replfailed = true; 371690792Sgshapiro newfilter = false; 371790792Sgshapiro replbody = true; 371864562Sgshapiro break; 371964562Sgshapiro 372064562Sgshapiro default: 372164562Sgshapiro /* Invalid response to command */ 372290792Sgshapiro if (MilterLogLevel > 0) 372364562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 372464562Sgshapiro "milter_data(%s): returned bogus response %c", 372564562Sgshapiro m->mf_name, rcmd); 372690792Sgshapiro milter_error(m, e); 372764562Sgshapiro break; 372864562Sgshapiro } 372990792Sgshapiro if (rcmd != SMFIR_REPLYCODE && response != NULL) 373064562Sgshapiro { 373190792Sgshapiro sm_free(response); /* XXX */ 373264562Sgshapiro response = NULL; 373364562Sgshapiro } 373464562Sgshapiro 373564562Sgshapiro if (m->mf_state == SMFS_ERROR) 373664562Sgshapiro break; 373764562Sgshapiro } 373864562Sgshapiro 373964562Sgshapiro if (replbody && !replfailed) 374064562Sgshapiro { 374164562Sgshapiro /* flush possible buffered character */ 374264562Sgshapiro milter_replbody(NULL, 0, !replbody, e); 374390792Sgshapiro replbody = false; 374464562Sgshapiro } 374564562Sgshapiro 374664562Sgshapiro if (m->mf_state == SMFS_ERROR) 374764562Sgshapiro { 374864562Sgshapiro MILTER_CHECK_ERROR(continue); 374964562Sgshapiro goto finishup; 375064562Sgshapiro } 375164562Sgshapiro } 375264562Sgshapiro 375364562Sgshapirofinishup: 375464562Sgshapiro /* leave things in the expected state if we touched it */ 375564562Sgshapiro if (replfailed) 375664562Sgshapiro { 375764562Sgshapiro if (*state == SMFIR_CONTINUE || 375864562Sgshapiro *state == SMFIR_ACCEPT) 375964562Sgshapiro { 376064562Sgshapiro *state = SMFIR_TEMPFAIL; 376190792Sgshapiro SM_FREE_CLR(response); 376264562Sgshapiro } 376364562Sgshapiro 376464562Sgshapiro if (dfopen) 376564562Sgshapiro { 376690792Sgshapiro (void) sm_io_close(e->e_dfp, SM_TIME_DEFAULT); 376764562Sgshapiro e->e_dfp = NULL; 376864562Sgshapiro e->e_flags &= ~EF_HAS_DF; 376990792Sgshapiro dfopen = false; 377064562Sgshapiro } 377190792Sgshapiro rewind = false; 377264562Sgshapiro } 377364562Sgshapiro 377464562Sgshapiro if ((dfopen && milter_reset_df(e) < 0) || 377564562Sgshapiro (rewind && bfrewind(e->e_dfp) < 0)) 377664562Sgshapiro { 377764562Sgshapiro save_errno = errno; 377864562Sgshapiro ExitStat = EX_IOERR; 377964562Sgshapiro 378064562Sgshapiro /* 378164562Sgshapiro ** If filter told us to keep message but we had 378264562Sgshapiro ** an error, we can't really keep it, tempfail it. 378364562Sgshapiro */ 378464562Sgshapiro 378564562Sgshapiro if (*state == SMFIR_CONTINUE || 378664562Sgshapiro *state == SMFIR_ACCEPT) 378764562Sgshapiro { 378864562Sgshapiro *state = SMFIR_TEMPFAIL; 378990792Sgshapiro SM_FREE_CLR(response); 379064562Sgshapiro } 379164562Sgshapiro 379264562Sgshapiro errno = save_errno; 379390792Sgshapiro syserr("milter_data: %s/%cf%s: read error", 379490792Sgshapiro qid_printqueue(e->e_qgrp, e->e_qdir), 379590792Sgshapiro DATAFL_LETTER, e->e_id); 379664562Sgshapiro } 379790792Sgshapiro 379864562Sgshapiro MILTER_CHECK_DONE_MSG(); 379990792Sgshapiro if (MilterLogLevel > 10 && *state == SMFIR_REJECT) 380090792Sgshapiro sm_syslog(LOG_INFO, e->e_id, "Milter: reject, data"); 380164562Sgshapiro return response; 380264562Sgshapiro} 380390792Sgshapiro/* 380464562Sgshapiro** MILTER_QUIT -- informs the filter(s) we are done and closes connection(s) 380564562Sgshapiro** 380664562Sgshapiro** Parameters: 380764562Sgshapiro** e -- current envelope. 380864562Sgshapiro** 380964562Sgshapiro** Returns: 381064562Sgshapiro** none 381164562Sgshapiro*/ 381264562Sgshapiro 381364562Sgshapirovoid 381464562Sgshapiromilter_quit(e) 381564562Sgshapiro ENVELOPE *e; 381664562Sgshapiro{ 381764562Sgshapiro int i; 381864562Sgshapiro 381964562Sgshapiro if (tTd(64, 10)) 382090792Sgshapiro sm_dprintf("milter_quit(%s)\n", e->e_id); 382164562Sgshapiro 382264562Sgshapiro for (i = 0; InputFilters[i] != NULL; i++) 382364562Sgshapiro milter_quit_filter(InputFilters[i], e); 382464562Sgshapiro} 382590792Sgshapiro/* 382664562Sgshapiro** MILTER_ABORT -- informs the filter(s) that we are aborting current message 382764562Sgshapiro** 382864562Sgshapiro** Parameters: 382964562Sgshapiro** e -- current envelope. 383064562Sgshapiro** 383164562Sgshapiro** Returns: 383264562Sgshapiro** none 383364562Sgshapiro*/ 383464562Sgshapiro 383564562Sgshapirovoid 383664562Sgshapiromilter_abort(e) 383764562Sgshapiro ENVELOPE *e; 383864562Sgshapiro{ 383964562Sgshapiro int i; 384064562Sgshapiro 384164562Sgshapiro if (tTd(64, 10)) 384290792Sgshapiro sm_dprintf("milter_abort\n"); 384364562Sgshapiro 384464562Sgshapiro for (i = 0; InputFilters[i] != NULL; i++) 384564562Sgshapiro { 384664562Sgshapiro struct milter *m = InputFilters[i]; 384764562Sgshapiro 384864562Sgshapiro /* sanity checks */ 384964562Sgshapiro if (m->mf_sock < 0 || m->mf_state != SMFS_INMSG) 385064562Sgshapiro continue; 385164562Sgshapiro 385264562Sgshapiro milter_abort_filter(m, e); 385364562Sgshapiro } 385464562Sgshapiro} 385590792Sgshapiro#endif /* MILTER */ 3856