milter.c revision 66494
164562Sgshapiro/* 264562Sgshapiro * Copyright (c) 1999-2000 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 1164562Sgshapiro#ifndef lint 1266494Sgshapirostatic char id[] = "@(#)$Id: milter.c,v 8.50.4.33 2000/09/19 19:40:15 gshapiro Exp $"; 1364562Sgshapiro#endif /* ! lint */ 1464562Sgshapiro 1564562Sgshapiro#if _FFR_MILTER 1664562Sgshapiro 1764562Sgshapiro# include <sendmail.h> 1864562Sgshapiro# include <errno.h> 1964562Sgshapiro# include <sys/time.h> 2064562Sgshapiro 2164562Sgshapiro# if NETINET || NETINET6 2264562Sgshapiro# include <arpa/inet.h> 2364562Sgshapiro# endif /* NETINET || NETINET6 */ 2464562Sgshapiro 2564562Sgshapiro 2664562Sgshapirostatic void milter_error __P((struct milter *)); 2764562Sgshapirostatic int milter_open __P((struct milter *, bool, ENVELOPE *)); 2864562Sgshapirostatic void milter_parse_timeouts __P((char *, struct milter *)); 2964562Sgshapiro 3064562Sgshapirostatic char *MilterConnectMacros[MAXFILTERMACROS + 1]; 3164562Sgshapirostatic char *MilterHeloMacros[MAXFILTERMACROS + 1]; 3264562Sgshapirostatic char *MilterEnvFromMacros[MAXFILTERMACROS + 1]; 3364562Sgshapirostatic char *MilterEnvRcptMacros[MAXFILTERMACROS + 1]; 3464562Sgshapiro 3564562Sgshapiro# define MILTER_CHECK_DONE_MSG() \ 3664562Sgshapiro if (*state == SMFIR_REPLYCODE || \ 3764562Sgshapiro *state == SMFIR_REJECT || \ 3864562Sgshapiro *state == SMFIR_DISCARD || \ 3964562Sgshapiro *state == SMFIR_TEMPFAIL) \ 4064562Sgshapiro { \ 4164562Sgshapiro /* Abort the filters to let them know we are done with msg */ \ 4264562Sgshapiro milter_abort(e); \ 4364562Sgshapiro } 4464562Sgshapiro 4564562Sgshapiro# define MILTER_CHECK_ERROR(action) \ 4664562Sgshapiro if (bitnset(SMF_TEMPFAIL, m->mf_flags)) \ 4764562Sgshapiro *state = SMFIR_TEMPFAIL; \ 4864562Sgshapiro else if (bitnset(SMF_REJECT, m->mf_flags)) \ 4964562Sgshapiro *state = SMFIR_REJECT; \ 5064562Sgshapiro else \ 5164562Sgshapiro action; 5264562Sgshapiro 5364562Sgshapiro# define MILTER_CHECK_REPLYCODE(default) \ 5464562Sgshapiro if (response == NULL || \ 5564562Sgshapiro strlen(response) + 1 != (size_t) rlen || \ 5664562Sgshapiro rlen < 3 || \ 5764562Sgshapiro (response[0] != '4' && response[0] != '5') || \ 5864562Sgshapiro !isascii(response[1]) || !isdigit(response[1]) || \ 5964562Sgshapiro !isascii(response[2]) || !isdigit(response[2])) \ 6064562Sgshapiro { \ 6164562Sgshapiro if (response != NULL) \ 6264562Sgshapiro free(response); \ 6364562Sgshapiro response = newstr(default); \ 6464562Sgshapiro } \ 6564562Sgshapiro else \ 6664562Sgshapiro { \ 6764562Sgshapiro char *ptr = response; \ 6864562Sgshapiro \ 6964562Sgshapiro /* Check for unprotected %'s in the string */ \ 7064562Sgshapiro while (*ptr != '\0') \ 7164562Sgshapiro { \ 7264562Sgshapiro if (*ptr == '%' && *++ptr != '%') \ 7364562Sgshapiro { \ 7464562Sgshapiro free(response); \ 7564562Sgshapiro response = newstr(default); \ 7664562Sgshapiro break; \ 7764562Sgshapiro } \ 7864562Sgshapiro ptr++; \ 7964562Sgshapiro } \ 8064562Sgshapiro } 8164562Sgshapiro 8264562Sgshapiro# define MILTER_DF_ERROR(msg) \ 8364562Sgshapiro{ \ 8464562Sgshapiro int save_errno = errno; \ 8564562Sgshapiro \ 8664562Sgshapiro if (tTd(64, 5)) \ 8764562Sgshapiro { \ 8864562Sgshapiro dprintf(msg, dfname, errstring(save_errno)); \ 8964562Sgshapiro dprintf("\n"); \ 9064562Sgshapiro } \ 9164562Sgshapiro if (LogLevel > 0) \ 9264562Sgshapiro sm_syslog(LOG_ERR, e->e_id, msg, dfname, errstring(save_errno)); \ 9364562Sgshapiro if (SuperSafe) \ 9464562Sgshapiro { \ 9564562Sgshapiro if (e->e_dfp != NULL) \ 9664562Sgshapiro { \ 9764562Sgshapiro (void) fclose(e->e_dfp); \ 9864562Sgshapiro e->e_dfp = NULL; \ 9964562Sgshapiro } \ 10064562Sgshapiro e->e_flags &= ~EF_HAS_DF; \ 10164562Sgshapiro } \ 10264562Sgshapiro errno = save_errno; \ 10364562Sgshapiro} 10464562Sgshapiro 10564562Sgshapiro/* 10664562Sgshapiro** MILTER_TIMEOUT -- make sure socket is ready in time 10764562Sgshapiro** 10864562Sgshapiro** Parameters: 10964562Sgshapiro** routine -- routine name for debug/logging 11064562Sgshapiro** secs -- number of seconds in timeout 11164562Sgshapiro** write -- waiting to read or write? 11264562Sgshapiro** 11364562Sgshapiro** Assumes 'm' is a milter structure for the current socket. 11464562Sgshapiro*/ 11564562Sgshapiro 11664562Sgshapiro 11764562Sgshapiro# define MILTER_TIMEOUT(routine, secs, write) \ 11864562Sgshapiro{ \ 11964562Sgshapiro int ret; \ 12064562Sgshapiro int save_errno; \ 12164562Sgshapiro fd_set fds; \ 12264562Sgshapiro struct timeval tv; \ 12364562Sgshapiro \ 12464562Sgshapiro if (m->mf_sock >= FD_SETSIZE) \ 12564562Sgshapiro { \ 12664562Sgshapiro if (tTd(64, 5)) \ 12764562Sgshapiro dprintf("%s(%s): socket %d is larger than FD_SETSIZE %d\n", \ 12864562Sgshapiro routine, m->mf_name, m->mf_sock, FD_SETSIZE); \ 12964562Sgshapiro if (LogLevel > 0) \ 13064562Sgshapiro sm_syslog(LOG_ERR, e->e_id, \ 13164562Sgshapiro "%s(%s): socket %d is larger than FD_SETSIZE %d\n", \ 13264562Sgshapiro routine, m->mf_name, m->mf_sock, FD_SETSIZE); \ 13364562Sgshapiro milter_error(m); \ 13464562Sgshapiro return NULL; \ 13564562Sgshapiro } \ 13664562Sgshapiro \ 13764562Sgshapiro FD_ZERO(&fds); \ 13864562Sgshapiro FD_SET(m->mf_sock, &fds); \ 13964562Sgshapiro tv.tv_sec = secs; \ 14064562Sgshapiro tv.tv_usec = 0; \ 14164562Sgshapiro ret = select(m->mf_sock + 1, \ 14264562Sgshapiro write ? NULL : &fds, \ 14364562Sgshapiro write ? &fds : NULL, \ 14464562Sgshapiro NULL, &tv); \ 14564562Sgshapiro \ 14664562Sgshapiro switch (ret) \ 14764562Sgshapiro { \ 14864562Sgshapiro case 0: \ 14964562Sgshapiro if (tTd(64, 5)) \ 15064562Sgshapiro dprintf("%s(%s): timeout\n", routine, m->mf_name); \ 15164562Sgshapiro if (LogLevel > 0) \ 15264562Sgshapiro sm_syslog(LOG_ERR, e->e_id, "%s(%s): timeout\n", \ 15364562Sgshapiro routine, m->mf_name); \ 15464562Sgshapiro milter_error(m); \ 15564562Sgshapiro return NULL; \ 15664562Sgshapiro \ 15764562Sgshapiro case -1: \ 15864562Sgshapiro save_errno = errno; \ 15964562Sgshapiro if (tTd(64, 5)) \ 16064562Sgshapiro dprintf("%s(%s): select: %s\n", \ 16164562Sgshapiro routine, m->mf_name, errstring(save_errno)); \ 16264562Sgshapiro if (LogLevel > 0) \ 16364562Sgshapiro sm_syslog(LOG_ERR, e->e_id, \ 16464562Sgshapiro "%s(%s): select: %s\n", \ 16564562Sgshapiro routine, m->mf_name, errstring(save_errno)); \ 16664562Sgshapiro milter_error(m); \ 16764562Sgshapiro return NULL; \ 16864562Sgshapiro \ 16964562Sgshapiro default: \ 17064562Sgshapiro if (FD_ISSET(m->mf_sock, &fds)) \ 17164562Sgshapiro break; \ 17264562Sgshapiro if (tTd(64, 5)) \ 17364562Sgshapiro dprintf("%s(%s): socket not ready\n", \ 17464562Sgshapiro routine, m->mf_name); \ 17564562Sgshapiro if (LogLevel > 0) \ 17664562Sgshapiro sm_syslog(LOG_ERR, e->e_id, \ 17764562Sgshapiro "%s(%s): socket not ready\n", \ 17864562Sgshapiro m->mf_name, routine); \ 17964562Sgshapiro milter_error(m); \ 18064562Sgshapiro return NULL; \ 18164562Sgshapiro } \ 18264562Sgshapiro} 18364562Sgshapiro 18464562Sgshapiro 18564562Sgshapiro/* 18664562Sgshapiro** Low level functions 18764562Sgshapiro*/ 18864562Sgshapiro 18964562Sgshapiro/* 19064562Sgshapiro** MILTER_READ -- read from a remote milter filter 19164562Sgshapiro** 19264562Sgshapiro** Parameters: 19364562Sgshapiro** m -- milter to read from. 19464562Sgshapiro** cmd -- return param for command read. 19564562Sgshapiro** rlen -- return length of response string. 19664562Sgshapiro** to -- timeout in seconds. 19764562Sgshapiro** e -- current envelope. 19864562Sgshapiro** 19964562Sgshapiro** Returns: 20064562Sgshapiro** response string (may be NULL) 20164562Sgshapiro*/ 20264562Sgshapiro 20364562Sgshapirostatic char * 20464562Sgshapiromilter_sysread(m, buf, sz, to, e) 20564562Sgshapiro struct milter *m; 20664562Sgshapiro char *buf; 20764562Sgshapiro ssize_t sz; 20864562Sgshapiro time_t to; 20964562Sgshapiro ENVELOPE *e; 21064562Sgshapiro{ 21166494Sgshapiro time_t readstart = 0; 21264562Sgshapiro ssize_t len, curl; 21364562Sgshapiro 21464562Sgshapiro curl = 0; 21564562Sgshapiro 21664562Sgshapiro if (to > 0) 21764562Sgshapiro readstart = curtime(); 21864562Sgshapiro 21964562Sgshapiro for (;;) 22064562Sgshapiro { 22164562Sgshapiro if (to > 0) 22264562Sgshapiro { 22364562Sgshapiro time_t now; 22464562Sgshapiro 22564562Sgshapiro now = curtime(); 22664562Sgshapiro if (now - readstart >= to) 22764562Sgshapiro { 22864562Sgshapiro if (tTd(64, 5)) 22964562Sgshapiro dprintf("milter_read(%s): timeout before data read\n", 23064562Sgshapiro m->mf_name); 23164562Sgshapiro if (LogLevel > 0) 23264562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 23364562Sgshapiro "milter_read(%s): timeout before data read\n", 23464562Sgshapiro m->mf_name); 23564562Sgshapiro milter_error(m); 23664562Sgshapiro return NULL; 23764562Sgshapiro } 23864562Sgshapiro to -= now - readstart; 23964562Sgshapiro readstart = now; 24064562Sgshapiro MILTER_TIMEOUT("milter_read", to, FALSE); 24164562Sgshapiro } 24264562Sgshapiro 24364562Sgshapiro len = read(m->mf_sock, buf + curl, sz - curl); 24464562Sgshapiro 24564562Sgshapiro if (len < 0) 24664562Sgshapiro { 24764562Sgshapiro int save_errno = errno; 24864562Sgshapiro 24964562Sgshapiro if (tTd(64, 5)) 25064562Sgshapiro dprintf("milter_read(%s): read returned %ld: %s\n", 25164562Sgshapiro m->mf_name, (long) len, 25264562Sgshapiro errstring(save_errno)); 25364562Sgshapiro if (LogLevel > 0) 25464562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 25564562Sgshapiro "milter_read(%s): read returned %ld: %s", 25664562Sgshapiro m->mf_name, (long) len, 25764562Sgshapiro errstring(save_errno)); 25864562Sgshapiro milter_error(m); 25964562Sgshapiro return NULL; 26064562Sgshapiro } 26164562Sgshapiro 26264562Sgshapiro curl += len; 26364562Sgshapiro if (len == 0 || len >= sz) 26464562Sgshapiro break; 26564562Sgshapiro 26664562Sgshapiro } 26764562Sgshapiro 26864562Sgshapiro if (curl != sz) 26964562Sgshapiro { 27064562Sgshapiro if (tTd(64, 5)) 27164562Sgshapiro dprintf("milter_read(%s): read returned %ld, expecting %ld\n", 27264562Sgshapiro m->mf_name, (long) curl, (long) sz); 27364562Sgshapiro if (LogLevel > 0) 27464562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 27564562Sgshapiro "milter_read(%s): read returned %ld, expecting %ld", 27664562Sgshapiro m->mf_name, (long) curl, (long) sz); 27764562Sgshapiro milter_error(m); 27864562Sgshapiro return NULL; 27964562Sgshapiro } 28064562Sgshapiro return buf; 28164562Sgshapiro} 28264562Sgshapiro 28364562Sgshapirostatic char * 28464562Sgshapiromilter_read(m, cmd, rlen, to, e) 28564562Sgshapiro struct milter *m; 28664562Sgshapiro char *cmd; 28764562Sgshapiro ssize_t *rlen; 28864562Sgshapiro time_t to; 28964562Sgshapiro ENVELOPE *e; 29064562Sgshapiro{ 29166494Sgshapiro time_t readstart = 0; 29264562Sgshapiro ssize_t expl; 29364562Sgshapiro mi_int32 i; 29464562Sgshapiro char *buf; 29564562Sgshapiro char data[MILTER_LEN_BYTES + 1]; 29664562Sgshapiro 29764562Sgshapiro *rlen = 0; 29864562Sgshapiro *cmd = '\0'; 29964562Sgshapiro 30064562Sgshapiro if (to > 0) 30164562Sgshapiro readstart = curtime(); 30264562Sgshapiro 30364562Sgshapiro if (milter_sysread(m, data, sizeof data, to, e) == NULL) 30464562Sgshapiro return NULL; 30564562Sgshapiro 30664562Sgshapiro /* reset timeout */ 30764562Sgshapiro if (to > 0) 30864562Sgshapiro { 30964562Sgshapiro time_t now; 31064562Sgshapiro 31164562Sgshapiro now = curtime(); 31264562Sgshapiro if (now - readstart >= to) 31364562Sgshapiro { 31464562Sgshapiro if (tTd(64, 5)) 31564562Sgshapiro dprintf("milter_read(%s): timeout before data read\n", 31664562Sgshapiro m->mf_name); 31764562Sgshapiro if (LogLevel > 0) 31864562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 31964562Sgshapiro "milter_read(%s): timeout before data read\n", 32064562Sgshapiro m->mf_name); 32164562Sgshapiro milter_error(m); 32264562Sgshapiro return NULL; 32364562Sgshapiro } 32464562Sgshapiro to -= now - readstart; 32564562Sgshapiro } 32664562Sgshapiro 32764562Sgshapiro *cmd = data[MILTER_LEN_BYTES]; 32864562Sgshapiro data[MILTER_LEN_BYTES] = '\0'; 32964562Sgshapiro (void) memcpy(&i, data, MILTER_LEN_BYTES); 33064562Sgshapiro expl = ntohl(i) - 1; 33164562Sgshapiro 33264562Sgshapiro if (tTd(64, 25)) 33364562Sgshapiro dprintf("milter_read(%s): expecting %ld bytes\n", 33464562Sgshapiro m->mf_name, (long) expl); 33564562Sgshapiro 33664562Sgshapiro if (expl < 0) 33764562Sgshapiro { 33864562Sgshapiro if (tTd(64, 5)) 33964562Sgshapiro dprintf("milter_read(%s): read size %ld out of range\n", 34064562Sgshapiro m->mf_name, (long) expl); 34164562Sgshapiro if (LogLevel > 0) 34264562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 34364562Sgshapiro "milter_read(%s): read size %ld out of range", 34464562Sgshapiro m->mf_name, (long) expl); 34564562Sgshapiro milter_error(m); 34664562Sgshapiro return NULL; 34764562Sgshapiro } 34864562Sgshapiro 34964562Sgshapiro if (expl == 0) 35064562Sgshapiro return NULL; 35164562Sgshapiro 35264562Sgshapiro buf = (char *)xalloc(expl); 35364562Sgshapiro 35464562Sgshapiro if (milter_sysread(m, buf, expl, to, e) == NULL) 35564562Sgshapiro { 35664562Sgshapiro free(buf); 35764562Sgshapiro return NULL; 35864562Sgshapiro } 35964562Sgshapiro 36064562Sgshapiro if (tTd(64, 50)) 36164562Sgshapiro dprintf("milter_read(%s): Returning %*s\n", 36264562Sgshapiro m->mf_name, (int) expl, buf); 36364562Sgshapiro *rlen = expl; 36464562Sgshapiro return buf; 36564562Sgshapiro} 36664562Sgshapiro/* 36764562Sgshapiro** MILTER_WRITE -- write to a remote milter filter 36864562Sgshapiro** 36964562Sgshapiro** Parameters: 37064562Sgshapiro** m -- milter to read from. 37164562Sgshapiro** cmd -- command to send. 37264562Sgshapiro** buf -- optional command data. 37364562Sgshapiro** len -- length of buf. 37464562Sgshapiro** to -- timeout in seconds. 37564562Sgshapiro** e -- current envelope. 37664562Sgshapiro** 37764562Sgshapiro** Returns: 37864562Sgshapiro** buf if successful, NULL otherwise 37964562Sgshapiro** Not actually used anywhere but function prototype 38064562Sgshapiro** must match milter_read() 38164562Sgshapiro*/ 38264562Sgshapiro 38364562Sgshapirostatic char * 38464562Sgshapiromilter_write(m, cmd, buf, len, to, e) 38564562Sgshapiro struct milter *m; 38664562Sgshapiro char cmd; 38764562Sgshapiro char *buf; 38864562Sgshapiro ssize_t len; 38964562Sgshapiro time_t to; 39064562Sgshapiro ENVELOPE *e; 39164562Sgshapiro{ 39264562Sgshapiro time_t writestart = (time_t) 0; 39364562Sgshapiro ssize_t sl, i; 39464562Sgshapiro mi_int32 nl; 39564562Sgshapiro char data[MILTER_LEN_BYTES + 1]; 39664562Sgshapiro 39764562Sgshapiro if (len < 0 || len > MILTER_CHUNK_SIZE) 39864562Sgshapiro { 39964562Sgshapiro if (tTd(64, 5)) 40064562Sgshapiro dprintf("milter_write(%s): length %ld out of range\n", 40164562Sgshapiro m->mf_name, (long) len); 40264562Sgshapiro if (LogLevel > 0) 40364562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 40464562Sgshapiro "milter_write(%s): length %ld out of range", 40564562Sgshapiro m->mf_name, (long) len); 40664562Sgshapiro milter_error(m); 40764562Sgshapiro return NULL; 40864562Sgshapiro } 40964562Sgshapiro 41064562Sgshapiro if (tTd(64, 20)) 41164562Sgshapiro dprintf("milter_write(%s): cmd %c, len %ld\n", 41264562Sgshapiro m->mf_name, cmd, (long) len); 41364562Sgshapiro 41464562Sgshapiro nl = htonl(len + 1); /* add 1 for the cmd char */ 41564562Sgshapiro (void) memcpy(data, (char *) &nl, MILTER_LEN_BYTES); 41664562Sgshapiro data[MILTER_LEN_BYTES] = cmd; 41764562Sgshapiro sl = MILTER_LEN_BYTES + 1; 41864562Sgshapiro 41964562Sgshapiro if (to > 0) 42064562Sgshapiro { 42164562Sgshapiro writestart = curtime(); 42264562Sgshapiro MILTER_TIMEOUT("milter_write", to, TRUE); 42364562Sgshapiro } 42464562Sgshapiro 42564562Sgshapiro /* use writev() instead to send the whole stuff at once? */ 42664562Sgshapiro i = write(m->mf_sock, (void *) data, sl); 42764562Sgshapiro if (i != sl) 42864562Sgshapiro { 42964562Sgshapiro int save_errno = errno; 43064562Sgshapiro 43164562Sgshapiro if (tTd(64, 5)) 43264562Sgshapiro dprintf("milter_write(%s): write(%c) returned %ld, expected %ld: %s\n", 43364562Sgshapiro m->mf_name, cmd, (long) i, (long) sl, 43464562Sgshapiro errstring(save_errno)); 43564562Sgshapiro if (LogLevel > 0) 43664562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 43764562Sgshapiro "milter_write(%s): write(%c) returned %ld, expected %ld: %s", 43864562Sgshapiro m->mf_name, cmd, (long) i, (long) sl, 43964562Sgshapiro errstring(save_errno)); 44064562Sgshapiro milter_error(m); 44164562Sgshapiro return buf; 44264562Sgshapiro } 44364562Sgshapiro 44464562Sgshapiro if (len <= 0 || buf == NULL) 44564562Sgshapiro return buf; 44664562Sgshapiro 44764562Sgshapiro if (tTd(64, 50)) 44864562Sgshapiro dprintf("milter_write(%s): Sending %*s\n", 44964562Sgshapiro m->mf_name, (int) len, buf); 45064562Sgshapiro 45164562Sgshapiro if (to > 0) 45264562Sgshapiro { 45364562Sgshapiro time_t now; 45464562Sgshapiro 45564562Sgshapiro now = curtime(); 45664562Sgshapiro if (now - writestart >= to) 45764562Sgshapiro { 45864562Sgshapiro if (tTd(64, 5)) 45964562Sgshapiro dprintf("milter_write(%s): timeout before data send\n", 46064562Sgshapiro m->mf_name); 46164562Sgshapiro if (LogLevel > 0) 46264562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 46364562Sgshapiro "milter_write(%s): timeout before data send\n", 46464562Sgshapiro m->mf_name); 46564562Sgshapiro milter_error(m); 46664562Sgshapiro return NULL; 46764562Sgshapiro } 46864562Sgshapiro else 46964562Sgshapiro { 47064562Sgshapiro to -= now - writestart; 47164562Sgshapiro MILTER_TIMEOUT("milter_write", to, TRUE); 47264562Sgshapiro } 47364562Sgshapiro } 47464562Sgshapiro 47564562Sgshapiro i = write(m->mf_sock, (void *) buf, len); 47664562Sgshapiro if (i != len) 47764562Sgshapiro { 47864562Sgshapiro int save_errno = errno; 47964562Sgshapiro 48064562Sgshapiro if (tTd(64, 5)) 48164562Sgshapiro dprintf("milter_write(%s): write(%c) returned %ld, expected %ld: %s\n", 48264562Sgshapiro m->mf_name, cmd, (long) i, (long) sl, 48364562Sgshapiro errstring(save_errno)); 48464562Sgshapiro if (LogLevel > 0) 48564562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 48664562Sgshapiro "milter_write(%s): write(%c) returned %ld, expected %ld: %s", 48764562Sgshapiro m->mf_name, cmd, (long) i, (long) len, 48864562Sgshapiro errstring(save_errno)); 48964562Sgshapiro milter_error(m); 49064562Sgshapiro return NULL; 49164562Sgshapiro } 49264562Sgshapiro return buf; 49364562Sgshapiro} 49464562Sgshapiro 49564562Sgshapiro/* 49664562Sgshapiro** Utility functions 49764562Sgshapiro*/ 49864562Sgshapiro 49964562Sgshapiro/* 50064562Sgshapiro** MILTER_OPEN -- connect to remote milter filter 50164562Sgshapiro** 50264562Sgshapiro** Parameters: 50364562Sgshapiro** m -- milter to connect to. 50464562Sgshapiro** parseonly -- parse but don't connect. 50564562Sgshapiro** e -- current envelope. 50664562Sgshapiro** 50764562Sgshapiro** Returns: 50864562Sgshapiro** connected socket if sucessful && !parseonly, 50964562Sgshapiro** 0 upon parse success if parseonly, 51064562Sgshapiro** -1 otherwise. 51164562Sgshapiro*/ 51264562Sgshapiro 51364562Sgshapirostatic int 51464562Sgshapiromilter_open(m, parseonly, e) 51564562Sgshapiro struct milter *m; 51664562Sgshapiro bool parseonly; 51764562Sgshapiro ENVELOPE *e; 51864562Sgshapiro{ 51964562Sgshapiro int sock = 0; 52064562Sgshapiro SOCKADDR_LEN_T addrlen = 0; 52164562Sgshapiro int addrno = 0; 52264562Sgshapiro int save_errno; 52364562Sgshapiro char *p; 52464562Sgshapiro char *colon; 52564562Sgshapiro char *at; 52664562Sgshapiro struct hostent *hp = NULL; 52764562Sgshapiro SOCKADDR addr; 52864562Sgshapiro 52964562Sgshapiro if (m->mf_conn == NULL || m->mf_conn[0] == '\0') 53064562Sgshapiro { 53164562Sgshapiro if (tTd(64, 5)) 53264562Sgshapiro dprintf("X%s: empty or missing socket information\n", 53364562Sgshapiro m->mf_name); 53464562Sgshapiro if (parseonly) 53564562Sgshapiro syserr("X%s: empty or missing socket information", 53664562Sgshapiro m->mf_name); 53764562Sgshapiro else if (LogLevel > 10) 53864562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 53964562Sgshapiro "X%s: empty or missing socket information", 54064562Sgshapiro m->mf_name); 54164562Sgshapiro milter_error(m); 54264562Sgshapiro return -1; 54364562Sgshapiro } 54464562Sgshapiro 54564562Sgshapiro /* protocol:filename or protocol:port@host */ 54664562Sgshapiro p = m->mf_conn; 54764562Sgshapiro colon = strchr(p, ':'); 54864562Sgshapiro if (colon != NULL) 54964562Sgshapiro { 55064562Sgshapiro *colon = '\0'; 55164562Sgshapiro 55264562Sgshapiro if (*p == '\0') 55364562Sgshapiro { 55464562Sgshapiro# if NETUNIX 55564562Sgshapiro /* default to AF_UNIX */ 55664562Sgshapiro addr.sa.sa_family = AF_UNIX; 55764562Sgshapiro# else /* NETUNIX */ 55864562Sgshapiro# if NETINET 55964562Sgshapiro /* default to AF_INET */ 56064562Sgshapiro addr.sa.sa_family = AF_INET; 56164562Sgshapiro# else /* NETINET */ 56264562Sgshapiro# if NETINET6 56364562Sgshapiro /* default to AF_INET6 */ 56464562Sgshapiro addr.sa.sa_family = AF_INET6; 56564562Sgshapiro# else /* NETINET6 */ 56664562Sgshapiro /* no protocols available */ 56764562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 56864562Sgshapiro "X%s: no valid socket protocols available", 56964562Sgshapiro m->mf_name); 57064562Sgshapiro milter_error(m); 57164562Sgshapiro return -1; 57264562Sgshapiro# endif /* NETINET6 */ 57364562Sgshapiro# endif /* NETINET */ 57464562Sgshapiro# endif /* NETUNIX */ 57564562Sgshapiro } 57664562Sgshapiro# if NETUNIX 57764562Sgshapiro else if (strcasecmp(p, "unix") == 0 || 57864562Sgshapiro strcasecmp(p, "local") == 0) 57964562Sgshapiro addr.sa.sa_family = AF_UNIX; 58064562Sgshapiro# endif /* NETUNIX */ 58164562Sgshapiro# if NETINET 58264562Sgshapiro else if (strcasecmp(p, "inet") == 0) 58364562Sgshapiro addr.sa.sa_family = AF_INET; 58464562Sgshapiro# endif /* NETINET */ 58564562Sgshapiro# if NETINET6 58664562Sgshapiro else if (strcasecmp(p, "inet6") == 0) 58764562Sgshapiro addr.sa.sa_family = AF_INET6; 58864562Sgshapiro# endif /* NETINET6 */ 58964562Sgshapiro else 59064562Sgshapiro { 59164562Sgshapiro# ifdef EPROTONOSUPPORT 59264562Sgshapiro errno = EPROTONOSUPPORT; 59364562Sgshapiro# else /* EPROTONOSUPPORT */ 59464562Sgshapiro errno = EINVAL; 59564562Sgshapiro# endif /* EPROTONOSUPPORT */ 59664562Sgshapiro if (tTd(64, 5)) 59764562Sgshapiro dprintf("X%s: unknown socket type %s\n", 59864562Sgshapiro m->mf_name, p); 59964562Sgshapiro if (parseonly) 60064562Sgshapiro syserr("X%s: unknown socket type %s", 60164562Sgshapiro m->mf_name, p); 60264562Sgshapiro else if (LogLevel > 10) 60364562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 60464562Sgshapiro "X%s: unknown socket type %s", 60564562Sgshapiro m->mf_name, p); 60664562Sgshapiro milter_error(m); 60764562Sgshapiro return -1; 60864562Sgshapiro } 60964562Sgshapiro *colon++ = ':'; 61064562Sgshapiro } 61164562Sgshapiro else 61264562Sgshapiro { 61364562Sgshapiro /* default to AF_UNIX */ 61464562Sgshapiro addr.sa.sa_family = AF_UNIX; 61564562Sgshapiro colon = p; 61664562Sgshapiro } 61764562Sgshapiro 61864562Sgshapiro# if NETUNIX 61964562Sgshapiro if (addr.sa.sa_family == AF_UNIX) 62064562Sgshapiro { 62164562Sgshapiro long sff = SFF_SAFEDIRPATH|SFF_OPENASROOT|SFF_NOLINK|SFF_EXECOK; 62264562Sgshapiro 62364562Sgshapiro at = colon; 62464562Sgshapiro if (strlen(colon) >= sizeof addr.sunix.sun_path) 62564562Sgshapiro { 62664562Sgshapiro if (tTd(64, 5)) 62764562Sgshapiro dprintf("X%s: local socket name %s too long\n", 62864562Sgshapiro m->mf_name, colon); 62964562Sgshapiro errno = EINVAL; 63064562Sgshapiro if (parseonly) 63164562Sgshapiro syserr("X%s: local socket name %s too long", 63264562Sgshapiro m->mf_name, colon); 63364562Sgshapiro else if (LogLevel > 10) 63464562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 63564562Sgshapiro "X%s: local socket name %s too long", 63664562Sgshapiro m->mf_name, colon); 63764562Sgshapiro milter_error(m); 63864562Sgshapiro return -1; 63964562Sgshapiro } 64064562Sgshapiro errno = safefile(colon, RunAsUid, RunAsGid, RunAsUserName, sff, 64164562Sgshapiro S_IRUSR|S_IWUSR, NULL); 64264562Sgshapiro 64364562Sgshapiro /* if just parsing .cf file, socket doesn't need to exist */ 64464562Sgshapiro if (parseonly && errno == ENOENT) 64564562Sgshapiro { 64664562Sgshapiro if (OpMode == MD_DAEMON || 64764562Sgshapiro OpMode == MD_FGDAEMON) 64864562Sgshapiro fprintf(stderr, 64964562Sgshapiro "WARNING: X%s: local socket name %s missing\n", 65064562Sgshapiro m->mf_name, colon); 65164562Sgshapiro } 65264562Sgshapiro else if (errno != 0) 65364562Sgshapiro { 65464562Sgshapiro /* if not safe, don't create */ 65564562Sgshapiro save_errno = errno; 65664562Sgshapiro if (tTd(64, 5)) 65764562Sgshapiro dprintf("X%s: local socket name %s unsafe\n", 65864562Sgshapiro m->mf_name, colon); 65964562Sgshapiro errno = save_errno; 66064562Sgshapiro if (parseonly) 66164562Sgshapiro { 66264562Sgshapiro if (OpMode == MD_DAEMON || 66364562Sgshapiro OpMode == MD_FGDAEMON || 66464562Sgshapiro OpMode == MD_SMTP) 66564562Sgshapiro syserr("X%s: local socket name %s unsafe", 66664562Sgshapiro m->mf_name, colon); 66764562Sgshapiro } 66864562Sgshapiro else if (LogLevel > 10) 66964562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 67064562Sgshapiro "X%s: local socket name %s unsafe", 67164562Sgshapiro m->mf_name, colon); 67264562Sgshapiro milter_error(m); 67364562Sgshapiro return -1; 67464562Sgshapiro } 67564562Sgshapiro 67664562Sgshapiro (void) strlcpy(addr.sunix.sun_path, colon, 67764562Sgshapiro sizeof addr.sunix.sun_path); 67864562Sgshapiro addrlen = sizeof (struct sockaddr_un); 67964562Sgshapiro } 68064562Sgshapiro else 68164562Sgshapiro# endif /* NETUNIX */ 68264562Sgshapiro# if NETINET || NETINET6 68364562Sgshapiro if (FALSE 68464562Sgshapiro# if NETINET 68564562Sgshapiro || addr.sa.sa_family == AF_INET 68664562Sgshapiro# endif /* NETINET */ 68764562Sgshapiro# if NETINET6 68864562Sgshapiro || addr.sa.sa_family == AF_INET6 68964562Sgshapiro# endif /* NETINET6 */ 69064562Sgshapiro ) 69164562Sgshapiro { 69264562Sgshapiro u_short port; 69364562Sgshapiro 69464562Sgshapiro /* Parse port@host */ 69564562Sgshapiro at = strchr(colon, '@'); 69664562Sgshapiro if (at == NULL) 69764562Sgshapiro { 69864562Sgshapiro if (tTd(64, 5)) 69964562Sgshapiro dprintf("X%s: bad address %s (expected port@host)\n", 70064562Sgshapiro m->mf_name, colon); 70164562Sgshapiro if (parseonly) 70264562Sgshapiro syserr("X%s: bad address %s (expected port@host)", 70364562Sgshapiro m->mf_name, colon); 70464562Sgshapiro else if (LogLevel > 10) 70564562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 70664562Sgshapiro "X%s: bad address %s (expected port@host)", 70764562Sgshapiro m->mf_name, colon); 70864562Sgshapiro milter_error(m); 70964562Sgshapiro return -1; 71064562Sgshapiro } 71164562Sgshapiro *at = '\0'; 71264562Sgshapiro if (isascii(*colon) && isdigit(*colon)) 71364562Sgshapiro port = htons((u_short) atoi(colon)); 71464562Sgshapiro else 71564562Sgshapiro { 71664562Sgshapiro# ifdef NO_GETSERVBYNAME 71764562Sgshapiro if (tTd(64, 5)) 71864562Sgshapiro dprintf("X%s: invalid port number %s\n", 71964562Sgshapiro m->mf_name, colon); 72064562Sgshapiro if (parseonly) 72164562Sgshapiro syserr("X%s: invalid port number %s", 72264562Sgshapiro m->mf_name, colon); 72364562Sgshapiro else if (LogLevel > 10) 72464562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 72564562Sgshapiro "X%s: invalid port number %s", 72664562Sgshapiro m->mf_name, colon); 72764562Sgshapiro milter_error(m); 72864562Sgshapiro return -1; 72964562Sgshapiro# else /* NO_GETSERVBYNAME */ 73064562Sgshapiro register struct servent *sp; 73164562Sgshapiro 73264562Sgshapiro sp = getservbyname(colon, "tcp"); 73364562Sgshapiro if (sp == NULL) 73464562Sgshapiro { 73564562Sgshapiro save_errno = errno; 73664562Sgshapiro if (tTd(64, 5)) 73764562Sgshapiro dprintf("X%s: unknown port name %s\n", 73864562Sgshapiro m->mf_name, colon); 73964562Sgshapiro errno = save_errno; 74064562Sgshapiro if (parseonly) 74164562Sgshapiro syserr("X%s: unknown port name %s", 74264562Sgshapiro m->mf_name, colon); 74364562Sgshapiro else if (LogLevel > 10) 74464562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 74564562Sgshapiro "X%s: unknown port name %s", 74664562Sgshapiro m->mf_name, colon); 74764562Sgshapiro milter_error(m); 74864562Sgshapiro return -1; 74964562Sgshapiro } 75064562Sgshapiro port = sp->s_port; 75164562Sgshapiro# endif /* NO_GETSERVBYNAME */ 75264562Sgshapiro } 75364562Sgshapiro *at++ = '@'; 75464562Sgshapiro if (*at == '[') 75564562Sgshapiro { 75664562Sgshapiro char *end; 75764562Sgshapiro 75864562Sgshapiro end = strchr(at, ']'); 75964562Sgshapiro if (end != NULL) 76064562Sgshapiro { 76164562Sgshapiro bool found = FALSE; 76264562Sgshapiro# if NETINET 76364562Sgshapiro unsigned long hid = INADDR_NONE; 76464562Sgshapiro# endif /* NETINET */ 76564562Sgshapiro# if NETINET6 76664562Sgshapiro struct sockaddr_in6 hid6; 76764562Sgshapiro# endif /* NETINET6 */ 76864562Sgshapiro 76964562Sgshapiro *end = '\0'; 77064562Sgshapiro# if NETINET 77164562Sgshapiro if (addr.sa.sa_family == AF_INET && 77264562Sgshapiro (hid = inet_addr(&at[1])) != INADDR_NONE) 77364562Sgshapiro { 77464562Sgshapiro addr.sin.sin_addr.s_addr = hid; 77564562Sgshapiro addr.sin.sin_port = port; 77664562Sgshapiro found = TRUE; 77764562Sgshapiro } 77864562Sgshapiro# endif /* NETINET */ 77964562Sgshapiro# if NETINET6 78064562Sgshapiro (void) memset(&hid6, '\0', sizeof hid6); 78164562Sgshapiro if (addr.sa.sa_family == AF_INET6 && 78264562Sgshapiro inet_pton(AF_INET6, &at[1], 78364562Sgshapiro &hid6.sin6_addr) == 1) 78464562Sgshapiro { 78564562Sgshapiro addr.sin6.sin6_addr = hid6.sin6_addr; 78664562Sgshapiro addr.sin6.sin6_port = port; 78764562Sgshapiro found = TRUE; 78864562Sgshapiro } 78964562Sgshapiro# endif /* NETINET6 */ 79064562Sgshapiro *end = ']'; 79164562Sgshapiro if (!found) 79264562Sgshapiro { 79364562Sgshapiro if (tTd(64, 5)) 79464562Sgshapiro dprintf("X%s: Invalid numeric domain spec \"%s\"\n", 79564562Sgshapiro m->mf_name, at); 79664562Sgshapiro if (parseonly) 79764562Sgshapiro syserr("X%s: Invalid numeric domain spec \"%s\"", 79864562Sgshapiro m->mf_name, at); 79964562Sgshapiro else if (LogLevel > 10) 80064562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 80164562Sgshapiro "X%s: Invalid numeric domain spec \"%s\"", 80264562Sgshapiro m->mf_name, at); 80364562Sgshapiro milter_error(m); 80464562Sgshapiro return -1; 80564562Sgshapiro } 80664562Sgshapiro } 80764562Sgshapiro else 80864562Sgshapiro { 80964562Sgshapiro if (tTd(64, 5)) 81064562Sgshapiro dprintf("X%s: Invalid numeric domain spec \"%s\"\n", 81164562Sgshapiro m->mf_name, at); 81264562Sgshapiro if (parseonly) 81364562Sgshapiro syserr("X%s: Invalid numeric domain spec \"%s\"", 81464562Sgshapiro m->mf_name, at); 81564562Sgshapiro else if (LogLevel > 10) 81664562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 81764562Sgshapiro "X%s: Invalid numeric domain spec \"%s\"", 81864562Sgshapiro m->mf_name, at); 81964562Sgshapiro milter_error(m); 82064562Sgshapiro return -1; 82164562Sgshapiro } 82264562Sgshapiro } 82364562Sgshapiro else 82464562Sgshapiro { 82564562Sgshapiro hp = sm_gethostbyname(at, addr.sa.sa_family); 82664562Sgshapiro if (hp == NULL) 82764562Sgshapiro { 82864562Sgshapiro save_errno = errno; 82964562Sgshapiro if (tTd(64, 5)) 83064562Sgshapiro dprintf("X%s: Unknown host name %s\n", 83164562Sgshapiro m->mf_name, at); 83264562Sgshapiro errno = save_errno; 83364562Sgshapiro if (parseonly) 83464562Sgshapiro syserr("X%s: Unknown host name %s", 83564562Sgshapiro m->mf_name, at); 83664562Sgshapiro else if (LogLevel > 10) 83764562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 83864562Sgshapiro "X%s: Unknown host name %s", 83964562Sgshapiro m->mf_name, at); 84064562Sgshapiro milter_error(m); 84164562Sgshapiro return -1; 84264562Sgshapiro } 84364562Sgshapiro addr.sa.sa_family = hp->h_addrtype; 84464562Sgshapiro switch (hp->h_addrtype) 84564562Sgshapiro { 84664562Sgshapiro# if NETINET 84764562Sgshapiro case AF_INET: 84864562Sgshapiro memmove(&addr.sin.sin_addr, 84964562Sgshapiro hp->h_addr, 85064562Sgshapiro INADDRSZ); 85164562Sgshapiro addr.sin.sin_port = port; 85264562Sgshapiro addrlen = sizeof (struct sockaddr_in); 85364562Sgshapiro addrno = 1; 85464562Sgshapiro break; 85564562Sgshapiro# endif /* NETINET */ 85664562Sgshapiro 85764562Sgshapiro# if NETINET6 85864562Sgshapiro case AF_INET6: 85964562Sgshapiro memmove(&addr.sin6.sin6_addr, 86064562Sgshapiro hp->h_addr, 86164562Sgshapiro IN6ADDRSZ); 86264562Sgshapiro addr.sin6.sin6_port = port; 86364562Sgshapiro addrlen = sizeof (struct sockaddr_in6); 86464562Sgshapiro addrno = 1; 86564562Sgshapiro break; 86664562Sgshapiro# endif /* NETINET6 */ 86764562Sgshapiro 86864562Sgshapiro default: 86964562Sgshapiro if (tTd(64, 5)) 87064562Sgshapiro dprintf("X%s: Unknown protocol for %s (%d)\n", 87164562Sgshapiro m->mf_name, at, 87264562Sgshapiro hp->h_addrtype); 87364562Sgshapiro if (parseonly) 87464562Sgshapiro syserr("X%s: Unknown protocol for %s (%d)", 87564562Sgshapiro m->mf_name, at, hp->h_addrtype); 87664562Sgshapiro else if (LogLevel > 10) 87764562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 87864562Sgshapiro "X%s: Unknown protocol for %s (%d)", 87964562Sgshapiro m->mf_name, at, 88064562Sgshapiro hp->h_addrtype); 88164562Sgshapiro milter_error(m); 88264562Sgshapiro return -1; 88364562Sgshapiro } 88464562Sgshapiro } 88564562Sgshapiro } 88664562Sgshapiro else 88764562Sgshapiro# endif /* NETINET || NETINET6 */ 88864562Sgshapiro { 88964562Sgshapiro if (tTd(64, 5)) 89064562Sgshapiro dprintf("X%s: unknown socket protocol\n", m->mf_name); 89164562Sgshapiro if (parseonly) 89264562Sgshapiro syserr("X%s: unknown socket protocol", m->mf_name); 89364562Sgshapiro else if (LogLevel > 10) 89464562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 89564562Sgshapiro "X%s: unknown socket protocol", m->mf_name); 89664562Sgshapiro milter_error(m); 89764562Sgshapiro return -1; 89864562Sgshapiro } 89964562Sgshapiro 90064562Sgshapiro /* just parsing through? */ 90164562Sgshapiro if (parseonly) 90264562Sgshapiro { 90364562Sgshapiro m->mf_state = SMFS_READY; 90464562Sgshapiro return 0; 90564562Sgshapiro } 90664562Sgshapiro 90764562Sgshapiro /* sanity check */ 90864562Sgshapiro if (m->mf_state != SMFS_READY && 90964562Sgshapiro m->mf_state != SMFS_CLOSED) 91064562Sgshapiro { 91164562Sgshapiro /* shouldn't happen */ 91264562Sgshapiro if (tTd(64, 1)) 91364562Sgshapiro dprintf("milter_open(%s): Trying to open filter in state %c\n", 91464562Sgshapiro m->mf_name, (char) m->mf_state); 91564562Sgshapiro milter_error(m); 91664562Sgshapiro return -1; 91764562Sgshapiro } 91864562Sgshapiro 91964562Sgshapiro /* nope, actually connecting */ 92064562Sgshapiro for (;;) 92164562Sgshapiro { 92264562Sgshapiro sock = socket(addr.sa.sa_family, SOCK_STREAM, 0); 92364562Sgshapiro if (sock < 0) 92464562Sgshapiro { 92564562Sgshapiro save_errno = errno; 92664562Sgshapiro if (tTd(64, 5)) 92764562Sgshapiro dprintf("X%s: error creating socket: %s\n", 92864562Sgshapiro m->mf_name, errstring(save_errno)); 92964562Sgshapiro if (LogLevel > 0) 93064562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 93164562Sgshapiro "X%s: error creating socket: %s", 93264562Sgshapiro m->mf_name, errstring(save_errno)); 93364562Sgshapiro milter_error(m); 93464562Sgshapiro return -1; 93564562Sgshapiro } 93664562Sgshapiro 93764562Sgshapiro if (connect(sock, (struct sockaddr *) &addr, addrlen) >= 0) 93864562Sgshapiro break; 93964562Sgshapiro 94064562Sgshapiro /* couldn't connect.... try next address */ 94164562Sgshapiro save_errno = errno; 94266494Sgshapiro p = CurHostName; 94366494Sgshapiro CurHostName = at; 94464562Sgshapiro if (tTd(64, 5)) 94564562Sgshapiro dprintf("milter_open(%s): %s failed: %s\n", 94664562Sgshapiro m->mf_name, at, errstring(save_errno)); 94764562Sgshapiro if (LogLevel >= 14) 94864562Sgshapiro sm_syslog(LOG_INFO, e->e_id, 94964562Sgshapiro "milter_open(%s): %s failed: %s", 95064562Sgshapiro m->mf_name, at, errstring(save_errno)); 95166494Sgshapiro CurHostName = p; 95264562Sgshapiro (void) close(sock); 95364562Sgshapiro 95464562Sgshapiro /* try next address */ 95564562Sgshapiro if (hp != NULL && hp->h_addr_list[addrno] != NULL) 95664562Sgshapiro { 95764562Sgshapiro switch (addr.sa.sa_family) 95864562Sgshapiro { 95964562Sgshapiro# if NETINET 96064562Sgshapiro case AF_INET: 96164562Sgshapiro memmove(&addr.sin.sin_addr, 96264562Sgshapiro hp->h_addr_list[addrno++], 96364562Sgshapiro INADDRSZ); 96464562Sgshapiro break; 96564562Sgshapiro# endif /* NETINET */ 96664562Sgshapiro 96764562Sgshapiro# if NETINET6 96864562Sgshapiro case AF_INET6: 96964562Sgshapiro memmove(&addr.sin6.sin6_addr, 97064562Sgshapiro hp->h_addr_list[addrno++], 97164562Sgshapiro IN6ADDRSZ); 97264562Sgshapiro break; 97364562Sgshapiro# endif /* NETINET6 */ 97464562Sgshapiro 97564562Sgshapiro default: 97664562Sgshapiro if (tTd(64, 5)) 97764562Sgshapiro dprintf("X%s: Unknown protocol for %s (%d)\n", 97864562Sgshapiro m->mf_name, at, 97964562Sgshapiro hp->h_addrtype); 98064562Sgshapiro if (LogLevel > 0) 98164562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 98264562Sgshapiro "X%s: Unknown protocol for %s (%d)", 98364562Sgshapiro m->mf_name, at, 98464562Sgshapiro hp->h_addrtype); 98564562Sgshapiro milter_error(m); 98664562Sgshapiro return -1; 98764562Sgshapiro } 98864562Sgshapiro continue; 98964562Sgshapiro } 99064562Sgshapiro if (tTd(64, 5)) 99164562Sgshapiro dprintf("X%s: error connecting to filter\n", 99264562Sgshapiro m->mf_name); 99364562Sgshapiro if (LogLevel > 0) 99464562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 99564562Sgshapiro "X%s: error connecting to filter", 99664562Sgshapiro m->mf_name); 99764562Sgshapiro milter_error(m); 99864562Sgshapiro return -1; 99964562Sgshapiro } 100064562Sgshapiro m->mf_state = SMFS_OPEN; 100164562Sgshapiro return sock; 100264562Sgshapiro} 100364562Sgshapiro/* 100464562Sgshapiro** MILTER_SETUP -- setup structure for a mail filter 100564562Sgshapiro** 100664562Sgshapiro** Parameters: 100764562Sgshapiro** line -- the options line. 100864562Sgshapiro** 100964562Sgshapiro** Returns: 101064562Sgshapiro** none 101164562Sgshapiro*/ 101264562Sgshapiro 101364562Sgshapirovoid 101464562Sgshapiromilter_setup(line) 101564562Sgshapiro char *line; 101664562Sgshapiro{ 101764562Sgshapiro char fcode; 101864562Sgshapiro register char *p; 101964562Sgshapiro register struct milter *m; 102064562Sgshapiro STAB *s; 102164562Sgshapiro 102266494Sgshapiro /* collect the filter name */ 102364562Sgshapiro for (p = line; 102464562Sgshapiro *p != '\0' && *p != ',' && !(isascii(*p) && isspace(*p)); 102564562Sgshapiro p++) 102664562Sgshapiro continue; 102764562Sgshapiro if (*p != '\0') 102864562Sgshapiro *p++ = '\0'; 102964562Sgshapiro if (line[0] == '\0') 103064562Sgshapiro { 103164562Sgshapiro syserr("name required for mail filter"); 103264562Sgshapiro return; 103364562Sgshapiro } 103464562Sgshapiro m = (struct milter *)xalloc(sizeof *m); 103564562Sgshapiro memset((char *) m, '\0', sizeof *m); 103664562Sgshapiro m->mf_name = newstr(line); 103764562Sgshapiro m->mf_state = SMFS_READY; 103864562Sgshapiro m->mf_sock = -1; 103964562Sgshapiro m->mf_timeout[SMFTO_WRITE] = (time_t) 10; 104064562Sgshapiro m->mf_timeout[SMFTO_READ] = (time_t) 10; 104164562Sgshapiro m->mf_timeout[SMFTO_EOM] = (time_t) 300; 104264562Sgshapiro 104364562Sgshapiro /* now scan through and assign info from the fields */ 104464562Sgshapiro while (*p != '\0') 104564562Sgshapiro { 104664562Sgshapiro char *delimptr; 104764562Sgshapiro 104864562Sgshapiro while (*p != '\0' && 104964562Sgshapiro (*p == ',' || (isascii(*p) && isspace(*p)))) 105064562Sgshapiro p++; 105164562Sgshapiro 105264562Sgshapiro /* p now points to field code */ 105364562Sgshapiro fcode = *p; 105464562Sgshapiro while (*p != '\0' && *p != '=' && *p != ',') 105564562Sgshapiro p++; 105664562Sgshapiro if (*p++ != '=') 105764562Sgshapiro { 105864562Sgshapiro syserr("X%s: `=' expected", m->mf_name); 105964562Sgshapiro return; 106064562Sgshapiro } 106164562Sgshapiro while (isascii(*p) && isspace(*p)) 106264562Sgshapiro p++; 106364562Sgshapiro 106464562Sgshapiro /* p now points to the field body */ 106564562Sgshapiro p = munchstring(p, &delimptr, ','); 106664562Sgshapiro 106766494Sgshapiro /* install the field into the filter struct */ 106864562Sgshapiro switch (fcode) 106964562Sgshapiro { 107064562Sgshapiro case 'S': /* socket */ 107164562Sgshapiro if (p == NULL) 107264562Sgshapiro m->mf_conn = NULL; 107364562Sgshapiro else 107464562Sgshapiro m->mf_conn = newstr(p); 107564562Sgshapiro break; 107664562Sgshapiro 107764562Sgshapiro case 'F': /* Milter flags configured on MTA */ 107864562Sgshapiro for (; *p != '\0'; p++) 107964562Sgshapiro { 108064562Sgshapiro if (!(isascii(*p) && isspace(*p))) 108164562Sgshapiro setbitn(*p, m->mf_flags); 108264562Sgshapiro } 108364562Sgshapiro break; 108464562Sgshapiro 108564562Sgshapiro case 'T': /* timeouts */ 108664562Sgshapiro milter_parse_timeouts(p, m); 108764562Sgshapiro break; 108864562Sgshapiro 108964562Sgshapiro default: 109064562Sgshapiro syserr("X%s: unknown filter equate %c=", 109164562Sgshapiro m->mf_name, fcode); 109264562Sgshapiro break; 109364562Sgshapiro } 109464562Sgshapiro p = delimptr; 109564562Sgshapiro } 109664562Sgshapiro 109764562Sgshapiro /* early check for errors */ 109864562Sgshapiro (void) milter_open(m, TRUE, CurEnv); 109964562Sgshapiro 110066494Sgshapiro /* enter the filter into the symbol table */ 110164562Sgshapiro s = stab(m->mf_name, ST_MILTER, ST_ENTER); 110264562Sgshapiro if (s->s_milter != NULL) 110364562Sgshapiro syserr("X%s: duplicate filter definition", m->mf_name); 110464562Sgshapiro else 110564562Sgshapiro s->s_milter = m; 110664562Sgshapiro} 110764562Sgshapiro/* 110864562Sgshapiro** MILTER_PARSE_LIST -- parse option list into an array 110964562Sgshapiro** 111064562Sgshapiro** Called when reading configuration file. 111164562Sgshapiro** 111264562Sgshapiro** Parameters: 111364562Sgshapiro** spec -- the filter list. 111464562Sgshapiro** list -- the array to fill in. 111564562Sgshapiro** max -- the maximum number of entries in list. 111664562Sgshapiro** 111764562Sgshapiro** Returns: 111864562Sgshapiro** none 111964562Sgshapiro*/ 112064562Sgshapiro 112164562Sgshapirovoid 112264562Sgshapiromilter_parse_list(spec, list, max) 112364562Sgshapiro char *spec; 112464562Sgshapiro struct milter **list; 112564562Sgshapiro int max; 112664562Sgshapiro{ 112764562Sgshapiro int numitems = 0; 112864562Sgshapiro register char *p; 112964562Sgshapiro 113064562Sgshapiro /* leave one for the NULL signifying the end of the list */ 113164562Sgshapiro max--; 113264562Sgshapiro 113364562Sgshapiro for (p = spec; p != NULL; ) 113464562Sgshapiro { 113564562Sgshapiro STAB *s; 113664562Sgshapiro 113764562Sgshapiro while (isascii(*p) && isspace(*p)) 113864562Sgshapiro p++; 113964562Sgshapiro if (*p == '\0') 114064562Sgshapiro break; 114164562Sgshapiro spec = p; 114264562Sgshapiro 114364562Sgshapiro if (numitems >= max) 114464562Sgshapiro { 114564562Sgshapiro syserr("Too many filters defined, %d max", max); 114664562Sgshapiro if (max > 0) 114764562Sgshapiro list[0] = NULL; 114864562Sgshapiro return; 114964562Sgshapiro } 115064562Sgshapiro p = strpbrk(p, ","); 115164562Sgshapiro if (p != NULL) 115264562Sgshapiro *p++ = '\0'; 115364562Sgshapiro 115464562Sgshapiro s = stab(spec, ST_MILTER, ST_FIND); 115564562Sgshapiro if (s == NULL) 115664562Sgshapiro { 115764562Sgshapiro syserr("InputFilter %s not defined", spec); 115864562Sgshapiro ExitStat = EX_CONFIG; 115964562Sgshapiro return; 116064562Sgshapiro } 116164562Sgshapiro list[numitems++] = s->s_milter; 116264562Sgshapiro } 116364562Sgshapiro list[numitems] = NULL; 116464562Sgshapiro} 116564562Sgshapiro/* 116664562Sgshapiro** MILTER_PARSE_TIMEOUTS -- parse timeout list 116764562Sgshapiro** 116864562Sgshapiro** Called when reading configuration file. 116964562Sgshapiro** 117064562Sgshapiro** Parameters: 117164562Sgshapiro** spec -- the timeout list. 117264562Sgshapiro** m -- milter to set. 117364562Sgshapiro** 117464562Sgshapiro** Returns: 117564562Sgshapiro** none 117664562Sgshapiro*/ 117764562Sgshapiro 117864562Sgshapirostatic void 117964562Sgshapiromilter_parse_timeouts(spec, m) 118064562Sgshapiro char *spec; 118164562Sgshapiro struct milter *m; 118264562Sgshapiro{ 118364562Sgshapiro char fcode; 118464562Sgshapiro register char *p; 118564562Sgshapiro 118664562Sgshapiro p = spec; 118764562Sgshapiro 118864562Sgshapiro /* now scan through and assign info from the fields */ 118964562Sgshapiro while (*p != '\0') 119064562Sgshapiro { 119164562Sgshapiro char *delimptr; 119264562Sgshapiro 119364562Sgshapiro while (*p != '\0' && 119464562Sgshapiro (*p == ';' || (isascii(*p) && isspace(*p)))) 119564562Sgshapiro p++; 119664562Sgshapiro 119764562Sgshapiro /* p now points to field code */ 119864562Sgshapiro fcode = *p; 119964562Sgshapiro while (*p != '\0' && *p != ':') 120064562Sgshapiro p++; 120164562Sgshapiro if (*p++ != ':') 120264562Sgshapiro { 120364562Sgshapiro syserr("X%s, T=: `:' expected", m->mf_name); 120464562Sgshapiro return; 120564562Sgshapiro } 120664562Sgshapiro while (isascii(*p) && isspace(*p)) 120764562Sgshapiro p++; 120864562Sgshapiro 120964562Sgshapiro /* p now points to the field body */ 121064562Sgshapiro p = munchstring(p, &delimptr, ';'); 121164562Sgshapiro 121266494Sgshapiro /* install the field into the filter struct */ 121364562Sgshapiro switch (fcode) 121464562Sgshapiro { 121564562Sgshapiro case 'S': 121664562Sgshapiro m->mf_timeout[SMFTO_WRITE] = convtime(p, 's'); 121764562Sgshapiro if (tTd(64, 5)) 121864562Sgshapiro printf("X%s: %c=%ld\n", 121964562Sgshapiro m->mf_name, fcode, 122064562Sgshapiro (u_long) m->mf_timeout[SMFTO_WRITE]); 122164562Sgshapiro break; 122264562Sgshapiro 122364562Sgshapiro case 'R': 122464562Sgshapiro m->mf_timeout[SMFTO_READ] = convtime(p, 's'); 122564562Sgshapiro if (tTd(64, 5)) 122664562Sgshapiro printf("X%s: %c=%ld\n", 122764562Sgshapiro m->mf_name, fcode, 122864562Sgshapiro (u_long) m->mf_timeout[SMFTO_READ]); 122964562Sgshapiro break; 123064562Sgshapiro 123164562Sgshapiro case 'E': 123264562Sgshapiro m->mf_timeout[SMFTO_EOM] = convtime(p, 's'); 123364562Sgshapiro if (tTd(64, 5)) 123464562Sgshapiro printf("X%s: %c=%ld\n", 123564562Sgshapiro m->mf_name, fcode, 123664562Sgshapiro (u_long) m->mf_timeout[SMFTO_EOM]); 123764562Sgshapiro break; 123864562Sgshapiro 123964562Sgshapiro default: 124064562Sgshapiro if (tTd(64, 5)) 124164562Sgshapiro printf("X%s: %c unknown\n", 124264562Sgshapiro m->mf_name, fcode); 124364562Sgshapiro syserr("X%s: unknown filter timeout %c", 124464562Sgshapiro m->mf_name, fcode); 124564562Sgshapiro break; 124664562Sgshapiro } 124764562Sgshapiro p = delimptr; 124864562Sgshapiro } 124964562Sgshapiro} 125064562Sgshapiro/* 125164562Sgshapiro** MILTER_SET_OPTION -- set an individual milter option 125264562Sgshapiro** 125364562Sgshapiro** Parameters: 125464562Sgshapiro** name -- the name of the option. 125564562Sgshapiro** val -- the value of the option. 125664562Sgshapiro** sticky -- if set, don't let other setoptions override 125764562Sgshapiro** this value. 125864562Sgshapiro** 125964562Sgshapiro** Returns: 126064562Sgshapiro** none. 126164562Sgshapiro*/ 126264562Sgshapiro 126364562Sgshapiro/* set if Milter sub-option is stuck */ 126464562Sgshapirostatic BITMAP256 StickyMilterOpt; 126564562Sgshapiro 126664562Sgshapirostatic struct milteropt 126764562Sgshapiro{ 126864562Sgshapiro char *mo_name; /* long name of milter option */ 126964562Sgshapiro u_char mo_code; /* code for option */ 127064562Sgshapiro} MilterOptTab[] = 127164562Sgshapiro{ 127264562Sgshapiro# define MO_MACROS_CONNECT 0x01 127364562Sgshapiro { "macros.connect", MO_MACROS_CONNECT }, 127464562Sgshapiro# define MO_MACROS_HELO 0x02 127564562Sgshapiro { "macros.helo", MO_MACROS_HELO }, 127664562Sgshapiro# define MO_MACROS_ENVFROM 0x03 127764562Sgshapiro { "macros.envfrom", MO_MACROS_ENVFROM }, 127864562Sgshapiro# define MO_MACROS_ENVRCPT 0x04 127964562Sgshapiro { "macros.envrcpt", MO_MACROS_ENVRCPT }, 128064562Sgshapiro { NULL, 0 }, 128164562Sgshapiro}; 128264562Sgshapiro 128364562Sgshapirovoid 128464562Sgshapiromilter_set_option(name, val, sticky) 128564562Sgshapiro char *name; 128664562Sgshapiro char *val; 128764562Sgshapiro bool sticky; 128864562Sgshapiro{ 128964562Sgshapiro int nummac = 0; 129064562Sgshapiro register struct milteropt *mo; 129164562Sgshapiro char *p; 129264562Sgshapiro char **macros = NULL; 129364562Sgshapiro 129464562Sgshapiro if (tTd(37, 2) || tTd(64, 5)) 129564562Sgshapiro dprintf("milter_set_option(%s = %s)", name, val); 129664562Sgshapiro 129764562Sgshapiro for (mo = MilterOptTab; mo->mo_name != NULL; mo++) 129864562Sgshapiro { 129964562Sgshapiro if (strcasecmp(mo->mo_name, name) == 0) 130064562Sgshapiro break; 130164562Sgshapiro } 130264562Sgshapiro 130364562Sgshapiro if (mo->mo_name == NULL) 130464562Sgshapiro syserr("milter_set_option: invalid Milter option %s", name); 130564562Sgshapiro 130664562Sgshapiro /* 130764562Sgshapiro ** See if this option is preset for us. 130864562Sgshapiro */ 130964562Sgshapiro 131064562Sgshapiro if (!sticky && bitnset(mo->mo_code, StickyMilterOpt)) 131164562Sgshapiro { 131264562Sgshapiro if (tTd(37, 2) || tTd(64,5)) 131364562Sgshapiro dprintf(" (ignored)\n"); 131464562Sgshapiro return; 131564562Sgshapiro } 131664562Sgshapiro 131764562Sgshapiro if (tTd(37, 2) || tTd(64,5)) 131864562Sgshapiro dprintf("\n"); 131964562Sgshapiro 132064562Sgshapiro switch (mo->mo_code) 132164562Sgshapiro { 132264562Sgshapiro case MO_MACROS_CONNECT: 132364562Sgshapiro if (macros == NULL) 132464562Sgshapiro macros = MilterConnectMacros; 132564562Sgshapiro /* FALLTHROUGH */ 132664562Sgshapiro 132764562Sgshapiro case MO_MACROS_HELO: 132864562Sgshapiro if (macros == NULL) 132964562Sgshapiro macros = MilterHeloMacros; 133064562Sgshapiro /* FALLTHROUGH */ 133164562Sgshapiro 133264562Sgshapiro case MO_MACROS_ENVFROM: 133364562Sgshapiro if (macros == NULL) 133464562Sgshapiro macros = MilterEnvFromMacros; 133564562Sgshapiro /* FALLTHROUGH */ 133664562Sgshapiro 133764562Sgshapiro case MO_MACROS_ENVRCPT: 133864562Sgshapiro if (macros == NULL) 133964562Sgshapiro macros = MilterEnvRcptMacros; 134064562Sgshapiro 134164562Sgshapiro p = newstr(val); 134264562Sgshapiro while (*p != '\0') 134364562Sgshapiro { 134464562Sgshapiro char *macro; 134564562Sgshapiro 134664562Sgshapiro /* Skip leading commas, spaces */ 134764562Sgshapiro while (*p != '\0' && 134864562Sgshapiro (*p == ',' || (isascii(*p) && isspace(*p)))) 134964562Sgshapiro p++; 135064562Sgshapiro 135164562Sgshapiro if (*p == '\0') 135264562Sgshapiro break; 135364562Sgshapiro 135464562Sgshapiro /* Find end of macro */ 135564562Sgshapiro macro = p; 135664562Sgshapiro while (*p != '\0' && *p != ',' && 135764562Sgshapiro isascii(*p) && !isspace(*p)) 135864562Sgshapiro p++; 135964562Sgshapiro if (*p != '\0') 136064562Sgshapiro *p++ = '\0'; 136164562Sgshapiro 136264562Sgshapiro if (nummac >= MAXFILTERMACROS) 136364562Sgshapiro { 136464562Sgshapiro syserr("milter_set_option: too many macros in Milter.%s (max %d)", 136564562Sgshapiro name, MAXFILTERMACROS); 136664562Sgshapiro macros[nummac] = NULL; 136764562Sgshapiro break; 136864562Sgshapiro } 136964562Sgshapiro macros[nummac++] = macro; 137064562Sgshapiro } 137164562Sgshapiro macros[nummac] = NULL; 137264562Sgshapiro break; 137364562Sgshapiro 137464562Sgshapiro default: 137564562Sgshapiro syserr("milter_set_option: invalid Milter option %s", name); 137664562Sgshapiro break; 137764562Sgshapiro } 137864562Sgshapiro 137964562Sgshapiro if (sticky) 138064562Sgshapiro setbitn(mo->mo_code, StickyMilterOpt); 138164562Sgshapiro} 138264562Sgshapiro/* 138364562Sgshapiro** MILTER_REOPEN_DF -- open & truncate the df file (for replbody) 138464562Sgshapiro** 138564562Sgshapiro** Parameters: 138664562Sgshapiro** e -- current envelope. 138764562Sgshapiro** 138864562Sgshapiro** Returns: 138964562Sgshapiro** 0 if succesful, -1 otherwise 139064562Sgshapiro*/ 139164562Sgshapiro 139264562Sgshapirostatic int 139364562Sgshapiromilter_reopen_df(e) 139464562Sgshapiro ENVELOPE *e; 139564562Sgshapiro{ 139664562Sgshapiro char dfname[MAXPATHLEN]; 139764562Sgshapiro 139864562Sgshapiro (void) strlcpy(dfname, queuename(e, 'd'), sizeof dfname); 139964562Sgshapiro 140064562Sgshapiro /* 140164562Sgshapiro ** In SuperSafe mode, e->e_dfp is a read-only FP so 140264562Sgshapiro ** close and reopen writable (later close and reopen 140364562Sgshapiro ** read only again). 140464562Sgshapiro ** 140564562Sgshapiro ** In !SuperSafe mode, e->e_dfp still points at the 140664562Sgshapiro ** buffered file I/O descriptor, still open for writing 140764562Sgshapiro ** so there isn't as much work to do, just truncate it 140864562Sgshapiro ** and go. 140964562Sgshapiro */ 141064562Sgshapiro 141164562Sgshapiro if (SuperSafe) 141264562Sgshapiro { 141364562Sgshapiro /* close read-only df */ 141464562Sgshapiro if (bitset(EF_HAS_DF, e->e_flags) && e->e_dfp != NULL) 141564562Sgshapiro { 141664562Sgshapiro (void) fclose(e->e_dfp); 141764562Sgshapiro e->e_flags &= ~EF_HAS_DF; 141864562Sgshapiro } 141964562Sgshapiro 142064562Sgshapiro /* open writable */ 142164562Sgshapiro if ((e->e_dfp = fopen(dfname, "w+")) == NULL) 142264562Sgshapiro { 142364562Sgshapiro MILTER_DF_ERROR("milter_reopen_df: fopen %s: %s"); 142464562Sgshapiro return -1; 142564562Sgshapiro } 142664562Sgshapiro } 142764562Sgshapiro else if (e->e_dfp == NULL) 142864562Sgshapiro { 142964562Sgshapiro /* shouldn't happen */ 143064562Sgshapiro errno = ENOENT; 143164562Sgshapiro MILTER_DF_ERROR("milter_reopen_df: NULL e_dfp (%s: %s)"); 143264562Sgshapiro return -1; 143364562Sgshapiro } 143464562Sgshapiro return 0; 143564562Sgshapiro} 143664562Sgshapiro/* 143764562Sgshapiro** MILTER_RESET_DF -- re-open read-only the df file (for replbody) 143864562Sgshapiro** 143964562Sgshapiro** Parameters: 144064562Sgshapiro** e -- current envelope. 144164562Sgshapiro** 144264562Sgshapiro** Returns: 144364562Sgshapiro** 0 if succesful, -1 otherwise 144464562Sgshapiro*/ 144564562Sgshapiro 144664562Sgshapirostatic int 144764562Sgshapiromilter_reset_df(e) 144864562Sgshapiro ENVELOPE *e; 144964562Sgshapiro{ 145064562Sgshapiro int afd; 145164562Sgshapiro char dfname[MAXPATHLEN]; 145264562Sgshapiro 145364562Sgshapiro (void) strlcpy(dfname, queuename(e, 'd'), sizeof dfname); 145464562Sgshapiro 145564562Sgshapiro if (fflush(e->e_dfp) != 0 || ferror(e->e_dfp)) 145664562Sgshapiro { 145764562Sgshapiro MILTER_DF_ERROR("milter_reset_df: error writing/flushing %s: %s"); 145864562Sgshapiro return -1; 145964562Sgshapiro } 146064562Sgshapiro else if (!SuperSafe) 146164562Sgshapiro { 146264562Sgshapiro /* skip next few clauses */ 146364562Sgshapiro /* EMPTY */ 146464562Sgshapiro } 146564562Sgshapiro else if ((afd = fileno(e->e_dfp)) >= 0 && fsync(afd) < 0) 146664562Sgshapiro { 146764562Sgshapiro MILTER_DF_ERROR("milter_reset_df: error sync'ing %s: %s"); 146864562Sgshapiro return -1; 146964562Sgshapiro } 147064562Sgshapiro else if (fclose(e->e_dfp) < 0) 147164562Sgshapiro { 147264562Sgshapiro MILTER_DF_ERROR("milter_reset_df: error closing %s: %s"); 147364562Sgshapiro return -1; 147464562Sgshapiro } 147564562Sgshapiro else if ((e->e_dfp = fopen(dfname, "r")) == NULL) 147664562Sgshapiro { 147764562Sgshapiro MILTER_DF_ERROR("milter_reset_df: error reopening %s: %s"); 147864562Sgshapiro return -1; 147964562Sgshapiro } 148064562Sgshapiro else 148164562Sgshapiro e->e_flags |= EF_HAS_DF; 148264562Sgshapiro return 0; 148364562Sgshapiro} 148464562Sgshapiro/* 148564562Sgshapiro** MILTER_CAN_DELRCPTS -- can any milter filters delete recipients? 148664562Sgshapiro** 148764562Sgshapiro** Parameters: 148864562Sgshapiro** none 148964562Sgshapiro** 149064562Sgshapiro** Returns: 149164562Sgshapiro** TRUE if any filter deletes recipients, FALSE otherwise 149264562Sgshapiro*/ 149364562Sgshapiro 149464562Sgshapirobool 149564562Sgshapiromilter_can_delrcpts() 149664562Sgshapiro{ 149764562Sgshapiro bool can = FALSE; 149864562Sgshapiro int i; 149964562Sgshapiro 150064562Sgshapiro if (tTd(64, 10)) 150164562Sgshapiro dprintf("milter_can_delrcpts:"); 150264562Sgshapiro 150364562Sgshapiro for (i = 0; InputFilters[i] != NULL; i++) 150464562Sgshapiro { 150564562Sgshapiro struct milter *m = InputFilters[i]; 150664562Sgshapiro 150764562Sgshapiro if (bitset(SMFIF_DELRCPT, m->mf_fflags)) 150864562Sgshapiro { 150964562Sgshapiro can = TRUE; 151064562Sgshapiro break; 151164562Sgshapiro } 151264562Sgshapiro } 151364562Sgshapiro if (tTd(64, 10)) 151464562Sgshapiro dprintf("%s\n", can ? "TRUE" : "FALSE"); 151564562Sgshapiro 151664562Sgshapiro return can; 151764562Sgshapiro} 151864562Sgshapiro/* 151964562Sgshapiro** MILTER_QUIT_FILTER -- close down a single filter 152064562Sgshapiro** 152164562Sgshapiro** Parameters: 152264562Sgshapiro** m -- milter structure of filter to close down. 152364562Sgshapiro** e -- current envelope. 152464562Sgshapiro** 152564562Sgshapiro** Returns: 152664562Sgshapiro** none 152764562Sgshapiro*/ 152864562Sgshapiro 152964562Sgshapirostatic void 153064562Sgshapiromilter_quit_filter(m, e) 153164562Sgshapiro struct milter *m; 153264562Sgshapiro ENVELOPE *e; 153364562Sgshapiro{ 153464562Sgshapiro if (tTd(64, 10)) 153564562Sgshapiro dprintf("milter_quit_filter(%s)\n", m->mf_name); 153664562Sgshapiro 153764562Sgshapiro /* Never replace error state */ 153864562Sgshapiro if (m->mf_state == SMFS_ERROR) 153964562Sgshapiro return; 154064562Sgshapiro 154164562Sgshapiro if (m->mf_sock < 0 || 154264562Sgshapiro m->mf_state == SMFS_CLOSED || 154364562Sgshapiro m->mf_state == SMFS_READY) 154464562Sgshapiro { 154564562Sgshapiro m->mf_sock = -1; 154664562Sgshapiro m->mf_state = SMFS_CLOSED; 154764562Sgshapiro return; 154864562Sgshapiro } 154964562Sgshapiro 155064562Sgshapiro (void) milter_write(m, SMFIC_QUIT, (char *) NULL, 0, 155164562Sgshapiro m->mf_timeout[SMFTO_WRITE], e); 155264562Sgshapiro (void) close(m->mf_sock); 155364562Sgshapiro m->mf_sock = -1; 155464562Sgshapiro if (m->mf_state != SMFS_ERROR) 155564562Sgshapiro m->mf_state = SMFS_CLOSED; 155664562Sgshapiro} 155764562Sgshapiro/* 155864562Sgshapiro** MILTER_ABORT_FILTER -- tell filter to abort current message 155964562Sgshapiro** 156064562Sgshapiro** Parameters: 156164562Sgshapiro** m -- milter structure of filter to abort. 156264562Sgshapiro** e -- current envelope. 156364562Sgshapiro** 156464562Sgshapiro** Returns: 156564562Sgshapiro** none 156664562Sgshapiro*/ 156764562Sgshapiro 156864562Sgshapirostatic void 156964562Sgshapiromilter_abort_filter(m, e) 157064562Sgshapiro struct milter *m; 157164562Sgshapiro ENVELOPE *e; 157264562Sgshapiro{ 157364562Sgshapiro if (tTd(64, 10)) 157464562Sgshapiro dprintf("milter_abort_filter(%s)\n", m->mf_name); 157564562Sgshapiro 157664562Sgshapiro if (m->mf_sock < 0 || 157764562Sgshapiro m->mf_state != SMFS_INMSG) 157864562Sgshapiro return; 157964562Sgshapiro 158064562Sgshapiro (void) milter_write(m, SMFIC_ABORT, (char *) NULL, 0, 158164562Sgshapiro m->mf_timeout[SMFTO_WRITE], e); 158264562Sgshapiro if (m->mf_state != SMFS_ERROR) 158364562Sgshapiro m->mf_state = SMFS_DONE; 158464562Sgshapiro} 158564562Sgshapiro/* 158664562Sgshapiro** MILTER_SEND_MACROS -- provide macros to the filters 158764562Sgshapiro** 158864562Sgshapiro** Parameters: 158964562Sgshapiro** m -- milter to send macros to. 159064562Sgshapiro** macros -- macros to send for filter smfi_getsymval(). 159164562Sgshapiro** cmd -- which command the macros are associated with. 159264562Sgshapiro** e -- current envelope (for macro access). 159364562Sgshapiro** 159464562Sgshapiro** Returns: 159564562Sgshapiro** none 159664562Sgshapiro*/ 159764562Sgshapiro 159864562Sgshapirostatic void 159964562Sgshapiromilter_send_macros(m, macros, cmd, e) 160064562Sgshapiro struct milter *m; 160164562Sgshapiro char **macros; 160264562Sgshapiro char cmd; 160364562Sgshapiro ENVELOPE *e; 160464562Sgshapiro{ 160564562Sgshapiro int i; 160664562Sgshapiro int mid; 160764562Sgshapiro char *v; 160864562Sgshapiro char *buf, *bp; 160964562Sgshapiro ssize_t s; 161064562Sgshapiro 161164562Sgshapiro /* sanity check */ 161264562Sgshapiro if (macros == NULL || macros[0] == NULL) 161364562Sgshapiro return; 161464562Sgshapiro 161564562Sgshapiro /* put together data */ 161664562Sgshapiro s = 1; /* for the command character */ 161764562Sgshapiro for (i = 0; macros[i] != NULL; i++) 161864562Sgshapiro { 161964562Sgshapiro mid = macid(macros[i], NULL); 162064562Sgshapiro if (mid == '\0') 162164562Sgshapiro continue; 162264562Sgshapiro v = macvalue(mid, e); 162364562Sgshapiro if (v == NULL) 162464562Sgshapiro continue; 162564562Sgshapiro s += strlen(macros[i]) + 1 + strlen(v) + 1; 162664562Sgshapiro } 162764562Sgshapiro 162864562Sgshapiro buf = (char *)xalloc(s); 162964562Sgshapiro bp = buf; 163064562Sgshapiro *bp++ = cmd; 163164562Sgshapiro for (i = 0; macros[i] != NULL; i++) 163264562Sgshapiro { 163364562Sgshapiro mid = macid(macros[i], NULL); 163464562Sgshapiro if (mid == '\0') 163564562Sgshapiro continue; 163664562Sgshapiro v = macvalue(mid, e); 163764562Sgshapiro if (v == NULL) 163864562Sgshapiro continue; 163964562Sgshapiro 164064562Sgshapiro if (tTd(64, 10)) 164164562Sgshapiro dprintf("milter_send_macros(%s, %c): %s=%s\n", 164264562Sgshapiro m->mf_name, cmd, macros[i], v); 164364562Sgshapiro 164464562Sgshapiro (void) strlcpy(bp, macros[i], s - (bp - buf)); 164564562Sgshapiro bp += strlen(bp) + 1; 164664562Sgshapiro (void) strlcpy(bp, v, s - (bp - buf)); 164764562Sgshapiro bp += strlen(bp) + 1; 164864562Sgshapiro } 164964562Sgshapiro (void) milter_write(m, SMFIC_MACRO, buf, s, 165064562Sgshapiro m->mf_timeout[SMFTO_WRITE], e); 165164562Sgshapiro free(buf); 165264562Sgshapiro} 165364562Sgshapiro 165464562Sgshapiro/* 165564562Sgshapiro** MILTER_SEND_COMMAND -- send a command and return the response for a filter 165664562Sgshapiro** 165764562Sgshapiro** Parameters: 165864562Sgshapiro** m -- current milter filter 165964562Sgshapiro** command -- command to send. 166064562Sgshapiro** data -- optional command data. 166164562Sgshapiro** sz -- length of buf. 166264562Sgshapiro** e -- current envelope (for e->e_id). 166364562Sgshapiro** state -- return state word. 166464562Sgshapiro** 166564562Sgshapiro** Returns: 166664562Sgshapiro** response string (may be NULL) 166764562Sgshapiro*/ 166864562Sgshapiro 166964562Sgshapirostatic char * 167064562Sgshapiromilter_send_command(m, command, data, sz, e, state) 167164562Sgshapiro struct milter *m; 167264562Sgshapiro char command; 167364562Sgshapiro void *data; 167464562Sgshapiro ssize_t sz; 167564562Sgshapiro ENVELOPE *e; 167664562Sgshapiro char *state; 167764562Sgshapiro{ 167864562Sgshapiro char rcmd; 167964562Sgshapiro ssize_t rlen; 168064562Sgshapiro u_long skipflag; 168164562Sgshapiro char *defresponse; 168264562Sgshapiro char *response; 168364562Sgshapiro 168464562Sgshapiro if (tTd(64, 10)) 168564562Sgshapiro dprintf("milter_send_command(%s): cmd %c len %ld\n", 168664562Sgshapiro m->mf_name, (char) command, (long) sz); 168764562Sgshapiro 168864562Sgshapiro /* find skip flag and default failure */ 168964562Sgshapiro switch (command) 169064562Sgshapiro { 169164562Sgshapiro case SMFIC_CONNECT: 169264562Sgshapiro skipflag = SMFIP_NOCONNECT; 169364562Sgshapiro defresponse = "554 Command rejected"; 169464562Sgshapiro break; 169564562Sgshapiro 169664562Sgshapiro case SMFIC_HELO: 169764562Sgshapiro skipflag = SMFIP_NOHELO; 169864562Sgshapiro defresponse = "550 Command rejected"; 169964562Sgshapiro break; 170064562Sgshapiro 170164562Sgshapiro case SMFIC_MAIL: 170264562Sgshapiro skipflag = SMFIP_NOMAIL; 170364562Sgshapiro defresponse = "550 5.7.1 Command rejected"; 170464562Sgshapiro break; 170564562Sgshapiro 170664562Sgshapiro case SMFIC_RCPT: 170764562Sgshapiro skipflag = SMFIP_NORCPT; 170864562Sgshapiro defresponse = "550 5.7.1 Command rejected"; 170964562Sgshapiro break; 171064562Sgshapiro 171164562Sgshapiro case SMFIC_HEADER: 171264562Sgshapiro skipflag = SMFIP_NOHDRS; 171364562Sgshapiro defresponse = "550 5.7.1 Command rejected"; 171464562Sgshapiro break; 171564562Sgshapiro 171664562Sgshapiro case SMFIC_BODY: 171764562Sgshapiro skipflag = SMFIP_NOBODY; 171864562Sgshapiro defresponse = "554 5.7.1 Command rejected"; 171964562Sgshapiro break; 172064562Sgshapiro 172164562Sgshapiro case SMFIC_EOH: 172264562Sgshapiro skipflag = SMFIP_NOEOH; 172364562Sgshapiro defresponse = "550 5.7.1 Command rejected"; 172464562Sgshapiro break; 172564562Sgshapiro 172664562Sgshapiro case SMFIC_BODYEOB: 172764562Sgshapiro case SMFIC_OPTNEG: 172864562Sgshapiro case SMFIC_MACRO: 172964562Sgshapiro case SMFIC_ABORT: 173064562Sgshapiro case SMFIC_QUIT: 173164562Sgshapiro /* NOTE: not handled by milter_send_command() */ 173264562Sgshapiro /* FALLTHROUGH */ 173364562Sgshapiro 173464562Sgshapiro default: 173564562Sgshapiro skipflag = 0; 173664562Sgshapiro defresponse = "550 5.7.1 Command rejected"; 173764562Sgshapiro break; 173864562Sgshapiro } 173964562Sgshapiro 174064562Sgshapiro /* check if filter wants this command */ 174164562Sgshapiro if (skipflag != 0 && 174264562Sgshapiro bitset(skipflag, m->mf_pflags)) 174364562Sgshapiro return NULL; 174464562Sgshapiro 174564562Sgshapiro 174664562Sgshapiro (void) milter_write(m, command, data, sz, 174764562Sgshapiro m->mf_timeout[SMFTO_WRITE], e); 174864562Sgshapiro if (m->mf_state == SMFS_ERROR) 174964562Sgshapiro { 175064562Sgshapiro MILTER_CHECK_ERROR(/* EMPTY */;); 175164562Sgshapiro return NULL; 175264562Sgshapiro } 175364562Sgshapiro 175464562Sgshapiro response = milter_read(m, &rcmd, &rlen, 175564562Sgshapiro m->mf_timeout[SMFTO_READ], e); 175664562Sgshapiro if (m->mf_state == SMFS_ERROR) 175764562Sgshapiro { 175864562Sgshapiro MILTER_CHECK_ERROR(/* EMPTY */;); 175964562Sgshapiro return NULL; 176064562Sgshapiro } 176164562Sgshapiro 176264562Sgshapiro if (tTd(64, 10)) 176364562Sgshapiro dprintf("milter_send_command(%s): returned %c\n", 176464562Sgshapiro m->mf_name, (char) rcmd); 176564562Sgshapiro 176664562Sgshapiro switch (rcmd) 176764562Sgshapiro { 176864562Sgshapiro case SMFIR_REPLYCODE: 176964562Sgshapiro MILTER_CHECK_REPLYCODE(defresponse); 177064562Sgshapiro /* FALLTHROUGH */ 177164562Sgshapiro 177264562Sgshapiro case SMFIR_REJECT: 177364562Sgshapiro case SMFIR_DISCARD: 177464562Sgshapiro case SMFIR_TEMPFAIL: 177564562Sgshapiro *state = rcmd; 177664562Sgshapiro break; 177764562Sgshapiro 177864562Sgshapiro case SMFIR_ACCEPT: 177964562Sgshapiro /* this filter is done with message/connection */ 178064562Sgshapiro m->mf_state = SMFS_DONE; 178164562Sgshapiro break; 178264562Sgshapiro 178364562Sgshapiro case SMFIR_CONTINUE: 178464562Sgshapiro /* if MAIL command is ok, filter is in message state */ 178564562Sgshapiro if (command == SMFIC_MAIL) 178664562Sgshapiro m->mf_state = SMFS_INMSG; 178764562Sgshapiro break; 178864562Sgshapiro 178964562Sgshapiro default: 179064562Sgshapiro /* Invalid response to command */ 179164562Sgshapiro if (LogLevel > 0) 179264562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 179364562Sgshapiro "milter_send_command(%s): returned bogus response %c", 179464562Sgshapiro m->mf_name, rcmd); 179564562Sgshapiro milter_error(m); 179664562Sgshapiro break; 179764562Sgshapiro } 179864562Sgshapiro 179964562Sgshapiro if (*state != SMFIR_REPLYCODE && 180064562Sgshapiro response != NULL) 180164562Sgshapiro { 180264562Sgshapiro free(response); 180364562Sgshapiro response = NULL; 180464562Sgshapiro } 180564562Sgshapiro return response; 180664562Sgshapiro} 180764562Sgshapiro 180864562Sgshapiro/* 180964562Sgshapiro** MILTER_COMMAND -- send a command and return the response for each filter 181064562Sgshapiro** 181164562Sgshapiro** Parameters: 181264562Sgshapiro** command -- command to send. 181364562Sgshapiro** data -- optional command data. 181464562Sgshapiro** sz -- length of buf. 181564562Sgshapiro** macros -- macros to send for filter smfi_getsymval(). 181664562Sgshapiro** e -- current envelope (for macro access). 181764562Sgshapiro** state -- return state word. 181864562Sgshapiro** 181964562Sgshapiro** Returns: 182064562Sgshapiro** response string (may be NULL) 182164562Sgshapiro*/ 182264562Sgshapiro 182364562Sgshapirostatic char * 182464562Sgshapiromilter_command(command, data, sz, macros, e, state) 182564562Sgshapiro char command; 182664562Sgshapiro void *data; 182764562Sgshapiro ssize_t sz; 182864562Sgshapiro char **macros; 182964562Sgshapiro ENVELOPE *e; 183064562Sgshapiro char *state; 183164562Sgshapiro{ 183264562Sgshapiro int i; 183364562Sgshapiro char *response = NULL; 183464562Sgshapiro 183564562Sgshapiro if (tTd(64, 10)) 183664562Sgshapiro dprintf("milter_command: cmd %c len %ld\n", 183764562Sgshapiro (char) command, (long) sz); 183864562Sgshapiro 183964562Sgshapiro *state = SMFIR_CONTINUE; 184064562Sgshapiro for (i = 0; InputFilters[i] != NULL; i++) 184164562Sgshapiro { 184264562Sgshapiro struct milter *m = InputFilters[i]; 184364562Sgshapiro 184464562Sgshapiro /* sanity check */ 184564562Sgshapiro if (m->mf_sock < 0 || 184664562Sgshapiro (m->mf_state != SMFS_OPEN && m->mf_state != SMFS_INMSG)) 184764562Sgshapiro continue; 184864562Sgshapiro 184964562Sgshapiro /* send macros (regardless of whether we send command) */ 185064562Sgshapiro if (macros != NULL && macros[0] != NULL) 185164562Sgshapiro { 185264562Sgshapiro milter_send_macros(m, macros, command, e); 185364562Sgshapiro if (m->mf_state == SMFS_ERROR) 185464562Sgshapiro { 185564562Sgshapiro MILTER_CHECK_ERROR(continue); 185664562Sgshapiro break; 185764562Sgshapiro } 185864562Sgshapiro } 185964562Sgshapiro 186064562Sgshapiro response = milter_send_command(m, command, data, sz, e, state); 186164562Sgshapiro if (*state != SMFIR_CONTINUE) 186264562Sgshapiro break; 186364562Sgshapiro } 186464562Sgshapiro return response; 186564562Sgshapiro} 186664562Sgshapiro/* 186764562Sgshapiro** MILTER_NEGOTIATE -- get version and flags from filter 186864562Sgshapiro** 186964562Sgshapiro** Parameters: 187064562Sgshapiro** m -- milter filter structure. 187164562Sgshapiro** e -- current envelope. 187264562Sgshapiro** 187364562Sgshapiro** Returns: 187464562Sgshapiro** 0 on success, -1 otherwise 187564562Sgshapiro*/ 187664562Sgshapiro 187764562Sgshapirostatic int 187864562Sgshapiromilter_negotiate(m, e) 187964562Sgshapiro struct milter *m; 188064562Sgshapiro ENVELOPE *e; 188164562Sgshapiro{ 188264562Sgshapiro char rcmd; 188364562Sgshapiro mi_int32 fvers; 188464562Sgshapiro mi_int32 fflags; 188564562Sgshapiro mi_int32 pflags; 188664562Sgshapiro char *response; 188764562Sgshapiro ssize_t rlen; 188864562Sgshapiro char data[MILTER_OPTLEN]; 188964562Sgshapiro 189064562Sgshapiro /* sanity check */ 189164562Sgshapiro if (m->mf_sock < 0 || m->mf_state != SMFS_OPEN) 189264562Sgshapiro { 189364562Sgshapiro if (LogLevel > 0) 189464562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 189564562Sgshapiro "milter_negotiate(%s): impossible state", 189664562Sgshapiro m->mf_name); 189764562Sgshapiro milter_error(m); 189864562Sgshapiro return -1; 189964562Sgshapiro } 190064562Sgshapiro 190164562Sgshapiro fvers = htonl(SMFI_VERSION); 190264562Sgshapiro fflags = htonl(SMFI_CURR_ACTS); 190364562Sgshapiro pflags = htonl(SMFI_CURR_PROT); 190464562Sgshapiro (void) memcpy(data, (char *) &fvers, MILTER_LEN_BYTES); 190564562Sgshapiro (void) memcpy(data + MILTER_LEN_BYTES, 190664562Sgshapiro (char *) &fflags, MILTER_LEN_BYTES); 190764562Sgshapiro (void) memcpy(data + (MILTER_LEN_BYTES * 2), 190864562Sgshapiro (char *) &pflags, MILTER_LEN_BYTES); 190964562Sgshapiro (void) milter_write(m, SMFIC_OPTNEG, data, sizeof data, 191064562Sgshapiro m->mf_timeout[SMFTO_WRITE], e); 191164562Sgshapiro 191264562Sgshapiro if (m->mf_state == SMFS_ERROR) 191364562Sgshapiro return -1; 191464562Sgshapiro 191564562Sgshapiro response = milter_read(m, &rcmd, &rlen, m->mf_timeout[SMFTO_READ], e); 191664562Sgshapiro if (m->mf_state == SMFS_ERROR) 191764562Sgshapiro return -1; 191864562Sgshapiro 191964562Sgshapiro if (rcmd != SMFIC_OPTNEG) 192064562Sgshapiro { 192164562Sgshapiro if (tTd(64, 5)) 192264562Sgshapiro dprintf("milter_negotiate(%s): returned %c instead of %c\n", 192364562Sgshapiro m->mf_name, rcmd, SMFIC_OPTNEG); 192464562Sgshapiro if (LogLevel > 0) 192564562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 192664562Sgshapiro "milter_negotiate(%s): returned %c instead of %c", 192764562Sgshapiro m->mf_name, rcmd, SMFIC_OPTNEG); 192864562Sgshapiro if (response != NULL) 192964562Sgshapiro free(response); 193064562Sgshapiro milter_error(m); 193164562Sgshapiro return -1; 193264562Sgshapiro } 193364562Sgshapiro 193464562Sgshapiro /* Make sure we have enough bytes for the version */ 193564562Sgshapiro if (response == NULL || rlen < MILTER_LEN_BYTES) 193664562Sgshapiro { 193764562Sgshapiro if (tTd(64, 5)) 193864562Sgshapiro dprintf("milter_negotiate(%s): did not return valid info\n", 193964562Sgshapiro m->mf_name); 194064562Sgshapiro if (LogLevel > 0) 194164562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 194264562Sgshapiro "milter_negotiate(%s): did not return valid info", 194364562Sgshapiro m->mf_name); 194464562Sgshapiro if (response != NULL) 194564562Sgshapiro free(response); 194664562Sgshapiro milter_error(m); 194764562Sgshapiro return -1; 194864562Sgshapiro } 194964562Sgshapiro 195064562Sgshapiro /* extract information */ 195164562Sgshapiro (void) memcpy((char *) &fvers, response, MILTER_LEN_BYTES); 195264562Sgshapiro 195364562Sgshapiro /* Now make sure we have enough for the feature bitmap */ 195464562Sgshapiro if (rlen != MILTER_OPTLEN) 195564562Sgshapiro { 195664562Sgshapiro if (tTd(64, 5)) 195764562Sgshapiro dprintf("milter_negotiate(%s): did not return enough info\n", 195864562Sgshapiro m->mf_name); 195964562Sgshapiro if (LogLevel > 0) 196064562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 196164562Sgshapiro "milter_negotiate(%s): did not return enough info", 196264562Sgshapiro m->mf_name); 196364562Sgshapiro if (response != NULL) 196464562Sgshapiro free(response); 196564562Sgshapiro milter_error(m); 196664562Sgshapiro return -1; 196764562Sgshapiro } 196864562Sgshapiro 196964562Sgshapiro (void) memcpy((char *) &fflags, response + MILTER_LEN_BYTES, 197064562Sgshapiro MILTER_LEN_BYTES); 197164562Sgshapiro (void) memcpy((char *) &pflags, response + (MILTER_LEN_BYTES * 2), 197264562Sgshapiro MILTER_LEN_BYTES); 197364562Sgshapiro free(response); 197464562Sgshapiro response = NULL; 197564562Sgshapiro 197664562Sgshapiro m->mf_fvers = ntohl(fvers); 197764562Sgshapiro m->mf_fflags = ntohl(fflags); 197864562Sgshapiro m->mf_pflags = ntohl(pflags); 197964562Sgshapiro 198064562Sgshapiro /* check for version compatibility */ 198164562Sgshapiro if (m->mf_fvers == 1 || 198264562Sgshapiro m->mf_fvers > SMFI_VERSION) 198364562Sgshapiro { 198464562Sgshapiro if (tTd(64, 5)) 198564562Sgshapiro dprintf("milter_negotiate(%s): version %lu != MTA milter version %d\n", 198664562Sgshapiro m->mf_name, m->mf_fvers, SMFI_VERSION); 198764562Sgshapiro if (LogLevel > 0) 198864562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 198964562Sgshapiro "milter_negotiate(%s): version %ld != MTA milter version %d", 199064562Sgshapiro m->mf_name, m->mf_fvers, SMFI_VERSION); 199164562Sgshapiro milter_error(m); 199264562Sgshapiro return -1; 199364562Sgshapiro } 199464562Sgshapiro 199564562Sgshapiro /* check for filter feature mismatch */ 199664562Sgshapiro if ((m->mf_fflags & SMFI_CURR_ACTS) != m->mf_fflags) 199764562Sgshapiro { 199864562Sgshapiro if (tTd(64, 5)) 199964562Sgshapiro dprintf("milter_negotiate(%s): filter abilities 0x%lx != MTA milter abilities 0x%lx\n", 200064562Sgshapiro m->mf_name, m->mf_fflags, 200164562Sgshapiro (u_long) SMFI_CURR_ACTS); 200264562Sgshapiro if (LogLevel > 0) 200364562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 200464562Sgshapiro "milter_negotiate(%s): filter abilities 0x%lx != MTA milter abilities 0x%lx\n", 200564562Sgshapiro m->mf_name, m->mf_fflags, 200664562Sgshapiro (u_long) SMFI_CURR_ACTS); 200764562Sgshapiro milter_error(m); 200864562Sgshapiro return -1; 200964562Sgshapiro } 201064562Sgshapiro 201164562Sgshapiro /* check for protocol feature mismatch */ 201264562Sgshapiro if ((m->mf_pflags & SMFI_CURR_PROT) != m->mf_pflags) 201364562Sgshapiro { 201464562Sgshapiro if (tTd(64, 5)) 201564562Sgshapiro dprintf("milter_negotiate(%s): protocol abilities 0x%lx != MTA milter abilities 0x%lx\n", 201664562Sgshapiro m->mf_name, m->mf_pflags, 201764562Sgshapiro (u_long) SMFI_CURR_PROT); 201864562Sgshapiro if (LogLevel > 0) 201964562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 202064562Sgshapiro "milter_negotiate(%s): protocol abilities 0x%lx != MTA milter abilities 0x%lx\n", 202164562Sgshapiro m->mf_name, m->mf_pflags, 202264562Sgshapiro (u_long) SMFI_CURR_PROT); 202364562Sgshapiro milter_error(m); 202464562Sgshapiro return -1; 202564562Sgshapiro } 202664562Sgshapiro 202764562Sgshapiro if (tTd(64, 5)) 202864562Sgshapiro dprintf("milter_negotiate(%s): version %lu, fflags 0x%lx, pflags 0x%lx\n", 202964562Sgshapiro m->mf_name, m->mf_fvers, m->mf_fflags, m->mf_pflags); 203064562Sgshapiro return 0; 203164562Sgshapiro} 203264562Sgshapiro/* 203364562Sgshapiro** MILTER_PER_CONNECTION_CHECK -- checks on per-connection commands 203464562Sgshapiro** 203564562Sgshapiro** Reduce code duplication by putting these checks in one place 203664562Sgshapiro** 203764562Sgshapiro** Parameters: 203864562Sgshapiro** e -- current envelope. 203964562Sgshapiro** 204064562Sgshapiro** Returns: 204164562Sgshapiro** none 204264562Sgshapiro*/ 204364562Sgshapiro 204464562Sgshapirostatic void 204564562Sgshapiromilter_per_connection_check(e) 204664562Sgshapiro ENVELOPE *e; 204764562Sgshapiro{ 204864562Sgshapiro int i; 204964562Sgshapiro 205064562Sgshapiro /* see if we are done with any of the filters */ 205164562Sgshapiro for (i = 0; InputFilters[i] != NULL; i++) 205264562Sgshapiro { 205364562Sgshapiro struct milter *m = InputFilters[i]; 205464562Sgshapiro 205564562Sgshapiro if (m->mf_state == SMFS_DONE) 205664562Sgshapiro milter_quit_filter(m, e); 205764562Sgshapiro } 205864562Sgshapiro} 205964562Sgshapiro/* 206064562Sgshapiro** MILTER_ERROR -- Put a milter filter into error state 206164562Sgshapiro** 206264562Sgshapiro** Parameters: 206364562Sgshapiro** m -- the broken filter. 206464562Sgshapiro** 206564562Sgshapiro** Returns: 206664562Sgshapiro** none 206764562Sgshapiro*/ 206864562Sgshapiro 206964562Sgshapirostatic void 207064562Sgshapiromilter_error(m) 207164562Sgshapiro struct milter *m; 207264562Sgshapiro{ 207364562Sgshapiro /* 207464562Sgshapiro ** We could send a quit here but 207564562Sgshapiro ** we may have gotten here due to 207664562Sgshapiro ** an I/O error so we don't want 207764562Sgshapiro ** to try to make things worse. 207864562Sgshapiro */ 207964562Sgshapiro 208064562Sgshapiro if (m->mf_sock >= 0) 208164562Sgshapiro { 208264562Sgshapiro (void) close(m->mf_sock); 208364562Sgshapiro m->mf_sock = -1; 208464562Sgshapiro } 208564562Sgshapiro m->mf_state = SMFS_ERROR; 208664562Sgshapiro} 208764562Sgshapiro/* 208864562Sgshapiro** MILTER_HEADERS -- send headers to a single milter filter 208964562Sgshapiro** 209064562Sgshapiro** Parameters: 209164562Sgshapiro** m -- current filter. 209264562Sgshapiro** e -- current envelope. 209364562Sgshapiro** state -- return state from response. 209464562Sgshapiro** 209564562Sgshapiro** Returns: 209664562Sgshapiro** response string (may be NULL) 209764562Sgshapiro*/ 209864562Sgshapiro 209964562Sgshapirostatic char * 210064562Sgshapiromilter_headers(m, e, state) 210164562Sgshapiro struct milter *m; 210264562Sgshapiro ENVELOPE *e; 210364562Sgshapiro char *state; 210464562Sgshapiro{ 210564562Sgshapiro char *response = NULL; 210664562Sgshapiro HDR *h; 210764562Sgshapiro 210864562Sgshapiro for (h = e->e_header; h != NULL; h = h->h_link) 210964562Sgshapiro { 211064562Sgshapiro char *buf; 211164562Sgshapiro ssize_t s; 211264562Sgshapiro 211364562Sgshapiro /* don't send over deleted headers */ 211464562Sgshapiro if (h->h_value == NULL) 211564562Sgshapiro { 211664562Sgshapiro /* strip H_USER so not counted in milter_chgheader() */ 211764562Sgshapiro h->h_flags &= ~H_USER; 211864562Sgshapiro continue; 211964562Sgshapiro } 212064562Sgshapiro 212164562Sgshapiro /* skip auto-generated */ 212264562Sgshapiro if (!bitset(H_USER, h->h_flags)) 212364562Sgshapiro continue; 212464562Sgshapiro 212564562Sgshapiro if (tTd(64, 10)) 212664562Sgshapiro dprintf("milter_headers: %s: %s\n", 212764562Sgshapiro h->h_field, h->h_value); 212864562Sgshapiro 212964562Sgshapiro s = strlen(h->h_field) + 1 + 213064562Sgshapiro strlen(h->h_value) + 1; 213164562Sgshapiro buf = (char *) xalloc(s); 213264562Sgshapiro snprintf(buf, s, "%s%c%s", h->h_field, '\0', h->h_value); 213364562Sgshapiro 213464562Sgshapiro /* send it over */ 213564562Sgshapiro response = milter_send_command(m, SMFIC_HEADER, buf, 213664562Sgshapiro s, e, state); 213764562Sgshapiro free(buf); 213864562Sgshapiro if (m->mf_state == SMFS_ERROR || 213964562Sgshapiro m->mf_state == SMFS_DONE || 214064562Sgshapiro *state != SMFIR_CONTINUE) 214164562Sgshapiro break; 214264562Sgshapiro } 214364562Sgshapiro return response; 214464562Sgshapiro} 214564562Sgshapiro/* 214664562Sgshapiro** MILTER_BODY -- send the body to a filter 214764562Sgshapiro** 214864562Sgshapiro** Parameters: 214964562Sgshapiro** m -- current filter. 215064562Sgshapiro** e -- current envelope. 215164562Sgshapiro** state -- return state from response. 215264562Sgshapiro** 215364562Sgshapiro** Returns: 215464562Sgshapiro** response string (may be NULL) 215564562Sgshapiro*/ 215664562Sgshapiro 215764562Sgshapirostatic char * 215864562Sgshapiromilter_body(m, e, state) 215964562Sgshapiro struct milter *m; 216064562Sgshapiro ENVELOPE *e; 216164562Sgshapiro char *state; 216264562Sgshapiro{ 216364562Sgshapiro char bufchar = '\0'; 216464562Sgshapiro char prevchar = '\0'; 216564562Sgshapiro int c; 216664562Sgshapiro char *response = NULL; 216764562Sgshapiro char *bp; 216864562Sgshapiro char buf[MILTER_CHUNK_SIZE]; 216964562Sgshapiro 217064562Sgshapiro if (tTd(64, 10)) 217164562Sgshapiro dprintf("milter_body\n"); 217264562Sgshapiro 217364562Sgshapiro if (bfrewind(e->e_dfp) < 0) 217464562Sgshapiro { 217564562Sgshapiro ExitStat = EX_IOERR; 217664562Sgshapiro *state = SMFIR_TEMPFAIL; 217764562Sgshapiro syserr("milter_body: %s/df%s: rewind error", 217864562Sgshapiro qid_printqueue(e->e_queuedir), e->e_id); 217964562Sgshapiro return NULL; 218064562Sgshapiro } 218164562Sgshapiro 218264562Sgshapiro bp = buf; 218364562Sgshapiro while ((c = getc(e->e_dfp)) != EOF) 218464562Sgshapiro { 218564562Sgshapiro /* Change LF to CRLF */ 218664562Sgshapiro if (c == '\n') 218764562Sgshapiro { 218864562Sgshapiro /* Not a CRLF already? */ 218964562Sgshapiro if (prevchar != '\r') 219064562Sgshapiro { 219164562Sgshapiro /* Room for CR now? */ 219264562Sgshapiro if (bp + 2 > &buf[sizeof buf]) 219364562Sgshapiro { 219464562Sgshapiro /* No room, buffer LF */ 219564562Sgshapiro bufchar = c; 219664562Sgshapiro 219764562Sgshapiro /* and send CR now */ 219864562Sgshapiro c = '\r'; 219964562Sgshapiro } 220064562Sgshapiro else 220164562Sgshapiro { 220264562Sgshapiro /* Room to do it now */ 220364562Sgshapiro *bp++ = '\r'; 220464562Sgshapiro prevchar = '\r'; 220564562Sgshapiro } 220664562Sgshapiro } 220764562Sgshapiro } 220864562Sgshapiro *bp++ = (char) c; 220964562Sgshapiro prevchar = c; 221064562Sgshapiro if (bp >= &buf[sizeof buf]) 221164562Sgshapiro { 221264562Sgshapiro /* send chunk */ 221364562Sgshapiro response = milter_send_command(m, SMFIC_BODY, buf, 221464562Sgshapiro bp - buf, e, state); 221564562Sgshapiro bp = buf; 221664562Sgshapiro if (bufchar != '\0') 221764562Sgshapiro { 221864562Sgshapiro *bp++ = bufchar; 221964562Sgshapiro bufchar = '\0'; 222064562Sgshapiro prevchar = bufchar; 222164562Sgshapiro } 222264562Sgshapiro } 222364562Sgshapiro if (m->mf_state == SMFS_ERROR || 222464562Sgshapiro m->mf_state == SMFS_DONE || 222564562Sgshapiro *state != SMFIR_CONTINUE) 222664562Sgshapiro break; 222764562Sgshapiro } 222864562Sgshapiro 222964562Sgshapiro /* check for read errors */ 223064562Sgshapiro if (ferror(e->e_dfp)) 223164562Sgshapiro { 223264562Sgshapiro ExitStat = EX_IOERR; 223364562Sgshapiro if (*state == SMFIR_CONTINUE || 223464562Sgshapiro *state == SMFIR_ACCEPT) 223564562Sgshapiro { 223664562Sgshapiro *state = SMFIR_TEMPFAIL; 223764562Sgshapiro if (response != NULL) 223864562Sgshapiro { 223964562Sgshapiro free(response); 224064562Sgshapiro response = NULL; 224164562Sgshapiro } 224264562Sgshapiro } 224364562Sgshapiro syserr("milter_body: %s/df%s: read error", 224464562Sgshapiro qid_printqueue(e->e_queuedir), e->e_id); 224564562Sgshapiro return response; 224664562Sgshapiro } 224764562Sgshapiro 224864562Sgshapiro /* send last body chunk */ 224964562Sgshapiro if (bp > buf && 225064562Sgshapiro m->mf_state != SMFS_ERROR && 225164562Sgshapiro m->mf_state != SMFS_DONE && 225264562Sgshapiro *state == SMFIR_CONTINUE) 225364562Sgshapiro { 225464562Sgshapiro /* send chunk */ 225564562Sgshapiro response = milter_send_command(m, SMFIC_BODY, buf, bp - buf, 225664562Sgshapiro e, state); 225764562Sgshapiro bp = buf; 225864562Sgshapiro } 225964562Sgshapiro return response; 226064562Sgshapiro} 226164562Sgshapiro 226264562Sgshapiro/* 226364562Sgshapiro** Actions 226464562Sgshapiro*/ 226564562Sgshapiro 226664562Sgshapiro/* 226764562Sgshapiro** MILTER_ADDHEADER -- Add the supplied header to the message 226864562Sgshapiro** 226964562Sgshapiro** Parameters: 227064562Sgshapiro** response -- encoded form of header/value. 227164562Sgshapiro** rlen -- length of response. 227264562Sgshapiro** e -- current envelope. 227364562Sgshapiro** 227464562Sgshapiro** Returns: 227564562Sgshapiro** none 227664562Sgshapiro*/ 227764562Sgshapiro 227864562Sgshapirostatic void 227964562Sgshapiromilter_addheader(response, rlen, e) 228064562Sgshapiro char *response; 228164562Sgshapiro ssize_t rlen; 228264562Sgshapiro ENVELOPE *e; 228364562Sgshapiro{ 228464562Sgshapiro char *val; 228564562Sgshapiro 228664562Sgshapiro if (tTd(64, 10)) 228764562Sgshapiro dprintf("milter_addheader: "); 228864562Sgshapiro 228964562Sgshapiro /* sanity checks */ 229064562Sgshapiro if (response == NULL) 229164562Sgshapiro { 229264562Sgshapiro if (tTd(64, 10)) 229364562Sgshapiro dprintf("NULL response\n"); 229464562Sgshapiro return; 229564562Sgshapiro } 229664562Sgshapiro 229764562Sgshapiro if (rlen < 2 || strlen(response) + 1 >= (size_t) rlen) 229864562Sgshapiro { 229964562Sgshapiro if (tTd(64, 10)) 230064562Sgshapiro dprintf("didn't follow protocol (total len)\n"); 230164562Sgshapiro return; 230264562Sgshapiro } 230364562Sgshapiro 230464562Sgshapiro /* Find separating NUL */ 230564562Sgshapiro val = response + strlen(response) + 1; 230664562Sgshapiro 230764562Sgshapiro /* another sanity check */ 230864562Sgshapiro if (strlen(response) + strlen(val) + 2 != (size_t) rlen) 230964562Sgshapiro { 231064562Sgshapiro if (tTd(64, 10)) 231164562Sgshapiro dprintf("didn't follow protocol (part len)\n"); 231264562Sgshapiro return; 231364562Sgshapiro } 231464562Sgshapiro 231564562Sgshapiro if (*response == '\0') 231664562Sgshapiro { 231764562Sgshapiro if (tTd(64, 10)) 231864562Sgshapiro dprintf("empty field name\n"); 231964562Sgshapiro return; 232064562Sgshapiro } 232164562Sgshapiro 232264562Sgshapiro /* add to e_msgsize */ 232364562Sgshapiro e->e_msgsize += strlen(response) + 2 + strlen(val); 232464562Sgshapiro 232564562Sgshapiro if (tTd(64, 10)) 232664562Sgshapiro dprintf("Add %s: %s\n", response, val); 232764562Sgshapiro 232864562Sgshapiro addheader(newstr(response), val, H_USER, &e->e_header); 232964562Sgshapiro} 233064562Sgshapiro/* 233164562Sgshapiro** MILTER_CHANGEHEADER -- Change the supplied header in the message 233264562Sgshapiro** 233364562Sgshapiro** Parameters: 233464562Sgshapiro** response -- encoded form of header/index/value. 233564562Sgshapiro** rlen -- length of response. 233664562Sgshapiro** e -- current envelope. 233764562Sgshapiro** 233864562Sgshapiro** Returns: 233964562Sgshapiro** none 234064562Sgshapiro*/ 234164562Sgshapiro 234264562Sgshapirostatic void 234364562Sgshapiromilter_changeheader(response, rlen, e) 234464562Sgshapiro char *response; 234564562Sgshapiro ssize_t rlen; 234664562Sgshapiro ENVELOPE *e; 234764562Sgshapiro{ 234864562Sgshapiro mi_int32 i, index; 234964562Sgshapiro char *field, *val; 235064562Sgshapiro HDR *h; 235164562Sgshapiro 235264562Sgshapiro if (tTd(64, 10)) 235364562Sgshapiro dprintf("milter_changeheader: "); 235464562Sgshapiro 235564562Sgshapiro /* sanity checks */ 235664562Sgshapiro if (response == NULL) 235764562Sgshapiro { 235864562Sgshapiro if (tTd(64, 10)) 235964562Sgshapiro dprintf("NULL response\n"); 236064562Sgshapiro return; 236164562Sgshapiro } 236264562Sgshapiro 236364562Sgshapiro if (rlen < 2 || strlen(response) + 1 >= (size_t) rlen) 236464562Sgshapiro { 236564562Sgshapiro if (tTd(64, 10)) 236664562Sgshapiro dprintf("didn't follow protocol (total len)\n"); 236764562Sgshapiro return; 236864562Sgshapiro } 236964562Sgshapiro 237064562Sgshapiro /* Find separating NUL */ 237164562Sgshapiro (void) memcpy((char *) &i, response, MILTER_LEN_BYTES); 237264562Sgshapiro index = ntohl(i); 237364562Sgshapiro field = response + MILTER_LEN_BYTES; 237464562Sgshapiro val = field + strlen(field) + 1; 237564562Sgshapiro 237664562Sgshapiro /* another sanity check */ 237764562Sgshapiro if (MILTER_LEN_BYTES + strlen(field) + 1 + 237864562Sgshapiro strlen(val) + 1 != (size_t) rlen) 237964562Sgshapiro { 238064562Sgshapiro if (tTd(64, 10)) 238164562Sgshapiro dprintf("didn't follow protocol (part len)\n"); 238264562Sgshapiro return; 238364562Sgshapiro } 238464562Sgshapiro 238564562Sgshapiro if (*field == '\0') 238664562Sgshapiro { 238764562Sgshapiro if (tTd(64, 10)) 238864562Sgshapiro dprintf("empty field name\n"); 238964562Sgshapiro return; 239064562Sgshapiro } 239164562Sgshapiro 239264562Sgshapiro for (h = e->e_header; h != NULL; h = h->h_link) 239364562Sgshapiro { 239464562Sgshapiro if (bitset(H_USER, h->h_flags) && 239564562Sgshapiro strcasecmp(h->h_field, field) == 0 && 239664562Sgshapiro --index <= 0) 239764562Sgshapiro break; 239864562Sgshapiro } 239964562Sgshapiro 240064562Sgshapiro if (h == NULL) 240164562Sgshapiro { 240264562Sgshapiro if (*val == '\0') 240364562Sgshapiro { 240464562Sgshapiro if (tTd(64, 10)) 240564562Sgshapiro dprintf("Delete (noop) %s:\n", field); 240664562Sgshapiro } 240764562Sgshapiro else 240864562Sgshapiro { 240964562Sgshapiro /* treat modify value with no existing header as add */ 241064562Sgshapiro if (tTd(64, 10)) 241164562Sgshapiro dprintf("Add %s: %s\n", field, val); 241264562Sgshapiro 241364562Sgshapiro addheader(newstr(field), val, H_USER, &e->e_header); 241464562Sgshapiro } 241564562Sgshapiro return; 241664562Sgshapiro } 241764562Sgshapiro 241864562Sgshapiro if (tTd(64, 10)) 241964562Sgshapiro { 242064562Sgshapiro if (*val == '\0') 242164562Sgshapiro { 242264562Sgshapiro dprintf("Delete %s: %s\n", field, 242364562Sgshapiro h->h_value == NULL ? "<NULL>" : h->h_value); 242464562Sgshapiro } 242564562Sgshapiro else 242664562Sgshapiro { 242764562Sgshapiro dprintf("Change %s: from %s to %s\n", 242864562Sgshapiro field, 242964562Sgshapiro h->h_value == NULL ? "<NULL>" : h->h_value, 243064562Sgshapiro val); 243164562Sgshapiro } 243264562Sgshapiro } 243364562Sgshapiro 243464562Sgshapiro if (h->h_value != NULL) 243564562Sgshapiro { 243664562Sgshapiro e->e_msgsize -= strlen(h->h_value); 243764562Sgshapiro free(h->h_value); 243864562Sgshapiro } 243964562Sgshapiro 244064562Sgshapiro if (*val == '\0') 244164562Sgshapiro { 244264562Sgshapiro /* Remove "Field: " from message size */ 244364562Sgshapiro e->e_msgsize -= strlen(h->h_field) + 2; 244464562Sgshapiro h->h_value = NULL; 244564562Sgshapiro } 244664562Sgshapiro else 244764562Sgshapiro { 244864562Sgshapiro h->h_value = newstr(val); 244964562Sgshapiro e->e_msgsize += strlen(h->h_value); 245064562Sgshapiro } 245164562Sgshapiro} 245264562Sgshapiro/* 245364562Sgshapiro** MILTER_ADDRCPT -- Add the supplied recipient to the message 245464562Sgshapiro** 245564562Sgshapiro** Parameters: 245664562Sgshapiro** response -- encoded form of recipient address. 245764562Sgshapiro** rlen -- length of response. 245864562Sgshapiro** e -- current envelope. 245964562Sgshapiro** 246064562Sgshapiro** Returns: 246164562Sgshapiro** none 246264562Sgshapiro*/ 246364562Sgshapiro 246464562Sgshapirostatic void 246564562Sgshapiromilter_addrcpt(response, rlen, e) 246664562Sgshapiro char *response; 246764562Sgshapiro ssize_t rlen; 246864562Sgshapiro ENVELOPE *e; 246964562Sgshapiro{ 247064562Sgshapiro if (tTd(64, 10)) 247164562Sgshapiro dprintf("milter_addrcpt: "); 247264562Sgshapiro 247364562Sgshapiro /* sanity checks */ 247464562Sgshapiro if (response == NULL) 247564562Sgshapiro { 247664562Sgshapiro if (tTd(64, 10)) 247764562Sgshapiro dprintf("NULL response\n"); 247864562Sgshapiro return; 247964562Sgshapiro } 248064562Sgshapiro 248164562Sgshapiro if (*response == '\0' || 248264562Sgshapiro strlen(response) + 1 != (size_t) rlen) 248364562Sgshapiro { 248464562Sgshapiro if (tTd(64, 10)) 248564562Sgshapiro dprintf("didn't follow protocol (total len %d != rlen %d)\n", 248664562Sgshapiro strlen(response), rlen -1); 248764562Sgshapiro return; 248864562Sgshapiro } 248964562Sgshapiro 249064562Sgshapiro if (tTd(64, 10)) 249164562Sgshapiro dprintf("%s\n", response); 249264562Sgshapiro (void) sendtolist(response, NULLADDR, &e->e_sendqueue, 0, e); 249364562Sgshapiro return; 249464562Sgshapiro} 249564562Sgshapiro/* 249664562Sgshapiro** MILTER_DELRCPT -- Delete the supplied recipient from the message 249764562Sgshapiro** 249864562Sgshapiro** Parameters: 249964562Sgshapiro** response -- encoded form of recipient address. 250064562Sgshapiro** rlen -- length of response. 250164562Sgshapiro** e -- current envelope. 250264562Sgshapiro** 250364562Sgshapiro** Returns: 250464562Sgshapiro** none 250564562Sgshapiro*/ 250664562Sgshapiro 250764562Sgshapirostatic void 250864562Sgshapiromilter_delrcpt(response, rlen, e) 250964562Sgshapiro char *response; 251064562Sgshapiro ssize_t rlen; 251164562Sgshapiro ENVELOPE *e; 251264562Sgshapiro{ 251364562Sgshapiro if (tTd(64, 10)) 251464562Sgshapiro dprintf("milter_delrcpt: "); 251564562Sgshapiro 251664562Sgshapiro /* sanity checks */ 251764562Sgshapiro if (response == NULL) 251864562Sgshapiro { 251964562Sgshapiro if (tTd(64, 10)) 252064562Sgshapiro dprintf("NULL response\n"); 252164562Sgshapiro return; 252264562Sgshapiro } 252364562Sgshapiro 252464562Sgshapiro if (*response == '\0' || 252564562Sgshapiro strlen(response) + 1 != (size_t) rlen) 252664562Sgshapiro { 252764562Sgshapiro if (tTd(64, 10)) 252864562Sgshapiro dprintf("didn't follow protocol (total len)\n"); 252964562Sgshapiro return; 253064562Sgshapiro } 253164562Sgshapiro 253264562Sgshapiro if (tTd(64, 10)) 253364562Sgshapiro dprintf("%s\n", response); 253464562Sgshapiro (void) removefromlist(response, &e->e_sendqueue, e); 253564562Sgshapiro return; 253664562Sgshapiro} 253764562Sgshapiro/* 253864562Sgshapiro** MILTER_REPLBODY -- Replace the current df file with new body 253964562Sgshapiro** 254064562Sgshapiro** Parameters: 254164562Sgshapiro** response -- encoded form of new body. 254264562Sgshapiro** rlen -- length of response. 254364562Sgshapiro** newfilter -- if first time called by a new filter 254464562Sgshapiro** e -- current envelope. 254564562Sgshapiro** 254664562Sgshapiro** Returns: 254764562Sgshapiro** 0 upon success, -1 upon failure 254864562Sgshapiro*/ 254964562Sgshapiro 255064562Sgshapirostatic int 255164562Sgshapiromilter_replbody(response, rlen, newfilter, e) 255264562Sgshapiro char *response; 255364562Sgshapiro ssize_t rlen; 255464562Sgshapiro bool newfilter; 255564562Sgshapiro ENVELOPE *e; 255664562Sgshapiro{ 255764562Sgshapiro static char prevchar; 255864562Sgshapiro int i; 255964562Sgshapiro 256064562Sgshapiro if (tTd(64, 10)) 256164562Sgshapiro dprintf("milter_replbody\n"); 256264562Sgshapiro 256364562Sgshapiro /* If a new filter, reset previous character and truncate df */ 256464562Sgshapiro if (newfilter) 256564562Sgshapiro { 256664562Sgshapiro off_t prevsize = 0; 256764562Sgshapiro char dfname[MAXPATHLEN]; 256864562Sgshapiro 256964562Sgshapiro (void) strlcpy(dfname, queuename(e, 'd'), sizeof dfname); 257064562Sgshapiro 257164562Sgshapiro /* Reset prevchar */ 257264562Sgshapiro prevchar = '\0'; 257364562Sgshapiro 257464562Sgshapiro /* Get the current df information */ 257564562Sgshapiro if (bitset(EF_HAS_DF, e->e_flags) && e->e_dfp != NULL) 257664562Sgshapiro { 257764562Sgshapiro int afd; 257864562Sgshapiro struct stat st; 257964562Sgshapiro 258064562Sgshapiro afd = fileno(e->e_dfp); 258164562Sgshapiro if (afd > 0 && fstat(afd, &st) == 0) 258264562Sgshapiro prevsize = st.st_size; 258364562Sgshapiro } 258464562Sgshapiro 258564562Sgshapiro /* truncate current df file */ 258664562Sgshapiro if (bftruncate(e->e_dfp) < 0) 258764562Sgshapiro { 258864562Sgshapiro MILTER_DF_ERROR("milter_reopen_df: bftruncate %s: %s"); 258964562Sgshapiro return -1; 259064562Sgshapiro } 259164562Sgshapiro else 259264562Sgshapiro { 259364562Sgshapiro if (prevsize > e->e_msgsize) 259464562Sgshapiro e->e_msgsize = 0; 259564562Sgshapiro else 259664562Sgshapiro e->e_msgsize -= prevsize; 259764562Sgshapiro } 259864562Sgshapiro } 259964562Sgshapiro 260064562Sgshapiro if (response == NULL) 260164562Sgshapiro { 260264562Sgshapiro /* Flush the buffered '\r' */ 260364562Sgshapiro if (prevchar == '\r') 260464562Sgshapiro { 260564562Sgshapiro (void) putc(prevchar, e->e_dfp); 260664562Sgshapiro e->e_msgsize++; 260764562Sgshapiro } 260864562Sgshapiro return 0; 260964562Sgshapiro } 261064562Sgshapiro 261164562Sgshapiro for (i = 0; i < rlen; i++) 261264562Sgshapiro { 261364562Sgshapiro /* Buffered char from last chunk */ 261464562Sgshapiro if (i == 0 && prevchar == '\r') 261564562Sgshapiro { 261664562Sgshapiro /* Not CRLF, output prevchar */ 261764562Sgshapiro if (response[i] != '\n') 261864562Sgshapiro { 261964562Sgshapiro (void) putc(prevchar, e->e_dfp); 262064562Sgshapiro e->e_msgsize++; 262164562Sgshapiro } 262264562Sgshapiro prevchar = '\0'; 262364562Sgshapiro } 262464562Sgshapiro 262564562Sgshapiro /* Turn CRLF into LF */ 262664562Sgshapiro if (response[i] == '\r') 262764562Sgshapiro { 262864562Sgshapiro /* check if at end of chunk */ 262964562Sgshapiro if (i + 1 < rlen) 263064562Sgshapiro { 263164562Sgshapiro /* If LF, strip CR */ 263264562Sgshapiro if (response[i + 1] == '\n') 263364562Sgshapiro i++; 263464562Sgshapiro } 263564562Sgshapiro else 263664562Sgshapiro { 263764562Sgshapiro /* check next chunk */ 263864562Sgshapiro prevchar = '\r'; 263964562Sgshapiro continue; 264064562Sgshapiro } 264164562Sgshapiro } 264264562Sgshapiro (void) putc(response[i], e->e_dfp); 264364562Sgshapiro e->e_msgsize++; 264464562Sgshapiro } 264564562Sgshapiro return 0; 264664562Sgshapiro} 264764562Sgshapiro 264864562Sgshapiro/* 264964562Sgshapiro** MTA callouts 265064562Sgshapiro*/ 265164562Sgshapiro 265264562Sgshapiro/* 265364562Sgshapiro** MILTER_INIT -- open and negotiate with all of the filters 265464562Sgshapiro** 265564562Sgshapiro** Parameters: 265664562Sgshapiro** e -- current envelope. 265764562Sgshapiro** state -- return state from response. 265864562Sgshapiro** 265964562Sgshapiro** Returns: 266064562Sgshapiro** none 266164562Sgshapiro*/ 266264562Sgshapiro 266364562Sgshapiro/* ARGSUSED */ 266464562Sgshapirovoid 266564562Sgshapiromilter_init(e, state) 266664562Sgshapiro ENVELOPE *e; 266764562Sgshapiro char *state; 266864562Sgshapiro{ 266964562Sgshapiro int i; 267064562Sgshapiro 267164562Sgshapiro if (tTd(64, 10)) 267264562Sgshapiro dprintf("milter_init\n"); 267364562Sgshapiro 267464562Sgshapiro *state = SMFIR_CONTINUE; 267564562Sgshapiro for (i = 0; InputFilters[i] != NULL; i++) 267664562Sgshapiro { 267764562Sgshapiro struct milter *m = InputFilters[i]; 267864562Sgshapiro 267964562Sgshapiro m->mf_sock = milter_open(m, FALSE, e); 268064562Sgshapiro if (m->mf_state == SMFS_ERROR) 268164562Sgshapiro { 268264562Sgshapiro MILTER_CHECK_ERROR(continue); 268364562Sgshapiro break; 268464562Sgshapiro } 268564562Sgshapiro 268664562Sgshapiro if (m->mf_sock < 0 || 268764562Sgshapiro milter_negotiate(m, e) < 0 || 268864562Sgshapiro m->mf_state == SMFS_ERROR) 268964562Sgshapiro { 269064562Sgshapiro if (tTd(64, 5)) 269164562Sgshapiro dprintf("milter_init(%s): failed to %s\n", 269264562Sgshapiro m->mf_name, 269364562Sgshapiro m->mf_sock < 0 ? "open" : "negotiate"); 269464562Sgshapiro 269564562Sgshapiro /* if negotation failure, close socket */ 269664562Sgshapiro if (m->mf_sock >= 0) 269764562Sgshapiro { 269864562Sgshapiro (void) close(m->mf_sock); 269964562Sgshapiro m->mf_sock = -1; 270064562Sgshapiro } 270164562Sgshapiro milter_error(m); 270264562Sgshapiro if (m->mf_state == SMFS_ERROR) 270364562Sgshapiro { 270464562Sgshapiro MILTER_CHECK_ERROR(continue); 270564562Sgshapiro break; 270664562Sgshapiro } 270764562Sgshapiro } 270864562Sgshapiro } 270964562Sgshapiro 271064562Sgshapiro /* 271164562Sgshapiro ** If something temp/perm failed with one of the filters, 271264562Sgshapiro ** we won't be using any of them, so clear any existing 271364562Sgshapiro ** connections. 271464562Sgshapiro */ 271564562Sgshapiro 271664562Sgshapiro if (*state != SMFIR_CONTINUE) 271764562Sgshapiro milter_quit(e); 271864562Sgshapiro} 271964562Sgshapiro/* 272064562Sgshapiro** MILTER_CONNECT -- send connection info to milter filters 272164562Sgshapiro** 272264562Sgshapiro** Parameters: 272364562Sgshapiro** hostname -- hostname of remote machine. 272464562Sgshapiro** addr -- address of remote machine. 272564562Sgshapiro** e -- current envelope. 272664562Sgshapiro** state -- return state from response. 272764562Sgshapiro** 272864562Sgshapiro** Returns: 272964562Sgshapiro** response string (may be NULL) 273064562Sgshapiro*/ 273164562Sgshapiro 273264562Sgshapirochar * 273364562Sgshapiromilter_connect(hostname, addr, e, state) 273464562Sgshapiro char *hostname; 273564562Sgshapiro SOCKADDR addr; 273664562Sgshapiro ENVELOPE *e; 273764562Sgshapiro char *state; 273864562Sgshapiro{ 273964562Sgshapiro char family; 274064562Sgshapiro u_short port; 274164562Sgshapiro char *buf, *bp; 274264562Sgshapiro char *response; 274364562Sgshapiro char *sockinfo = NULL; 274464562Sgshapiro ssize_t s; 274564562Sgshapiro# if NETINET6 274664562Sgshapiro char buf6[INET6_ADDRSTRLEN]; 274764562Sgshapiro# endif /* NETINET6 */ 274864562Sgshapiro 274964562Sgshapiro if (tTd(64, 10)) 275064562Sgshapiro dprintf("milter_connect(%s)\n", hostname); 275164562Sgshapiro 275264562Sgshapiro /* gather data */ 275364562Sgshapiro switch (addr.sa.sa_family) 275464562Sgshapiro { 275564562Sgshapiro# if NETUNIX 275664562Sgshapiro case AF_UNIX: 275764562Sgshapiro family = SMFIA_UNIX; 275864562Sgshapiro port = htons(0); 275964562Sgshapiro sockinfo = addr.sunix.sun_path; 276064562Sgshapiro break; 276164562Sgshapiro# endif /* NETUNIX */ 276264562Sgshapiro 276364562Sgshapiro# if NETINET 276464562Sgshapiro case AF_INET: 276564562Sgshapiro family = SMFIA_INET; 276664562Sgshapiro port = htons(addr.sin.sin_port); 276764562Sgshapiro sockinfo = (char *) inet_ntoa(addr.sin.sin_addr); 276864562Sgshapiro break; 276964562Sgshapiro# endif /* NETINET */ 277064562Sgshapiro 277164562Sgshapiro# if NETINET6 277264562Sgshapiro case AF_INET6: 277364562Sgshapiro family = SMFIA_INET6; 277464562Sgshapiro port = htons(addr.sin6.sin6_port); 277564562Sgshapiro sockinfo = anynet_ntop(&addr.sin6.sin6_addr, buf6, 277664562Sgshapiro sizeof buf6); 277764562Sgshapiro if (sockinfo == NULL) 277864562Sgshapiro sockinfo = ""; 277964562Sgshapiro break; 278064562Sgshapiro# endif /* NETINET6 */ 278164562Sgshapiro 278264562Sgshapiro default: 278364562Sgshapiro family = SMFIA_UNKNOWN; 278464562Sgshapiro break; 278564562Sgshapiro } 278664562Sgshapiro 278764562Sgshapiro s = strlen(hostname) + 1 + sizeof(family); 278864562Sgshapiro if (family != SMFIA_UNKNOWN) 278964562Sgshapiro s += sizeof(port) + strlen(sockinfo) + 1; 279064562Sgshapiro 279164562Sgshapiro buf = (char *)xalloc(s); 279264562Sgshapiro bp = buf; 279364562Sgshapiro 279464562Sgshapiro /* put together data */ 279564562Sgshapiro (void) memcpy(bp, hostname, strlen(hostname)); 279664562Sgshapiro bp += strlen(hostname); 279764562Sgshapiro *bp++ = '\0'; 279864562Sgshapiro (void) memcpy(bp, &family, sizeof family); 279964562Sgshapiro bp += sizeof family; 280064562Sgshapiro if (family != SMFIA_UNKNOWN) 280164562Sgshapiro { 280264562Sgshapiro (void) memcpy(bp, &port, sizeof port); 280364562Sgshapiro bp += sizeof port; 280464562Sgshapiro 280564562Sgshapiro /* include trailing '\0' */ 280664562Sgshapiro (void) memcpy(bp, sockinfo, strlen(sockinfo) + 1); 280764562Sgshapiro } 280864562Sgshapiro 280964562Sgshapiro response = milter_command(SMFIC_CONNECT, buf, s, 281064562Sgshapiro MilterConnectMacros, e, state); 281164562Sgshapiro free(buf); 281264562Sgshapiro 281364562Sgshapiro /* 281464562Sgshapiro ** If this message connection is done for, 281564562Sgshapiro ** close the filters. 281664562Sgshapiro */ 281764562Sgshapiro 281864562Sgshapiro if (*state != SMFIR_CONTINUE) 281964562Sgshapiro milter_quit(e); 282064562Sgshapiro else 282164562Sgshapiro milter_per_connection_check(e); 282264562Sgshapiro 282364562Sgshapiro /* 282464562Sgshapiro ** SMFIR_REPLYCODE can't work with connect due to 282564562Sgshapiro ** the requirements of SMTP. Therefore, ignore the 282664562Sgshapiro ** reply code text but keep the state it would reflect. 282764562Sgshapiro */ 282864562Sgshapiro 282964562Sgshapiro if (*state == SMFIR_REPLYCODE) 283064562Sgshapiro { 283164562Sgshapiro if (response != NULL && 283264562Sgshapiro *response == '4') 283364562Sgshapiro *state = SMFIR_TEMPFAIL; 283464562Sgshapiro else 283564562Sgshapiro *state = SMFIR_REJECT; 283664562Sgshapiro if (response != NULL) 283764562Sgshapiro { 283864562Sgshapiro free(response); 283964562Sgshapiro response = NULL; 284064562Sgshapiro } 284164562Sgshapiro } 284264562Sgshapiro return response; 284364562Sgshapiro} 284464562Sgshapiro/* 284564562Sgshapiro** MILTER_HELO -- send SMTP HELO/EHLO command info to milter filters 284664562Sgshapiro** 284764562Sgshapiro** Parameters: 284864562Sgshapiro** helo -- argument to SMTP HELO/EHLO command. 284964562Sgshapiro** e -- current envelope. 285064562Sgshapiro** state -- return state from response. 285164562Sgshapiro** 285264562Sgshapiro** Returns: 285364562Sgshapiro** response string (may be NULL) 285464562Sgshapiro*/ 285564562Sgshapiro 285664562Sgshapirochar * 285764562Sgshapiromilter_helo(helo, e, state) 285864562Sgshapiro char *helo; 285964562Sgshapiro ENVELOPE *e; 286064562Sgshapiro char *state; 286164562Sgshapiro{ 286264562Sgshapiro char *response; 286364562Sgshapiro 286464562Sgshapiro if (tTd(64, 10)) 286564562Sgshapiro dprintf("milter_helo(%s)\n", helo); 286664562Sgshapiro 286764562Sgshapiro response = milter_command(SMFIC_HELO, helo, strlen(helo) + 1, 286864562Sgshapiro MilterHeloMacros, e, state); 286964562Sgshapiro milter_per_connection_check(e); 287064562Sgshapiro return response; 287164562Sgshapiro} 287264562Sgshapiro/* 287364562Sgshapiro** MILTER_ENVFROM -- send SMTP MAIL command info to milter filters 287464562Sgshapiro** 287564562Sgshapiro** Parameters: 287664562Sgshapiro** args -- SMTP MAIL command args (args[0] == sender). 287764562Sgshapiro** e -- current envelope. 287864562Sgshapiro** state -- return state from response. 287964562Sgshapiro** 288064562Sgshapiro** Returns: 288164562Sgshapiro** response string (may be NULL) 288264562Sgshapiro*/ 288364562Sgshapiro 288464562Sgshapirochar * 288564562Sgshapiromilter_envfrom(args, e, state) 288664562Sgshapiro char **args; 288764562Sgshapiro ENVELOPE *e; 288864562Sgshapiro char *state; 288964562Sgshapiro{ 289064562Sgshapiro int i; 289164562Sgshapiro char *buf, *bp; 289264562Sgshapiro char *response; 289364562Sgshapiro ssize_t s; 289464562Sgshapiro 289564562Sgshapiro if (tTd(64, 10)) 289664562Sgshapiro { 289764562Sgshapiro dprintf("milter_envfrom:"); 289864562Sgshapiro for (i = 0; args[i] != NULL; i++) 289964562Sgshapiro dprintf(" %s", args[i]); 290064562Sgshapiro dprintf("\n"); 290164562Sgshapiro } 290264562Sgshapiro 290364562Sgshapiro /* sanity check */ 290464562Sgshapiro if (args[0] == NULL) 290564562Sgshapiro { 290664562Sgshapiro *state = SMFIR_REJECT; 290764562Sgshapiro return NULL; 290864562Sgshapiro } 290964562Sgshapiro 291064562Sgshapiro /* new message, so ... */ 291164562Sgshapiro for (i = 0; InputFilters[i] != NULL; i++) 291264562Sgshapiro { 291364562Sgshapiro struct milter *m = InputFilters[i]; 291464562Sgshapiro 291564562Sgshapiro switch (m->mf_state) 291664562Sgshapiro { 291764562Sgshapiro case SMFS_INMSG: 291864562Sgshapiro /* abort in message filters */ 291964562Sgshapiro milter_abort_filter(m, e); 292064562Sgshapiro /* FALLTHROUGH */ 292164562Sgshapiro 292264562Sgshapiro case SMFS_DONE: 292364562Sgshapiro /* reset done filters */ 292464562Sgshapiro m->mf_state = SMFS_OPEN; 292564562Sgshapiro break; 292664562Sgshapiro } 292764562Sgshapiro } 292864562Sgshapiro 292964562Sgshapiro /* put together data */ 293064562Sgshapiro s = 0; 293164562Sgshapiro for (i = 0; args[i] != NULL; i++) 293264562Sgshapiro s += strlen(args[i]) + 1; 293364562Sgshapiro buf = (char *)xalloc(s); 293464562Sgshapiro bp = buf; 293564562Sgshapiro for (i = 0; args[i] != NULL; i++) 293664562Sgshapiro { 293764562Sgshapiro (void) strlcpy(bp, args[i], s - (bp - buf)); 293864562Sgshapiro bp += strlen(bp) + 1; 293964562Sgshapiro } 294064562Sgshapiro 294164562Sgshapiro /* send it over */ 294264562Sgshapiro response = milter_command(SMFIC_MAIL, buf, s, 294364562Sgshapiro MilterEnvFromMacros, e, state); 294464562Sgshapiro free(buf); 294564562Sgshapiro 294664562Sgshapiro /* 294764562Sgshapiro ** If filter rejects/discards a per message command, 294864562Sgshapiro ** abort the other filters since we are done with the 294964562Sgshapiro ** current message. 295064562Sgshapiro */ 295164562Sgshapiro 295264562Sgshapiro MILTER_CHECK_DONE_MSG(); 295364562Sgshapiro return response; 295464562Sgshapiro} 295564562Sgshapiro/* 295664562Sgshapiro** MILTER_ENVRCPT -- send SMTP RCPT command info to milter filters 295764562Sgshapiro** 295864562Sgshapiro** Parameters: 295964562Sgshapiro** args -- SMTP MAIL command args (args[0] == recipient). 296064562Sgshapiro** e -- current envelope. 296164562Sgshapiro** state -- return state from response. 296264562Sgshapiro** 296364562Sgshapiro** Returns: 296464562Sgshapiro** response string (may be NULL) 296564562Sgshapiro*/ 296664562Sgshapiro 296764562Sgshapirochar * 296864562Sgshapiromilter_envrcpt(args, e, state) 296964562Sgshapiro char **args; 297064562Sgshapiro ENVELOPE *e; 297164562Sgshapiro char *state; 297264562Sgshapiro{ 297364562Sgshapiro int i; 297464562Sgshapiro char *buf, *bp; 297564562Sgshapiro char *response; 297664562Sgshapiro ssize_t s; 297764562Sgshapiro 297864562Sgshapiro if (tTd(64, 10)) 297964562Sgshapiro { 298064562Sgshapiro dprintf("milter_envrcpt:"); 298164562Sgshapiro for (i = 0; args[i] != NULL; i++) 298264562Sgshapiro dprintf(" %s", args[i]); 298364562Sgshapiro dprintf("\n"); 298464562Sgshapiro } 298564562Sgshapiro 298664562Sgshapiro /* sanity check */ 298764562Sgshapiro if (args[0] == NULL) 298864562Sgshapiro { 298964562Sgshapiro *state = SMFIR_REJECT; 299064562Sgshapiro return NULL; 299164562Sgshapiro } 299264562Sgshapiro 299364562Sgshapiro /* put together data */ 299464562Sgshapiro s = 0; 299564562Sgshapiro for (i = 0; args[i] != NULL; i++) 299664562Sgshapiro s += strlen(args[i]) + 1; 299764562Sgshapiro buf = (char *)xalloc(s); 299864562Sgshapiro bp = buf; 299964562Sgshapiro for (i = 0; args[i] != NULL; i++) 300064562Sgshapiro { 300164562Sgshapiro (void) strlcpy(bp, args[i], s - (bp - buf)); 300264562Sgshapiro bp += strlen(bp) + 1; 300364562Sgshapiro } 300464562Sgshapiro 300564562Sgshapiro /* send it over */ 300664562Sgshapiro response = milter_command(SMFIC_RCPT, buf, s, 300764562Sgshapiro MilterEnvRcptMacros, e, state); 300864562Sgshapiro free(buf); 300964562Sgshapiro return response; 301064562Sgshapiro} 301164562Sgshapiro/* 301264562Sgshapiro** MILTER_DATA -- send message headers/body and gather final message results 301364562Sgshapiro** 301464562Sgshapiro** Parameters: 301564562Sgshapiro** e -- current envelope. 301664562Sgshapiro** state -- return state from response. 301764562Sgshapiro** 301864562Sgshapiro** Returns: 301964562Sgshapiro** response string (may be NULL) 302064562Sgshapiro** 302164562Sgshapiro** Side effects: 302264562Sgshapiro** - Uses e->e_dfp for access to the body 302364562Sgshapiro** - Can call the various milter action routines to 302464562Sgshapiro** modify the envelope or message. 302564562Sgshapiro*/ 302664562Sgshapiro 302764562Sgshapiro# define MILTER_CHECK_RESULTS() \ 302864562Sgshapiro if (*state == SMFIR_ACCEPT || \ 302964562Sgshapiro m->mf_state == SMFS_DONE || \ 303064562Sgshapiro m->mf_state == SMFS_ERROR) \ 303164562Sgshapiro { \ 303264562Sgshapiro if (m->mf_state != SMFS_ERROR) \ 303364562Sgshapiro m->mf_state = SMFS_DONE; \ 303464562Sgshapiro continue; /* to next filter */ \ 303564562Sgshapiro } \ 303664562Sgshapiro if (*state != SMFIR_CONTINUE) \ 303764562Sgshapiro { \ 303864562Sgshapiro m->mf_state = SMFS_DONE; \ 303964562Sgshapiro goto finishup; \ 304064562Sgshapiro } 304164562Sgshapiro 304264562Sgshapirochar * 304364562Sgshapiromilter_data(e, state) 304464562Sgshapiro ENVELOPE *e; 304564562Sgshapiro char *state; 304664562Sgshapiro{ 304764562Sgshapiro bool replbody = FALSE; /* milter_replbody() called? */ 304864562Sgshapiro bool replfailed = FALSE; /* milter_replbody() failed? */ 304964562Sgshapiro bool rewind = FALSE; /* rewind df file? */ 305064562Sgshapiro bool dfopen = FALSE; /* df open for writing? */ 305164562Sgshapiro bool newfilter; /* reset on each new filter */ 305264562Sgshapiro char rcmd; 305364562Sgshapiro int i; 305464562Sgshapiro int save_errno; 305564562Sgshapiro char *response = NULL; 305664562Sgshapiro time_t eomsent; 305764562Sgshapiro ssize_t rlen; 305864562Sgshapiro 305964562Sgshapiro if (tTd(64, 10)) 306064562Sgshapiro dprintf("milter_data\n"); 306164562Sgshapiro 306264562Sgshapiro *state = SMFIR_CONTINUE; 306364562Sgshapiro 306464562Sgshapiro /* 306564562Sgshapiro ** XXX: Should actually send body chunks to each filter 306664562Sgshapiro ** a chunk at a time instead of sending the whole body to 306764562Sgshapiro ** each filter in turn. However, only if the filters don't 306864562Sgshapiro ** change the body. 306964562Sgshapiro */ 307064562Sgshapiro 307164562Sgshapiro for (i = 0; InputFilters[i] != NULL; i++) 307264562Sgshapiro { 307364562Sgshapiro struct milter *m = InputFilters[i]; 307464562Sgshapiro 307564562Sgshapiro if (*state != SMFIR_CONTINUE && 307664562Sgshapiro *state != SMFIR_ACCEPT) 307764562Sgshapiro { 307864562Sgshapiro /* 307964562Sgshapiro ** A previous filter has dealt with the message, 308064562Sgshapiro ** safe to stop processing the filters. 308164562Sgshapiro */ 308264562Sgshapiro 308364562Sgshapiro break; 308464562Sgshapiro } 308564562Sgshapiro 308664562Sgshapiro /* Now reset state for later evaluation */ 308764562Sgshapiro *state = SMFIR_CONTINUE; 308864562Sgshapiro newfilter = TRUE; 308964562Sgshapiro 309064562Sgshapiro /* sanity checks */ 309164562Sgshapiro if (m->mf_sock < 0 || 309264562Sgshapiro (m->mf_state != SMFS_OPEN && m->mf_state != SMFS_INMSG)) 309364562Sgshapiro continue; 309464562Sgshapiro 309564562Sgshapiro m->mf_state = SMFS_INMSG; 309664562Sgshapiro 309764562Sgshapiro /* check if filter wants the headers */ 309864562Sgshapiro if (!bitset(SMFIP_NOHDRS, m->mf_pflags)) 309964562Sgshapiro { 310064562Sgshapiro response = milter_headers(m, e, state); 310164562Sgshapiro MILTER_CHECK_RESULTS(); 310264562Sgshapiro } 310364562Sgshapiro 310464562Sgshapiro /* check if filter wants EOH */ 310564562Sgshapiro if (!bitset(SMFIP_NOEOH, m->mf_pflags)) 310664562Sgshapiro { 310764562Sgshapiro if (tTd(64, 10)) 310864562Sgshapiro dprintf("milter_data: eoh\n"); 310964562Sgshapiro 311064562Sgshapiro /* send it over */ 311164562Sgshapiro response = milter_send_command(m, SMFIC_EOH, NULL, 0, 311264562Sgshapiro e, state); 311364562Sgshapiro MILTER_CHECK_RESULTS(); 311464562Sgshapiro } 311564562Sgshapiro 311664562Sgshapiro /* check if filter wants the body */ 311764562Sgshapiro if (!bitset(SMFIP_NOBODY, m->mf_pflags) && 311864562Sgshapiro e->e_dfp != NULL) 311964562Sgshapiro { 312064562Sgshapiro rewind = TRUE; 312164562Sgshapiro response = milter_body(m, e, state); 312264562Sgshapiro MILTER_CHECK_RESULTS(); 312364562Sgshapiro } 312464562Sgshapiro 312564562Sgshapiro /* send the final body chunk */ 312664562Sgshapiro (void) milter_write(m, SMFIC_BODYEOB, NULL, 0, 312764562Sgshapiro m->mf_timeout[SMFTO_WRITE], e); 312864562Sgshapiro 312964562Sgshapiro /* Get time EOM sent for timeout */ 313064562Sgshapiro eomsent = curtime(); 313164562Sgshapiro 313264562Sgshapiro /* deal with the possibility of multiple responses */ 313364562Sgshapiro while (*state == SMFIR_CONTINUE) 313464562Sgshapiro { 313564562Sgshapiro /* Check total timeout from EOM to final ACK/NAK */ 313664562Sgshapiro if (m->mf_timeout[SMFTO_EOM] > 0 && 313764562Sgshapiro curtime() - eomsent >= m->mf_timeout[SMFTO_EOM]) 313864562Sgshapiro { 313964562Sgshapiro if (tTd(64, 5)) 314064562Sgshapiro dprintf("milter_data(%s): EOM ACK/NAK timeout\n", 314164562Sgshapiro m->mf_name); 314264562Sgshapiro if (LogLevel > 0) 314364562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 314464562Sgshapiro "milter_data(%s): EOM ACK/NAK timeout\n", 314564562Sgshapiro m->mf_name); 314664562Sgshapiro milter_error(m); 314764562Sgshapiro MILTER_CHECK_ERROR(continue); 314864562Sgshapiro break; 314964562Sgshapiro } 315064562Sgshapiro 315164562Sgshapiro response = milter_read(m, &rcmd, &rlen, 315264562Sgshapiro m->mf_timeout[SMFTO_READ], e); 315364562Sgshapiro if (m->mf_state == SMFS_ERROR) 315464562Sgshapiro break; 315564562Sgshapiro 315664562Sgshapiro if (tTd(64, 10)) 315764562Sgshapiro dprintf("milter_data(%s): state %c\n", 315864562Sgshapiro m->mf_name, (char) rcmd); 315964562Sgshapiro 316064562Sgshapiro switch (rcmd) 316164562Sgshapiro { 316264562Sgshapiro case SMFIR_REPLYCODE: 316364562Sgshapiro MILTER_CHECK_REPLYCODE("554 5.7.1 Command rejected"); 316464562Sgshapiro *state = rcmd; 316564562Sgshapiro m->mf_state = SMFS_DONE; 316664562Sgshapiro break; 316764562Sgshapiro 316864562Sgshapiro case SMFIR_REJECT: 316964562Sgshapiro case SMFIR_DISCARD: 317064562Sgshapiro case SMFIR_TEMPFAIL: 317164562Sgshapiro *state = rcmd; 317264562Sgshapiro m->mf_state = SMFS_DONE; 317364562Sgshapiro break; 317464562Sgshapiro 317564562Sgshapiro case SMFIR_CONTINUE: 317664562Sgshapiro case SMFIR_ACCEPT: 317764562Sgshapiro /* this filter is done with message */ 317864562Sgshapiro if (replfailed) 317964562Sgshapiro *state = SMFIR_TEMPFAIL; 318064562Sgshapiro else 318164562Sgshapiro *state = SMFIR_ACCEPT; 318264562Sgshapiro m->mf_state = SMFS_DONE; 318364562Sgshapiro break; 318464562Sgshapiro 318564562Sgshapiro case SMFIR_PROGRESS: 318664562Sgshapiro break; 318764562Sgshapiro 318864562Sgshapiro case SMFIR_ADDHEADER: 318964562Sgshapiro if (!bitset(SMFIF_ADDHDRS, m->mf_fflags)) 319064562Sgshapiro { 319164562Sgshapiro if (LogLevel > 9) 319264562Sgshapiro sm_syslog(LOG_WARNING, e->e_id, 319364562Sgshapiro "milter_data(%s): lied about adding headers, honoring request anyway", 319464562Sgshapiro m->mf_name); 319564562Sgshapiro } 319664562Sgshapiro milter_addheader(response, rlen, e); 319764562Sgshapiro break; 319864562Sgshapiro 319964562Sgshapiro case SMFIR_CHGHEADER: 320064562Sgshapiro if (!bitset(SMFIF_CHGHDRS, m->mf_fflags)) 320164562Sgshapiro { 320264562Sgshapiro if (LogLevel > 9) 320364562Sgshapiro sm_syslog(LOG_WARNING, e->e_id, 320464562Sgshapiro "milter_data(%s): lied about changing headers, honoring request anyway", 320564562Sgshapiro m->mf_name); 320664562Sgshapiro } 320764562Sgshapiro milter_changeheader(response, rlen, e); 320864562Sgshapiro break; 320964562Sgshapiro 321064562Sgshapiro case SMFIR_ADDRCPT: 321164562Sgshapiro if (!bitset(SMFIF_ADDRCPT, m->mf_fflags)) 321264562Sgshapiro { 321364562Sgshapiro if (LogLevel > 9) 321464562Sgshapiro sm_syslog(LOG_WARNING, e->e_id, 321564562Sgshapiro "milter_data(%s) lied about adding recipients, honoring request anyway", 321664562Sgshapiro m->mf_name); 321764562Sgshapiro } 321864562Sgshapiro milter_addrcpt(response, rlen, e); 321964562Sgshapiro break; 322064562Sgshapiro 322164562Sgshapiro case SMFIR_DELRCPT: 322264562Sgshapiro if (!bitset(SMFIF_DELRCPT, m->mf_fflags)) 322364562Sgshapiro { 322464562Sgshapiro if (LogLevel > 9) 322564562Sgshapiro sm_syslog(LOG_WARNING, e->e_id, 322664562Sgshapiro "milter_data(%s): lied about removing recipients, honoring request anyway", 322764562Sgshapiro m->mf_name); 322864562Sgshapiro } 322964562Sgshapiro milter_delrcpt(response, rlen, e); 323064562Sgshapiro break; 323164562Sgshapiro 323264562Sgshapiro case SMFIR_REPLBODY: 323364562Sgshapiro if (!bitset(SMFIF_MODBODY, m->mf_fflags)) 323464562Sgshapiro { 323564562Sgshapiro if (LogLevel > 0) 323664562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 323764562Sgshapiro "milter_data(%s): lied about replacing body, rejecting request and tempfailing message", 323864562Sgshapiro m->mf_name); 323964562Sgshapiro replfailed = TRUE; 324064562Sgshapiro break; 324164562Sgshapiro } 324264562Sgshapiro 324364562Sgshapiro /* already failed in attempt */ 324464562Sgshapiro if (replfailed) 324564562Sgshapiro break; 324664562Sgshapiro 324764562Sgshapiro if (!dfopen) 324864562Sgshapiro { 324964562Sgshapiro if (milter_reopen_df(e) < 0) 325064562Sgshapiro { 325164562Sgshapiro replfailed = TRUE; 325264562Sgshapiro break; 325364562Sgshapiro } 325464562Sgshapiro dfopen = TRUE; 325564562Sgshapiro rewind = TRUE; 325664562Sgshapiro } 325764562Sgshapiro 325864562Sgshapiro if (milter_replbody(response, rlen, 325964562Sgshapiro newfilter, e) < 0) 326064562Sgshapiro replfailed = TRUE; 326164562Sgshapiro newfilter = FALSE; 326264562Sgshapiro replbody = TRUE; 326364562Sgshapiro break; 326464562Sgshapiro 326564562Sgshapiro default: 326664562Sgshapiro /* Invalid response to command */ 326764562Sgshapiro if (LogLevel > 0) 326864562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 326964562Sgshapiro "milter_data(%s): returned bogus response %c", 327064562Sgshapiro m->mf_name, rcmd); 327164562Sgshapiro milter_error(m); 327264562Sgshapiro break; 327364562Sgshapiro } 327464562Sgshapiro if (rcmd != SMFIR_REPLYCODE && 327564562Sgshapiro response != NULL) 327664562Sgshapiro { 327764562Sgshapiro free(response); 327864562Sgshapiro response = NULL; 327964562Sgshapiro } 328064562Sgshapiro 328164562Sgshapiro if (m->mf_state == SMFS_ERROR) 328264562Sgshapiro break; 328364562Sgshapiro } 328464562Sgshapiro 328564562Sgshapiro if (replbody && !replfailed) 328664562Sgshapiro { 328764562Sgshapiro /* flush possible buffered character */ 328864562Sgshapiro milter_replbody(NULL, 0, !replbody, e); 328964562Sgshapiro replbody = FALSE; 329064562Sgshapiro } 329164562Sgshapiro 329264562Sgshapiro if (m->mf_state == SMFS_ERROR) 329364562Sgshapiro { 329464562Sgshapiro MILTER_CHECK_ERROR(continue); 329564562Sgshapiro goto finishup; 329664562Sgshapiro } 329764562Sgshapiro } 329864562Sgshapiro 329964562Sgshapirofinishup: 330064562Sgshapiro /* leave things in the expected state if we touched it */ 330164562Sgshapiro if (replfailed) 330264562Sgshapiro { 330364562Sgshapiro if (*state == SMFIR_CONTINUE || 330464562Sgshapiro *state == SMFIR_ACCEPT) 330564562Sgshapiro { 330664562Sgshapiro *state = SMFIR_TEMPFAIL; 330764562Sgshapiro if (response != NULL) 330864562Sgshapiro { 330964562Sgshapiro free(response); 331064562Sgshapiro response = NULL; 331164562Sgshapiro } 331264562Sgshapiro } 331364562Sgshapiro 331464562Sgshapiro if (dfopen) 331564562Sgshapiro { 331664562Sgshapiro (void) fclose(e->e_dfp); 331764562Sgshapiro e->e_dfp = NULL; 331864562Sgshapiro e->e_flags &= ~EF_HAS_DF; 331964562Sgshapiro dfopen = FALSE; 332064562Sgshapiro } 332164562Sgshapiro rewind = FALSE; 332264562Sgshapiro } 332364562Sgshapiro 332464562Sgshapiro if ((dfopen && milter_reset_df(e) < 0) || 332564562Sgshapiro (rewind && bfrewind(e->e_dfp) < 0)) 332664562Sgshapiro { 332764562Sgshapiro save_errno = errno; 332864562Sgshapiro ExitStat = EX_IOERR; 332964562Sgshapiro 333064562Sgshapiro /* 333164562Sgshapiro ** If filter told us to keep message but we had 333264562Sgshapiro ** an error, we can't really keep it, tempfail it. 333364562Sgshapiro */ 333464562Sgshapiro 333564562Sgshapiro if (*state == SMFIR_CONTINUE || 333664562Sgshapiro *state == SMFIR_ACCEPT) 333764562Sgshapiro { 333864562Sgshapiro *state = SMFIR_TEMPFAIL; 333964562Sgshapiro if (response != NULL) 334064562Sgshapiro { 334164562Sgshapiro free(response); 334264562Sgshapiro response = NULL; 334364562Sgshapiro } 334464562Sgshapiro } 334564562Sgshapiro 334664562Sgshapiro errno = save_errno; 334764562Sgshapiro syserr("milter_data: %s/df%s: read error", 334864562Sgshapiro qid_printqueue(e->e_queuedir), e->e_id); 334964562Sgshapiro } 335064562Sgshapiro MILTER_CHECK_DONE_MSG(); 335164562Sgshapiro return response; 335264562Sgshapiro} 335364562Sgshapiro/* 335464562Sgshapiro** MILTER_QUIT -- informs the filter(s) we are done and closes connection(s) 335564562Sgshapiro** 335664562Sgshapiro** Parameters: 335764562Sgshapiro** e -- current envelope. 335864562Sgshapiro** 335964562Sgshapiro** Returns: 336064562Sgshapiro** none 336164562Sgshapiro*/ 336264562Sgshapiro 336364562Sgshapirovoid 336464562Sgshapiromilter_quit(e) 336564562Sgshapiro ENVELOPE *e; 336664562Sgshapiro{ 336764562Sgshapiro int i; 336864562Sgshapiro 336964562Sgshapiro if (tTd(64, 10)) 337064562Sgshapiro dprintf("milter_quit\n"); 337164562Sgshapiro 337264562Sgshapiro for (i = 0; InputFilters[i] != NULL; i++) 337364562Sgshapiro milter_quit_filter(InputFilters[i], e); 337464562Sgshapiro} 337564562Sgshapiro/* 337664562Sgshapiro** MILTER_ABORT -- informs the filter(s) that we are aborting current message 337764562Sgshapiro** 337864562Sgshapiro** Parameters: 337964562Sgshapiro** e -- current envelope. 338064562Sgshapiro** 338164562Sgshapiro** Returns: 338264562Sgshapiro** none 338364562Sgshapiro*/ 338464562Sgshapiro 338564562Sgshapirovoid 338664562Sgshapiromilter_abort(e) 338764562Sgshapiro ENVELOPE *e; 338864562Sgshapiro{ 338964562Sgshapiro int i; 339064562Sgshapiro 339164562Sgshapiro if (tTd(64, 10)) 339264562Sgshapiro dprintf("milter_abort\n"); 339364562Sgshapiro 339464562Sgshapiro for (i = 0; InputFilters[i] != NULL; i++) 339564562Sgshapiro { 339664562Sgshapiro struct milter *m = InputFilters[i]; 339764562Sgshapiro 339864562Sgshapiro /* sanity checks */ 339964562Sgshapiro if (m->mf_sock < 0 || m->mf_state != SMFS_INMSG) 340064562Sgshapiro continue; 340164562Sgshapiro 340264562Sgshapiro milter_abort_filter(m, e); 340364562Sgshapiro } 340464562Sgshapiro} 340564562Sgshapiro#endif /* _FFR_MILTER */ 3406