10Sstevel@tonic-gate /* 2*3544Sjbeck * Copyright (c) 1999-2006 Sendmail, Inc. and its suppliers. 30Sstevel@tonic-gate * All rights reserved. 40Sstevel@tonic-gate * 50Sstevel@tonic-gate * By using this file, you agree to the terms and conditions set 60Sstevel@tonic-gate * forth in the LICENSE file which can be found at the top level of 70Sstevel@tonic-gate * the sendmail distribution. 80Sstevel@tonic-gate * 90Sstevel@tonic-gate */ 100Sstevel@tonic-gate 110Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 120Sstevel@tonic-gate 130Sstevel@tonic-gate #include <sendmail.h> 140Sstevel@tonic-gate 15*3544Sjbeck SM_RCSID("@(#)$Id: milter.c,v 8.266 2006/11/29 00:20:41 ca Exp $") 160Sstevel@tonic-gate 170Sstevel@tonic-gate #if MILTER 18*3544Sjbeck # include <sm/sendmail.h> 190Sstevel@tonic-gate # include <libmilter/mfapi.h> 200Sstevel@tonic-gate # include <libmilter/mfdef.h> 210Sstevel@tonic-gate 220Sstevel@tonic-gate # include <errno.h> 23616Sjbeck # include <sm/time.h> 240Sstevel@tonic-gate # include <sys/uio.h> 250Sstevel@tonic-gate 260Sstevel@tonic-gate # if NETINET || NETINET6 270Sstevel@tonic-gate # include <arpa/inet.h> 28*3544Sjbeck # if MILTER_NO_NAGLE 290Sstevel@tonic-gate # include <netinet/tcp.h> 30*3544Sjbeck # endif /* MILTER_NO_NAGLE */ 310Sstevel@tonic-gate # endif /* NETINET || NETINET6 */ 320Sstevel@tonic-gate 330Sstevel@tonic-gate # include <sm/fdset.h> 340Sstevel@tonic-gate 350Sstevel@tonic-gate static void milter_connect_timeout __P((int)); 360Sstevel@tonic-gate static void milter_error __P((struct milter *, ENVELOPE *)); 370Sstevel@tonic-gate static int milter_open __P((struct milter *, bool, ENVELOPE *)); 380Sstevel@tonic-gate static void milter_parse_timeouts __P((char *, struct milter *)); 39*3544Sjbeck static char *milter_sysread __P((struct milter *, char *, ssize_t, time_t, 40*3544Sjbeck ENVELOPE *, const char *)); 41*3544Sjbeck static char *milter_read __P((struct milter *, char *, ssize_t *, time_t, 42*3544Sjbeck ENVELOPE *, const char *)); 43*3544Sjbeck static char *milter_write __P((struct milter *, int, char *, ssize_t, 44*3544Sjbeck time_t, ENVELOPE *, const char *)); 45*3544Sjbeck static char *milter_send_command __P((struct milter *, int, void *, 46*3544Sjbeck ssize_t, ENVELOPE *, char *, const char *)); 47*3544Sjbeck static char *milter_command __P((int, void *, ssize_t, char **, 48*3544Sjbeck ENVELOPE *, char *, const char *, bool)); 49*3544Sjbeck static char *milter_body __P((struct milter *, ENVELOPE *, char *)); 50*3544Sjbeck static int milter_reopen_df __P((ENVELOPE *)); 51*3544Sjbeck static int milter_reset_df __P((ENVELOPE *)); 52*3544Sjbeck static void milter_quit_filter __P((struct milter *, ENVELOPE *)); 53*3544Sjbeck static void milter_abort_filter __P((struct milter *, ENVELOPE *)); 54*3544Sjbeck static void milter_send_macros __P((struct milter *, char **, int, 55*3544Sjbeck ENVELOPE *)); 56*3544Sjbeck static int milter_negotiate __P((struct milter *, ENVELOPE *)); 57*3544Sjbeck static void milter_per_connection_check __P((ENVELOPE *)); 58*3544Sjbeck static char *milter_headers __P((struct milter *, ENVELOPE *, char *)); 59*3544Sjbeck static void milter_addheader __P((struct milter *, char *, ssize_t, 60*3544Sjbeck ENVELOPE *)); 61*3544Sjbeck static void milter_insheader __P((struct milter *, char *, ssize_t, 62*3544Sjbeck ENVELOPE *)); 63*3544Sjbeck static void milter_changeheader __P((struct milter *, char *, ssize_t, 64*3544Sjbeck ENVELOPE *)); 65*3544Sjbeck static void milter_chgfrom __P((char *, ssize_t, ENVELOPE *)); 66*3544Sjbeck static void milter_addrcpt __P((char *, ssize_t, ENVELOPE *)); 67*3544Sjbeck static void milter_addrcpt_par __P((char *, ssize_t, ENVELOPE *)); 68*3544Sjbeck static void milter_delrcpt __P((char *, ssize_t, ENVELOPE *)); 69*3544Sjbeck static int milter_replbody __P((char *, ssize_t, bool, ENVELOPE *)); 70*3544Sjbeck static int milter_set_macros __P((char *, char **, char *, int)); 71*3544Sjbeck 72*3544Sjbeck 73*3544Sjbeck /* milter states */ 74*3544Sjbeck # define SMFS_CLOSED 'C' /* closed for all further actions */ 75*3544Sjbeck # define SMFS_OPEN 'O' /* connected to remote milter filter */ 76*3544Sjbeck # define SMFS_INMSG 'M' /* currently servicing a message */ 77*3544Sjbeck # define SMFS_DONE 'D' /* done with current message */ 78*3544Sjbeck # define SMFS_CLOSABLE 'Q' /* done with current connection */ 79*3544Sjbeck # define SMFS_ERROR 'E' /* error state */ 80*3544Sjbeck # define SMFS_READY 'R' /* ready for action */ 81*3544Sjbeck # define SMFS_SKIP 'S' /* skip body */ 820Sstevel@tonic-gate 830Sstevel@tonic-gate static char *MilterConnectMacros[MAXFILTERMACROS + 1]; 840Sstevel@tonic-gate static char *MilterHeloMacros[MAXFILTERMACROS + 1]; 850Sstevel@tonic-gate static char *MilterEnvFromMacros[MAXFILTERMACROS + 1]; 860Sstevel@tonic-gate static char *MilterEnvRcptMacros[MAXFILTERMACROS + 1]; 870Sstevel@tonic-gate static char *MilterDataMacros[MAXFILTERMACROS + 1]; 880Sstevel@tonic-gate static char *MilterEOMMacros[MAXFILTERMACROS + 1]; 89*3544Sjbeck static char *MilterEOHMacros[MAXFILTERMACROS + 1]; 900Sstevel@tonic-gate static size_t MilterMaxDataSize = MILTER_MAX_DATA_SIZE; 910Sstevel@tonic-gate 920Sstevel@tonic-gate # define MILTER_CHECK_DONE_MSG() \ 930Sstevel@tonic-gate if (*state == SMFIR_REPLYCODE || \ 940Sstevel@tonic-gate *state == SMFIR_REJECT || \ 950Sstevel@tonic-gate *state == SMFIR_DISCARD || \ 960Sstevel@tonic-gate *state == SMFIR_TEMPFAIL) \ 970Sstevel@tonic-gate { \ 980Sstevel@tonic-gate /* Abort the filters to let them know we are done with msg */ \ 990Sstevel@tonic-gate milter_abort(e); \ 1000Sstevel@tonic-gate } 1010Sstevel@tonic-gate 1020Sstevel@tonic-gate # define MILTER_CHECK_ERROR(initial, action) \ 1030Sstevel@tonic-gate if (!initial && tTd(71, 100)) \ 1040Sstevel@tonic-gate { \ 1050Sstevel@tonic-gate if (e->e_quarmsg == NULL) \ 1060Sstevel@tonic-gate { \ 1070Sstevel@tonic-gate e->e_quarmsg = sm_rpool_strdup_x(e->e_rpool, \ 1080Sstevel@tonic-gate "filter failure"); \ 1090Sstevel@tonic-gate macdefine(&e->e_macro, A_PERM, macid("{quarantine}"), \ 1100Sstevel@tonic-gate e->e_quarmsg); \ 1110Sstevel@tonic-gate } \ 1120Sstevel@tonic-gate } \ 1130Sstevel@tonic-gate else if (tTd(71, 101)) \ 1140Sstevel@tonic-gate { \ 1150Sstevel@tonic-gate if (e->e_quarmsg == NULL) \ 1160Sstevel@tonic-gate { \ 1170Sstevel@tonic-gate e->e_quarmsg = sm_rpool_strdup_x(e->e_rpool, \ 1180Sstevel@tonic-gate "filter failure"); \ 1190Sstevel@tonic-gate macdefine(&e->e_macro, A_PERM, macid("{quarantine}"), \ 1200Sstevel@tonic-gate e->e_quarmsg); \ 1210Sstevel@tonic-gate } \ 1220Sstevel@tonic-gate } \ 1230Sstevel@tonic-gate else if (bitnset(SMF_TEMPFAIL, m->mf_flags)) \ 1240Sstevel@tonic-gate *state = SMFIR_TEMPFAIL; \ 1250Sstevel@tonic-gate else if (bitnset(SMF_TEMPDROP, m->mf_flags)) \ 1260Sstevel@tonic-gate *state = SMFIR_SHUTDOWN; \ 1270Sstevel@tonic-gate else if (bitnset(SMF_REJECT, m->mf_flags)) \ 1280Sstevel@tonic-gate *state = SMFIR_REJECT; \ 1290Sstevel@tonic-gate else \ 1300Sstevel@tonic-gate action; 1310Sstevel@tonic-gate 1320Sstevel@tonic-gate # define MILTER_CHECK_REPLYCODE(default) \ 1330Sstevel@tonic-gate if (response == NULL || \ 1340Sstevel@tonic-gate strlen(response) + 1 != (size_t) rlen || \ 1350Sstevel@tonic-gate rlen < 3 || \ 1360Sstevel@tonic-gate (response[0] != '4' && response[0] != '5') || \ 1370Sstevel@tonic-gate !isascii(response[1]) || !isdigit(response[1]) || \ 1380Sstevel@tonic-gate !isascii(response[2]) || !isdigit(response[2])) \ 1390Sstevel@tonic-gate { \ 1400Sstevel@tonic-gate if (response != NULL) \ 1410Sstevel@tonic-gate sm_free(response); /* XXX */ \ 1420Sstevel@tonic-gate response = newstr(default); \ 1430Sstevel@tonic-gate } \ 1440Sstevel@tonic-gate else \ 1450Sstevel@tonic-gate { \ 1460Sstevel@tonic-gate char *ptr = response; \ 1470Sstevel@tonic-gate \ 1480Sstevel@tonic-gate /* Check for unprotected %'s in the string */ \ 1490Sstevel@tonic-gate while (*ptr != '\0') \ 1500Sstevel@tonic-gate { \ 1510Sstevel@tonic-gate if (*ptr == '%' && *++ptr != '%') \ 1520Sstevel@tonic-gate { \ 1530Sstevel@tonic-gate sm_free(response); /* XXX */ \ 1540Sstevel@tonic-gate response = newstr(default); \ 1550Sstevel@tonic-gate break; \ 1560Sstevel@tonic-gate } \ 1570Sstevel@tonic-gate ptr++; \ 1580Sstevel@tonic-gate } \ 1590Sstevel@tonic-gate } 1600Sstevel@tonic-gate 1610Sstevel@tonic-gate # define MILTER_DF_ERROR(msg) \ 1620Sstevel@tonic-gate { \ 1630Sstevel@tonic-gate int save_errno = errno; \ 1640Sstevel@tonic-gate \ 1650Sstevel@tonic-gate if (tTd(64, 5)) \ 1660Sstevel@tonic-gate { \ 1670Sstevel@tonic-gate sm_dprintf(msg, dfname, sm_errstring(save_errno)); \ 1680Sstevel@tonic-gate sm_dprintf("\n"); \ 1690Sstevel@tonic-gate } \ 1700Sstevel@tonic-gate if (MilterLogLevel > 0) \ 1710Sstevel@tonic-gate sm_syslog(LOG_ERR, e->e_id, msg, dfname, sm_errstring(save_errno)); \ 1720Sstevel@tonic-gate if (SuperSafe == SAFE_REALLY) \ 1730Sstevel@tonic-gate { \ 1740Sstevel@tonic-gate if (e->e_dfp != NULL) \ 1750Sstevel@tonic-gate { \ 1760Sstevel@tonic-gate (void) sm_io_close(e->e_dfp, SM_TIME_DEFAULT); \ 1770Sstevel@tonic-gate e->e_dfp = NULL; \ 1780Sstevel@tonic-gate } \ 1790Sstevel@tonic-gate e->e_flags &= ~EF_HAS_DF; \ 1800Sstevel@tonic-gate } \ 1810Sstevel@tonic-gate errno = save_errno; \ 1820Sstevel@tonic-gate } 1830Sstevel@tonic-gate 1840Sstevel@tonic-gate /* 1850Sstevel@tonic-gate ** MILTER_TIMEOUT -- make sure socket is ready in time 1860Sstevel@tonic-gate ** 1870Sstevel@tonic-gate ** Parameters: 1880Sstevel@tonic-gate ** routine -- routine name for debug/logging 1890Sstevel@tonic-gate ** secs -- number of seconds in timeout 1900Sstevel@tonic-gate ** write -- waiting to read or write? 1910Sstevel@tonic-gate ** started -- whether this is part of a previous sequence 1920Sstevel@tonic-gate ** 1930Sstevel@tonic-gate ** Assumes 'm' is a milter structure for the current socket. 1940Sstevel@tonic-gate */ 1950Sstevel@tonic-gate 196*3544Sjbeck # define MILTER_TIMEOUT(routine, secs, write, started, function) \ 1970Sstevel@tonic-gate { \ 1980Sstevel@tonic-gate int ret; \ 1990Sstevel@tonic-gate int save_errno; \ 2000Sstevel@tonic-gate fd_set fds; \ 2010Sstevel@tonic-gate struct timeval tv; \ 2020Sstevel@tonic-gate \ 2030Sstevel@tonic-gate if (SM_FD_SETSIZE > 0 && m->mf_sock >= SM_FD_SETSIZE) \ 2040Sstevel@tonic-gate { \ 2050Sstevel@tonic-gate if (tTd(64, 5)) \ 2060Sstevel@tonic-gate sm_dprintf("milter_%s(%s): socket %d is larger than FD_SETSIZE %d\n", \ 2070Sstevel@tonic-gate (routine), m->mf_name, m->mf_sock, \ 2080Sstevel@tonic-gate SM_FD_SETSIZE); \ 2090Sstevel@tonic-gate if (MilterLogLevel > 0) \ 2100Sstevel@tonic-gate sm_syslog(LOG_ERR, e->e_id, \ 2110Sstevel@tonic-gate "Milter (%s): socket(%s) %d is larger than FD_SETSIZE %d", \ 2120Sstevel@tonic-gate m->mf_name, (routine), m->mf_sock, \ 2130Sstevel@tonic-gate SM_FD_SETSIZE); \ 2140Sstevel@tonic-gate milter_error(m, e); \ 2150Sstevel@tonic-gate return NULL; \ 2160Sstevel@tonic-gate } \ 2170Sstevel@tonic-gate \ 2180Sstevel@tonic-gate do \ 2190Sstevel@tonic-gate { \ 2200Sstevel@tonic-gate FD_ZERO(&fds); \ 2210Sstevel@tonic-gate SM_FD_SET(m->mf_sock, &fds); \ 2220Sstevel@tonic-gate tv.tv_sec = (secs); \ 2230Sstevel@tonic-gate tv.tv_usec = 0; \ 2240Sstevel@tonic-gate ret = select(m->mf_sock + 1, \ 2250Sstevel@tonic-gate (write) ? NULL : &fds, \ 2260Sstevel@tonic-gate (write) ? &fds : NULL, \ 2270Sstevel@tonic-gate NULL, &tv); \ 2280Sstevel@tonic-gate } while (ret < 0 && errno == EINTR); \ 2290Sstevel@tonic-gate \ 2300Sstevel@tonic-gate switch (ret) \ 2310Sstevel@tonic-gate { \ 2320Sstevel@tonic-gate case 0: \ 2330Sstevel@tonic-gate if (tTd(64, 5)) \ 234*3544Sjbeck sm_dprintf("milter_%s(%s): timeout, where=%s\n", \ 235*3544Sjbeck (routine), m->mf_name, (function)); \ 2360Sstevel@tonic-gate if (MilterLogLevel > 0) \ 2370Sstevel@tonic-gate sm_syslog(LOG_ERR, e->e_id, \ 238*3544Sjbeck "Milter (%s): timeout %s data %s, where=%s", \ 239*3544Sjbeck m->mf_name, \ 2400Sstevel@tonic-gate started ? "during" : "before", \ 241*3544Sjbeck (routine), (function)); \ 2420Sstevel@tonic-gate milter_error(m, e); \ 2430Sstevel@tonic-gate return NULL; \ 2440Sstevel@tonic-gate \ 2450Sstevel@tonic-gate case -1: \ 2460Sstevel@tonic-gate save_errno = errno; \ 2470Sstevel@tonic-gate if (tTd(64, 5)) \ 2480Sstevel@tonic-gate sm_dprintf("milter_%s(%s): select: %s\n", (routine), \ 2490Sstevel@tonic-gate m->mf_name, sm_errstring(save_errno)); \ 2500Sstevel@tonic-gate if (MilterLogLevel > 0) \ 2510Sstevel@tonic-gate { \ 2520Sstevel@tonic-gate sm_syslog(LOG_ERR, e->e_id, \ 2530Sstevel@tonic-gate "Milter (%s): select(%s): %s", \ 2540Sstevel@tonic-gate m->mf_name, (routine), \ 2550Sstevel@tonic-gate sm_errstring(save_errno)); \ 2560Sstevel@tonic-gate } \ 2570Sstevel@tonic-gate milter_error(m, e); \ 2580Sstevel@tonic-gate return NULL; \ 2590Sstevel@tonic-gate \ 2600Sstevel@tonic-gate default: \ 2610Sstevel@tonic-gate if (SM_FD_ISSET(m->mf_sock, &fds)) \ 2620Sstevel@tonic-gate break; \ 2630Sstevel@tonic-gate if (tTd(64, 5)) \ 2640Sstevel@tonic-gate sm_dprintf("milter_%s(%s): socket not ready\n", \ 2650Sstevel@tonic-gate (routine), m->mf_name); \ 2660Sstevel@tonic-gate if (MilterLogLevel > 0) \ 2670Sstevel@tonic-gate { \ 2680Sstevel@tonic-gate sm_syslog(LOG_ERR, e->e_id, \ 2690Sstevel@tonic-gate "Milter (%s): socket(%s) not ready", \ 2700Sstevel@tonic-gate m->mf_name, (routine)); \ 2710Sstevel@tonic-gate } \ 2720Sstevel@tonic-gate milter_error(m, e); \ 2730Sstevel@tonic-gate return NULL; \ 2740Sstevel@tonic-gate } \ 2750Sstevel@tonic-gate } 2760Sstevel@tonic-gate 2770Sstevel@tonic-gate /* 2780Sstevel@tonic-gate ** Low level functions 2790Sstevel@tonic-gate */ 2800Sstevel@tonic-gate 2810Sstevel@tonic-gate /* 2820Sstevel@tonic-gate ** MILTER_READ -- read from a remote milter filter 2830Sstevel@tonic-gate ** 2840Sstevel@tonic-gate ** Parameters: 2850Sstevel@tonic-gate ** m -- milter to read from. 2860Sstevel@tonic-gate ** cmd -- return param for command read. 2870Sstevel@tonic-gate ** rlen -- return length of response string. 2880Sstevel@tonic-gate ** to -- timeout in seconds. 2890Sstevel@tonic-gate ** e -- current envelope. 2900Sstevel@tonic-gate ** 2910Sstevel@tonic-gate ** Returns: 2920Sstevel@tonic-gate ** response string (may be NULL) 2930Sstevel@tonic-gate */ 2940Sstevel@tonic-gate 2950Sstevel@tonic-gate static char * 296*3544Sjbeck milter_sysread(m, buf, sz, to, e, where) 2970Sstevel@tonic-gate struct milter *m; 2980Sstevel@tonic-gate char *buf; 2990Sstevel@tonic-gate ssize_t sz; 3000Sstevel@tonic-gate time_t to; 3010Sstevel@tonic-gate ENVELOPE *e; 302*3544Sjbeck const char *where; 3030Sstevel@tonic-gate { 3040Sstevel@tonic-gate time_t readstart = 0; 3050Sstevel@tonic-gate ssize_t len, curl; 3060Sstevel@tonic-gate bool started = false; 3070Sstevel@tonic-gate 3080Sstevel@tonic-gate curl = 0; 3090Sstevel@tonic-gate 3100Sstevel@tonic-gate if (to > 0) 3110Sstevel@tonic-gate readstart = curtime(); 3120Sstevel@tonic-gate 3130Sstevel@tonic-gate for (;;) 3140Sstevel@tonic-gate { 3150Sstevel@tonic-gate if (to > 0) 3160Sstevel@tonic-gate { 3170Sstevel@tonic-gate time_t now; 3180Sstevel@tonic-gate 3190Sstevel@tonic-gate now = curtime(); 3200Sstevel@tonic-gate if (now - readstart >= to) 3210Sstevel@tonic-gate { 3220Sstevel@tonic-gate if (tTd(64, 5)) 323*3544Sjbeck sm_dprintf("milter_sys_read (%s): timeout %s data read in %s", 324*3544Sjbeck m->mf_name, 3250Sstevel@tonic-gate started ? "during" : "before", 326*3544Sjbeck where); 3270Sstevel@tonic-gate if (MilterLogLevel > 0) 3280Sstevel@tonic-gate sm_syslog(LOG_ERR, e->e_id, 329*3544Sjbeck "Milter (%s): timeout %s data read in %s", 330*3544Sjbeck m->mf_name, 3310Sstevel@tonic-gate started ? "during" : "before", 332*3544Sjbeck where); 3330Sstevel@tonic-gate milter_error(m, e); 3340Sstevel@tonic-gate return NULL; 3350Sstevel@tonic-gate } 3360Sstevel@tonic-gate to -= now - readstart; 3370Sstevel@tonic-gate readstart = now; 338*3544Sjbeck MILTER_TIMEOUT("read", to, false, started, where); 3390Sstevel@tonic-gate } 3400Sstevel@tonic-gate 3410Sstevel@tonic-gate len = read(m->mf_sock, buf + curl, sz - curl); 3420Sstevel@tonic-gate 3430Sstevel@tonic-gate if (len < 0) 3440Sstevel@tonic-gate { 3450Sstevel@tonic-gate int save_errno = errno; 3460Sstevel@tonic-gate 3470Sstevel@tonic-gate if (tTd(64, 5)) 348*3544Sjbeck sm_dprintf("milter_sys_read(%s): read returned %ld: %s\n", 3490Sstevel@tonic-gate m->mf_name, (long) len, 3500Sstevel@tonic-gate sm_errstring(save_errno)); 3510Sstevel@tonic-gate if (MilterLogLevel > 0) 3520Sstevel@tonic-gate sm_syslog(LOG_ERR, e->e_id, 3530Sstevel@tonic-gate "Milter (%s): read returned %ld: %s", 3540Sstevel@tonic-gate m->mf_name, (long) len, 3550Sstevel@tonic-gate sm_errstring(save_errno)); 3560Sstevel@tonic-gate milter_error(m, e); 3570Sstevel@tonic-gate return NULL; 3580Sstevel@tonic-gate } 3590Sstevel@tonic-gate 3600Sstevel@tonic-gate started = true; 3610Sstevel@tonic-gate curl += len; 3620Sstevel@tonic-gate if (len == 0 || curl >= sz) 3630Sstevel@tonic-gate break; 3640Sstevel@tonic-gate 3650Sstevel@tonic-gate } 3660Sstevel@tonic-gate 3670Sstevel@tonic-gate if (curl != sz) 3680Sstevel@tonic-gate { 3690Sstevel@tonic-gate if (tTd(64, 5)) 370*3544Sjbeck sm_dprintf("milter_sys_read(%s): cmd read returned %ld, expecting %ld\n", 3710Sstevel@tonic-gate m->mf_name, (long) curl, (long) sz); 3720Sstevel@tonic-gate if (MilterLogLevel > 0) 3730Sstevel@tonic-gate sm_syslog(LOG_ERR, e->e_id, 374*3544Sjbeck "milter_sys_read(%s): cmd read returned %ld, expecting %ld", 3750Sstevel@tonic-gate m->mf_name, (long) curl, (long) sz); 3760Sstevel@tonic-gate milter_error(m, e); 3770Sstevel@tonic-gate return NULL; 3780Sstevel@tonic-gate } 3790Sstevel@tonic-gate return buf; 3800Sstevel@tonic-gate } 3810Sstevel@tonic-gate 3820Sstevel@tonic-gate static char * 383*3544Sjbeck milter_read(m, cmd, rlen, to, e, where) 3840Sstevel@tonic-gate struct milter *m; 3850Sstevel@tonic-gate char *cmd; 3860Sstevel@tonic-gate ssize_t *rlen; 3870Sstevel@tonic-gate time_t to; 3880Sstevel@tonic-gate ENVELOPE *e; 389*3544Sjbeck const char *where; 3900Sstevel@tonic-gate { 3910Sstevel@tonic-gate time_t readstart = 0; 3920Sstevel@tonic-gate ssize_t expl; 3930Sstevel@tonic-gate mi_int32 i; 394*3544Sjbeck # if MILTER_NO_NAGLE && defined(TCP_CORK) 3950Sstevel@tonic-gate int cork = 0; 396*3544Sjbeck # endif /* MILTER_NO_NAGLE && defined(TCP_CORK) */ 3970Sstevel@tonic-gate char *buf; 3980Sstevel@tonic-gate char data[MILTER_LEN_BYTES + 1]; 3990Sstevel@tonic-gate 4000Sstevel@tonic-gate if (m->mf_sock < 0) 4010Sstevel@tonic-gate { 4020Sstevel@tonic-gate if (MilterLogLevel > 0) 4030Sstevel@tonic-gate sm_syslog(LOG_ERR, e->e_id, 404*3544Sjbeck "milter_read(%s): socket closed, where=%s", 405*3544Sjbeck m->mf_name, where); 4060Sstevel@tonic-gate milter_error(m, e); 4070Sstevel@tonic-gate return NULL; 4080Sstevel@tonic-gate } 4090Sstevel@tonic-gate 4100Sstevel@tonic-gate *rlen = 0; 4110Sstevel@tonic-gate *cmd = '\0'; 4120Sstevel@tonic-gate 4130Sstevel@tonic-gate if (to > 0) 4140Sstevel@tonic-gate readstart = curtime(); 4150Sstevel@tonic-gate 416*3544Sjbeck # if MILTER_NO_NAGLE && defined(TCP_CORK) 4170Sstevel@tonic-gate setsockopt(m->mf_sock, IPPROTO_TCP, TCP_CORK, (char *)&cork, 4180Sstevel@tonic-gate sizeof(cork)); 419*3544Sjbeck # endif /* MILTER_NO_NAGLE && defined(TCP_CORK) */ 420*3544Sjbeck 421*3544Sjbeck if (milter_sysread(m, data, sizeof(data), to, e, where) == NULL) 4220Sstevel@tonic-gate return NULL; 4230Sstevel@tonic-gate 424*3544Sjbeck # if MILTER_NO_NAGLE && defined(TCP_CORK) 4250Sstevel@tonic-gate cork = 1; 4260Sstevel@tonic-gate setsockopt(m->mf_sock, IPPROTO_TCP, TCP_CORK, (char *)&cork, 4270Sstevel@tonic-gate sizeof(cork)); 428*3544Sjbeck # endif /* MILTER_NO_NAGLE && defined(TCP_CORK) */ 4290Sstevel@tonic-gate 4300Sstevel@tonic-gate /* reset timeout */ 4310Sstevel@tonic-gate if (to > 0) 4320Sstevel@tonic-gate { 4330Sstevel@tonic-gate time_t now; 4340Sstevel@tonic-gate 4350Sstevel@tonic-gate now = curtime(); 4360Sstevel@tonic-gate if (now - readstart >= to) 4370Sstevel@tonic-gate { 4380Sstevel@tonic-gate if (tTd(64, 5)) 439*3544Sjbeck sm_dprintf("milter_read(%s): timeout before data read, where=%s\n", 440*3544Sjbeck m->mf_name, where); 4410Sstevel@tonic-gate if (MilterLogLevel > 0) 4420Sstevel@tonic-gate sm_syslog(LOG_ERR, e->e_id, 443*3544Sjbeck "Milter read(%s): timeout before data read, where=%s", 444*3544Sjbeck m->mf_name, where); 4450Sstevel@tonic-gate milter_error(m, e); 4460Sstevel@tonic-gate return NULL; 4470Sstevel@tonic-gate } 4480Sstevel@tonic-gate to -= now - readstart; 4490Sstevel@tonic-gate } 4500Sstevel@tonic-gate 4510Sstevel@tonic-gate *cmd = data[MILTER_LEN_BYTES]; 4520Sstevel@tonic-gate data[MILTER_LEN_BYTES] = '\0'; 4530Sstevel@tonic-gate (void) memcpy(&i, data, MILTER_LEN_BYTES); 4540Sstevel@tonic-gate expl = ntohl(i) - 1; 4550Sstevel@tonic-gate 4560Sstevel@tonic-gate if (tTd(64, 25)) 4570Sstevel@tonic-gate sm_dprintf("milter_read(%s): expecting %ld bytes\n", 4580Sstevel@tonic-gate m->mf_name, (long) expl); 4590Sstevel@tonic-gate 4600Sstevel@tonic-gate if (expl < 0) 4610Sstevel@tonic-gate { 4620Sstevel@tonic-gate if (tTd(64, 5)) 463*3544Sjbeck sm_dprintf("milter_read(%s): read size %ld out of range, where=%s\n", 464*3544Sjbeck m->mf_name, (long) expl, where); 4650Sstevel@tonic-gate if (MilterLogLevel > 0) 4660Sstevel@tonic-gate sm_syslog(LOG_ERR, e->e_id, 467*3544Sjbeck "milter_read(%s): read size %ld out of range, where=%s", 468*3544Sjbeck m->mf_name, (long) expl, where); 4690Sstevel@tonic-gate milter_error(m, e); 4700Sstevel@tonic-gate return NULL; 4710Sstevel@tonic-gate } 4720Sstevel@tonic-gate 4730Sstevel@tonic-gate if (expl == 0) 4740Sstevel@tonic-gate return NULL; 4750Sstevel@tonic-gate 4760Sstevel@tonic-gate buf = (char *) xalloc(expl); 4770Sstevel@tonic-gate 478*3544Sjbeck if (milter_sysread(m, buf, expl, to, e, where) == NULL) 4790Sstevel@tonic-gate { 4800Sstevel@tonic-gate sm_free(buf); /* XXX */ 4810Sstevel@tonic-gate return NULL; 4820Sstevel@tonic-gate } 4830Sstevel@tonic-gate 4840Sstevel@tonic-gate if (tTd(64, 50)) 4850Sstevel@tonic-gate sm_dprintf("milter_read(%s): Returning %*s\n", 4860Sstevel@tonic-gate m->mf_name, (int) expl, buf); 4870Sstevel@tonic-gate *rlen = expl; 4880Sstevel@tonic-gate return buf; 4890Sstevel@tonic-gate } 4900Sstevel@tonic-gate 4910Sstevel@tonic-gate /* 4920Sstevel@tonic-gate ** MILTER_WRITE -- write to a remote milter filter 4930Sstevel@tonic-gate ** 4940Sstevel@tonic-gate ** Parameters: 4950Sstevel@tonic-gate ** m -- milter to read from. 4960Sstevel@tonic-gate ** cmd -- command to send. 4970Sstevel@tonic-gate ** buf -- optional command data. 4980Sstevel@tonic-gate ** len -- length of buf. 4990Sstevel@tonic-gate ** to -- timeout in seconds. 5000Sstevel@tonic-gate ** e -- current envelope. 5010Sstevel@tonic-gate ** 5020Sstevel@tonic-gate ** Returns: 5030Sstevel@tonic-gate ** buf if successful, NULL otherwise 5040Sstevel@tonic-gate ** Not actually used anywhere but function prototype 5050Sstevel@tonic-gate ** must match milter_read() 5060Sstevel@tonic-gate */ 5070Sstevel@tonic-gate 5080Sstevel@tonic-gate static char * 509*3544Sjbeck milter_write(m, cmd, buf, len, to, e, where) 5100Sstevel@tonic-gate struct milter *m; 511*3544Sjbeck int cmd; 5120Sstevel@tonic-gate char *buf; 5130Sstevel@tonic-gate ssize_t len; 5140Sstevel@tonic-gate time_t to; 5150Sstevel@tonic-gate ENVELOPE *e; 516*3544Sjbeck const char *where; 5170Sstevel@tonic-gate { 5180Sstevel@tonic-gate time_t writestart = (time_t) 0; 5190Sstevel@tonic-gate ssize_t sl, i; 5200Sstevel@tonic-gate int num_vectors; 5210Sstevel@tonic-gate mi_int32 nl; 522*3544Sjbeck char command = (char) cmd; 5230Sstevel@tonic-gate char data[MILTER_LEN_BYTES + 1]; 5240Sstevel@tonic-gate bool started = false; 5250Sstevel@tonic-gate struct iovec vector[2]; 5260Sstevel@tonic-gate 5270Sstevel@tonic-gate /* 5280Sstevel@tonic-gate ** At most two buffers will be written, though 5290Sstevel@tonic-gate ** only one may actually be used (see num_vectors). 5300Sstevel@tonic-gate ** The first is the size/command and the second is the command data. 5310Sstevel@tonic-gate */ 5320Sstevel@tonic-gate 5330Sstevel@tonic-gate if (len < 0 || len > MilterMaxDataSize) 5340Sstevel@tonic-gate { 5350Sstevel@tonic-gate if (tTd(64, 5)) 5360Sstevel@tonic-gate sm_dprintf("milter_write(%s): length %ld out of range\n", 5370Sstevel@tonic-gate m->mf_name, (long) len); 5380Sstevel@tonic-gate if (MilterLogLevel > 0) 5390Sstevel@tonic-gate sm_syslog(LOG_ERR, e->e_id, 5400Sstevel@tonic-gate "milter_write(%s): length %ld out of range", 5410Sstevel@tonic-gate m->mf_name, (long) len); 5420Sstevel@tonic-gate milter_error(m, e); 5430Sstevel@tonic-gate return NULL; 5440Sstevel@tonic-gate } 5450Sstevel@tonic-gate if (m->mf_sock < 0) 5460Sstevel@tonic-gate { 5470Sstevel@tonic-gate if (MilterLogLevel > 0) 5480Sstevel@tonic-gate sm_syslog(LOG_ERR, e->e_id, 5490Sstevel@tonic-gate "milter_write(%s): socket closed", 5500Sstevel@tonic-gate m->mf_name); 5510Sstevel@tonic-gate milter_error(m, e); 5520Sstevel@tonic-gate return NULL; 5530Sstevel@tonic-gate } 5540Sstevel@tonic-gate 5550Sstevel@tonic-gate if (tTd(64, 20)) 5560Sstevel@tonic-gate sm_dprintf("milter_write(%s): cmd %c, len %ld\n", 557*3544Sjbeck m->mf_name, command, (long) len); 558*3544Sjbeck 559*3544Sjbeck nl = htonl(len + 1); /* add 1 for the command char */ 5600Sstevel@tonic-gate (void) memcpy(data, (char *) &nl, MILTER_LEN_BYTES); 561*3544Sjbeck data[MILTER_LEN_BYTES] = command; 5620Sstevel@tonic-gate sl = MILTER_LEN_BYTES + 1; 5630Sstevel@tonic-gate 5640Sstevel@tonic-gate /* set up the vector for the size / command */ 5650Sstevel@tonic-gate vector[0].iov_base = (void *) data; 5660Sstevel@tonic-gate vector[0].iov_len = sl; 5670Sstevel@tonic-gate 5680Sstevel@tonic-gate /* 5690Sstevel@tonic-gate ** Determine if there is command data. If so, there will be two 5700Sstevel@tonic-gate ** vectors. If not, there will be only one. The vectors are set 5710Sstevel@tonic-gate ** up here and 'num_vectors' and 'sl' are set appropriately. 5720Sstevel@tonic-gate */ 5730Sstevel@tonic-gate 5740Sstevel@tonic-gate /* NOTE: len<0 has already been checked for. Pedantic */ 5750Sstevel@tonic-gate if (len <= 0 || buf == NULL) 5760Sstevel@tonic-gate { 5770Sstevel@tonic-gate /* There is no command data -- only a size / command data */ 5780Sstevel@tonic-gate num_vectors = 1; 5790Sstevel@tonic-gate } 5800Sstevel@tonic-gate else 5810Sstevel@tonic-gate { 5820Sstevel@tonic-gate /* 5830Sstevel@tonic-gate ** There is both size / command and command data. 5840Sstevel@tonic-gate ** Set up the vector for the command data. 5850Sstevel@tonic-gate */ 5860Sstevel@tonic-gate 5870Sstevel@tonic-gate num_vectors = 2; 5880Sstevel@tonic-gate sl += len; 5890Sstevel@tonic-gate vector[1].iov_base = (void *) buf; 5900Sstevel@tonic-gate vector[1].iov_len = len; 5910Sstevel@tonic-gate 5920Sstevel@tonic-gate if (tTd(64, 50)) 5930Sstevel@tonic-gate sm_dprintf("milter_write(%s): Sending %*s\n", 5940Sstevel@tonic-gate m->mf_name, (int) len, buf); 5950Sstevel@tonic-gate } 5960Sstevel@tonic-gate 5970Sstevel@tonic-gate if (to > 0) 5980Sstevel@tonic-gate { 5990Sstevel@tonic-gate writestart = curtime(); 600*3544Sjbeck MILTER_TIMEOUT("write", to, true, started, where); 6010Sstevel@tonic-gate } 6020Sstevel@tonic-gate 6030Sstevel@tonic-gate /* write the vector(s) */ 6040Sstevel@tonic-gate i = writev(m->mf_sock, vector, num_vectors); 6050Sstevel@tonic-gate if (i != sl) 6060Sstevel@tonic-gate { 6070Sstevel@tonic-gate int save_errno = errno; 6080Sstevel@tonic-gate 6090Sstevel@tonic-gate if (tTd(64, 5)) 6100Sstevel@tonic-gate sm_dprintf("milter_write(%s): write(%c) returned %ld, expected %ld: %s\n", 611*3544Sjbeck m->mf_name, command, (long) i, (long) sl, 6120Sstevel@tonic-gate sm_errstring(save_errno)); 6130Sstevel@tonic-gate if (MilterLogLevel > 0) 6140Sstevel@tonic-gate sm_syslog(LOG_ERR, e->e_id, 6150Sstevel@tonic-gate "Milter (%s): write(%c) returned %ld, expected %ld: %s", 616*3544Sjbeck m->mf_name, command, (long) i, (long) sl, 6170Sstevel@tonic-gate sm_errstring(save_errno)); 6180Sstevel@tonic-gate milter_error(m, e); 6190Sstevel@tonic-gate return NULL; 6200Sstevel@tonic-gate } 6210Sstevel@tonic-gate return buf; 6220Sstevel@tonic-gate } 6230Sstevel@tonic-gate 6240Sstevel@tonic-gate /* 6250Sstevel@tonic-gate ** Utility functions 6260Sstevel@tonic-gate */ 6270Sstevel@tonic-gate 6280Sstevel@tonic-gate /* 6290Sstevel@tonic-gate ** MILTER_OPEN -- connect to remote milter filter 6300Sstevel@tonic-gate ** 6310Sstevel@tonic-gate ** Parameters: 6320Sstevel@tonic-gate ** m -- milter to connect to. 6330Sstevel@tonic-gate ** parseonly -- parse but don't connect. 6340Sstevel@tonic-gate ** e -- current envelope. 6350Sstevel@tonic-gate ** 6360Sstevel@tonic-gate ** Returns: 6370Sstevel@tonic-gate ** connected socket if successful && !parseonly, 6380Sstevel@tonic-gate ** 0 upon parse success if parseonly, 6390Sstevel@tonic-gate ** -1 otherwise. 6400Sstevel@tonic-gate */ 6410Sstevel@tonic-gate 6420Sstevel@tonic-gate static jmp_buf MilterConnectTimeout; 6430Sstevel@tonic-gate 6440Sstevel@tonic-gate static int 6450Sstevel@tonic-gate milter_open(m, parseonly, e) 6460Sstevel@tonic-gate struct milter *m; 6470Sstevel@tonic-gate bool parseonly; 6480Sstevel@tonic-gate ENVELOPE *e; 6490Sstevel@tonic-gate { 6500Sstevel@tonic-gate int sock = 0; 6510Sstevel@tonic-gate SOCKADDR_LEN_T addrlen = 0; 6520Sstevel@tonic-gate int addrno = 0; 6530Sstevel@tonic-gate int save_errno; 6540Sstevel@tonic-gate char *p; 6550Sstevel@tonic-gate char *colon; 6560Sstevel@tonic-gate char *at; 6570Sstevel@tonic-gate struct hostent *hp = NULL; 6580Sstevel@tonic-gate SOCKADDR addr; 6590Sstevel@tonic-gate 6600Sstevel@tonic-gate if (m->mf_conn == NULL || m->mf_conn[0] == '\0') 6610Sstevel@tonic-gate { 6620Sstevel@tonic-gate if (tTd(64, 5)) 6630Sstevel@tonic-gate sm_dprintf("X%s: empty or missing socket information\n", 6640Sstevel@tonic-gate m->mf_name); 6650Sstevel@tonic-gate if (parseonly) 6660Sstevel@tonic-gate syserr("X%s: empty or missing socket information", 6670Sstevel@tonic-gate m->mf_name); 6680Sstevel@tonic-gate else if (MilterLogLevel > 0) 6690Sstevel@tonic-gate sm_syslog(LOG_ERR, e->e_id, 6700Sstevel@tonic-gate "Milter (%s): empty or missing socket information", 6710Sstevel@tonic-gate m->mf_name); 6720Sstevel@tonic-gate milter_error(m, e); 6730Sstevel@tonic-gate return -1; 6740Sstevel@tonic-gate } 6750Sstevel@tonic-gate 6760Sstevel@tonic-gate /* protocol:filename or protocol:port@host */ 677*3544Sjbeck memset(&addr, '\0', sizeof(addr)); 6780Sstevel@tonic-gate p = m->mf_conn; 6790Sstevel@tonic-gate colon = strchr(p, ':'); 6800Sstevel@tonic-gate if (colon != NULL) 6810Sstevel@tonic-gate { 6820Sstevel@tonic-gate *colon = '\0'; 6830Sstevel@tonic-gate 6840Sstevel@tonic-gate if (*p == '\0') 6850Sstevel@tonic-gate { 6860Sstevel@tonic-gate # if NETUNIX 6870Sstevel@tonic-gate /* default to AF_UNIX */ 6880Sstevel@tonic-gate addr.sa.sa_family = AF_UNIX; 6890Sstevel@tonic-gate # else /* NETUNIX */ 6900Sstevel@tonic-gate # if NETINET 6910Sstevel@tonic-gate /* default to AF_INET */ 6920Sstevel@tonic-gate addr.sa.sa_family = AF_INET; 6930Sstevel@tonic-gate # else /* NETINET */ 6940Sstevel@tonic-gate # if NETINET6 6950Sstevel@tonic-gate /* default to AF_INET6 */ 6960Sstevel@tonic-gate addr.sa.sa_family = AF_INET6; 6970Sstevel@tonic-gate # else /* NETINET6 */ 6980Sstevel@tonic-gate /* no protocols available */ 6990Sstevel@tonic-gate if (MilterLogLevel > 0) 7000Sstevel@tonic-gate sm_syslog(LOG_ERR, e->e_id, 7010Sstevel@tonic-gate "Milter (%s): no valid socket protocols available", 7020Sstevel@tonic-gate m->mf_name); 7030Sstevel@tonic-gate milter_error(m, e); 7040Sstevel@tonic-gate return -1; 7050Sstevel@tonic-gate # endif /* NETINET6 */ 7060Sstevel@tonic-gate # endif /* NETINET */ 7070Sstevel@tonic-gate # endif /* NETUNIX */ 7080Sstevel@tonic-gate } 7090Sstevel@tonic-gate # if NETUNIX 7100Sstevel@tonic-gate else if (sm_strcasecmp(p, "unix") == 0 || 7110Sstevel@tonic-gate sm_strcasecmp(p, "local") == 0) 7120Sstevel@tonic-gate addr.sa.sa_family = AF_UNIX; 7130Sstevel@tonic-gate # endif /* NETUNIX */ 7140Sstevel@tonic-gate # if NETINET 7150Sstevel@tonic-gate else if (sm_strcasecmp(p, "inet") == 0) 7160Sstevel@tonic-gate addr.sa.sa_family = AF_INET; 7170Sstevel@tonic-gate # endif /* NETINET */ 7180Sstevel@tonic-gate # if NETINET6 7190Sstevel@tonic-gate else if (sm_strcasecmp(p, "inet6") == 0) 7200Sstevel@tonic-gate addr.sa.sa_family = AF_INET6; 7210Sstevel@tonic-gate # endif /* NETINET6 */ 7220Sstevel@tonic-gate else 7230Sstevel@tonic-gate { 7240Sstevel@tonic-gate # ifdef EPROTONOSUPPORT 7250Sstevel@tonic-gate errno = EPROTONOSUPPORT; 7260Sstevel@tonic-gate # else /* EPROTONOSUPPORT */ 7270Sstevel@tonic-gate errno = EINVAL; 7280Sstevel@tonic-gate # endif /* EPROTONOSUPPORT */ 7290Sstevel@tonic-gate if (tTd(64, 5)) 7300Sstevel@tonic-gate sm_dprintf("X%s: unknown socket type %s\n", 7310Sstevel@tonic-gate m->mf_name, p); 7320Sstevel@tonic-gate if (parseonly) 7330Sstevel@tonic-gate syserr("X%s: unknown socket type %s", 7340Sstevel@tonic-gate m->mf_name, p); 7350Sstevel@tonic-gate else if (MilterLogLevel > 0) 7360Sstevel@tonic-gate sm_syslog(LOG_ERR, e->e_id, 7370Sstevel@tonic-gate "Milter (%s): unknown socket type %s", 7380Sstevel@tonic-gate m->mf_name, p); 7390Sstevel@tonic-gate milter_error(m, e); 7400Sstevel@tonic-gate return -1; 7410Sstevel@tonic-gate } 7420Sstevel@tonic-gate *colon++ = ':'; 7430Sstevel@tonic-gate } 7440Sstevel@tonic-gate else 7450Sstevel@tonic-gate { 7460Sstevel@tonic-gate /* default to AF_UNIX */ 7470Sstevel@tonic-gate addr.sa.sa_family = AF_UNIX; 7480Sstevel@tonic-gate colon = p; 7490Sstevel@tonic-gate } 7500Sstevel@tonic-gate 7510Sstevel@tonic-gate # if NETUNIX 7520Sstevel@tonic-gate if (addr.sa.sa_family == AF_UNIX) 7530Sstevel@tonic-gate { 7540Sstevel@tonic-gate long sff = SFF_SAFEDIRPATH|SFF_OPENASROOT|SFF_NOLINK|SFF_EXECOK; 7550Sstevel@tonic-gate 7560Sstevel@tonic-gate at = colon; 757*3544Sjbeck if (strlen(colon) >= sizeof(addr.sunix.sun_path)) 7580Sstevel@tonic-gate { 7590Sstevel@tonic-gate if (tTd(64, 5)) 7600Sstevel@tonic-gate sm_dprintf("X%s: local socket name %s too long\n", 7610Sstevel@tonic-gate m->mf_name, colon); 7620Sstevel@tonic-gate errno = EINVAL; 7630Sstevel@tonic-gate if (parseonly) 7640Sstevel@tonic-gate syserr("X%s: local socket name %s too long", 7650Sstevel@tonic-gate m->mf_name, colon); 7660Sstevel@tonic-gate else if (MilterLogLevel > 0) 7670Sstevel@tonic-gate sm_syslog(LOG_ERR, e->e_id, 7680Sstevel@tonic-gate "Milter (%s): local socket name %s too long", 7690Sstevel@tonic-gate m->mf_name, colon); 7700Sstevel@tonic-gate milter_error(m, e); 7710Sstevel@tonic-gate return -1; 7720Sstevel@tonic-gate } 7730Sstevel@tonic-gate errno = safefile(colon, RunAsUid, RunAsGid, RunAsUserName, sff, 7740Sstevel@tonic-gate S_IRUSR|S_IWUSR, NULL); 7750Sstevel@tonic-gate 7760Sstevel@tonic-gate /* if just parsing .cf file, socket doesn't need to exist */ 7770Sstevel@tonic-gate if (parseonly && errno == ENOENT) 7780Sstevel@tonic-gate { 7790Sstevel@tonic-gate if (OpMode == MD_DAEMON || 7800Sstevel@tonic-gate OpMode == MD_FGDAEMON) 7810Sstevel@tonic-gate (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, 7820Sstevel@tonic-gate "WARNING: X%s: local socket name %s missing\n", 7830Sstevel@tonic-gate m->mf_name, colon); 7840Sstevel@tonic-gate } 7850Sstevel@tonic-gate else if (errno != 0) 7860Sstevel@tonic-gate { 7870Sstevel@tonic-gate /* if not safe, don't create */ 7880Sstevel@tonic-gate save_errno = errno; 7890Sstevel@tonic-gate if (tTd(64, 5)) 7900Sstevel@tonic-gate sm_dprintf("X%s: local socket name %s unsafe\n", 7910Sstevel@tonic-gate m->mf_name, colon); 7920Sstevel@tonic-gate errno = save_errno; 7930Sstevel@tonic-gate if (parseonly) 7940Sstevel@tonic-gate { 7950Sstevel@tonic-gate if (OpMode == MD_DAEMON || 7960Sstevel@tonic-gate OpMode == MD_FGDAEMON || 7970Sstevel@tonic-gate OpMode == MD_SMTP) 7980Sstevel@tonic-gate syserr("X%s: local socket name %s unsafe", 7990Sstevel@tonic-gate m->mf_name, colon); 8000Sstevel@tonic-gate } 8010Sstevel@tonic-gate else if (MilterLogLevel > 0) 8020Sstevel@tonic-gate sm_syslog(LOG_ERR, e->e_id, 8030Sstevel@tonic-gate "Milter (%s): local socket name %s unsafe", 8040Sstevel@tonic-gate m->mf_name, colon); 8050Sstevel@tonic-gate milter_error(m, e); 8060Sstevel@tonic-gate return -1; 8070Sstevel@tonic-gate } 8080Sstevel@tonic-gate 8090Sstevel@tonic-gate (void) sm_strlcpy(addr.sunix.sun_path, colon, 810*3544Sjbeck sizeof(addr.sunix.sun_path)); 811*3544Sjbeck addrlen = sizeof(struct sockaddr_un); 8120Sstevel@tonic-gate } 8130Sstevel@tonic-gate else 8140Sstevel@tonic-gate # endif /* NETUNIX */ 8150Sstevel@tonic-gate # if NETINET || NETINET6 8160Sstevel@tonic-gate if (false 8170Sstevel@tonic-gate # if NETINET 8180Sstevel@tonic-gate || addr.sa.sa_family == AF_INET 8190Sstevel@tonic-gate # endif /* NETINET */ 8200Sstevel@tonic-gate # if NETINET6 8210Sstevel@tonic-gate || addr.sa.sa_family == AF_INET6 8220Sstevel@tonic-gate # endif /* NETINET6 */ 8230Sstevel@tonic-gate ) 8240Sstevel@tonic-gate { 8250Sstevel@tonic-gate unsigned short port; 8260Sstevel@tonic-gate 8270Sstevel@tonic-gate /* Parse port@host */ 8280Sstevel@tonic-gate at = strchr(colon, '@'); 8290Sstevel@tonic-gate if (at == NULL) 8300Sstevel@tonic-gate { 8310Sstevel@tonic-gate if (tTd(64, 5)) 8320Sstevel@tonic-gate sm_dprintf("X%s: bad address %s (expected port@host)\n", 8330Sstevel@tonic-gate m->mf_name, colon); 8340Sstevel@tonic-gate if (parseonly) 8350Sstevel@tonic-gate syserr("X%s: bad address %s (expected port@host)", 8360Sstevel@tonic-gate m->mf_name, colon); 8370Sstevel@tonic-gate else if (MilterLogLevel > 0) 8380Sstevel@tonic-gate sm_syslog(LOG_ERR, e->e_id, 8390Sstevel@tonic-gate "Milter (%s): bad address %s (expected port@host)", 8400Sstevel@tonic-gate m->mf_name, colon); 8410Sstevel@tonic-gate milter_error(m, e); 8420Sstevel@tonic-gate return -1; 8430Sstevel@tonic-gate } 8440Sstevel@tonic-gate *at = '\0'; 8450Sstevel@tonic-gate if (isascii(*colon) && isdigit(*colon)) 8460Sstevel@tonic-gate port = htons((unsigned short) atoi(colon)); 8470Sstevel@tonic-gate else 8480Sstevel@tonic-gate { 8490Sstevel@tonic-gate # ifdef NO_GETSERVBYNAME 8500Sstevel@tonic-gate if (tTd(64, 5)) 8510Sstevel@tonic-gate sm_dprintf("X%s: invalid port number %s\n", 8520Sstevel@tonic-gate m->mf_name, colon); 8530Sstevel@tonic-gate if (parseonly) 8540Sstevel@tonic-gate syserr("X%s: invalid port number %s", 8550Sstevel@tonic-gate m->mf_name, colon); 8560Sstevel@tonic-gate else if (MilterLogLevel > 0) 8570Sstevel@tonic-gate sm_syslog(LOG_ERR, e->e_id, 8580Sstevel@tonic-gate "Milter (%s): invalid port number %s", 8590Sstevel@tonic-gate m->mf_name, colon); 8600Sstevel@tonic-gate milter_error(m, e); 8610Sstevel@tonic-gate return -1; 8620Sstevel@tonic-gate # else /* NO_GETSERVBYNAME */ 863*3544Sjbeck struct servent *sp; 8640Sstevel@tonic-gate 8650Sstevel@tonic-gate sp = getservbyname(colon, "tcp"); 8660Sstevel@tonic-gate if (sp == NULL) 8670Sstevel@tonic-gate { 8680Sstevel@tonic-gate save_errno = errno; 8690Sstevel@tonic-gate if (tTd(64, 5)) 8700Sstevel@tonic-gate sm_dprintf("X%s: unknown port name %s\n", 8710Sstevel@tonic-gate m->mf_name, colon); 8720Sstevel@tonic-gate errno = save_errno; 8730Sstevel@tonic-gate if (parseonly) 8740Sstevel@tonic-gate syserr("X%s: unknown port name %s", 8750Sstevel@tonic-gate m->mf_name, colon); 8760Sstevel@tonic-gate else if (MilterLogLevel > 0) 8770Sstevel@tonic-gate sm_syslog(LOG_ERR, e->e_id, 8780Sstevel@tonic-gate "Milter (%s): unknown port name %s", 8790Sstevel@tonic-gate m->mf_name, colon); 8800Sstevel@tonic-gate milter_error(m, e); 8810Sstevel@tonic-gate return -1; 8820Sstevel@tonic-gate } 8830Sstevel@tonic-gate port = sp->s_port; 8840Sstevel@tonic-gate # endif /* NO_GETSERVBYNAME */ 8850Sstevel@tonic-gate } 8860Sstevel@tonic-gate *at++ = '@'; 8870Sstevel@tonic-gate if (*at == '[') 8880Sstevel@tonic-gate { 8890Sstevel@tonic-gate char *end; 8900Sstevel@tonic-gate 8910Sstevel@tonic-gate end = strchr(at, ']'); 8920Sstevel@tonic-gate if (end != NULL) 8930Sstevel@tonic-gate { 8940Sstevel@tonic-gate bool found = false; 8950Sstevel@tonic-gate # if NETINET 8960Sstevel@tonic-gate unsigned long hid = INADDR_NONE; 8970Sstevel@tonic-gate # endif /* NETINET */ 8980Sstevel@tonic-gate # if NETINET6 8990Sstevel@tonic-gate struct sockaddr_in6 hid6; 9000Sstevel@tonic-gate # endif /* NETINET6 */ 9010Sstevel@tonic-gate 9020Sstevel@tonic-gate *end = '\0'; 9030Sstevel@tonic-gate # if NETINET 9040Sstevel@tonic-gate if (addr.sa.sa_family == AF_INET && 9050Sstevel@tonic-gate (hid = inet_addr(&at[1])) != INADDR_NONE) 9060Sstevel@tonic-gate { 9070Sstevel@tonic-gate addr.sin.sin_addr.s_addr = hid; 9080Sstevel@tonic-gate addr.sin.sin_port = port; 9090Sstevel@tonic-gate found = true; 9100Sstevel@tonic-gate } 9110Sstevel@tonic-gate # endif /* NETINET */ 9120Sstevel@tonic-gate # if NETINET6 913*3544Sjbeck (void) memset(&hid6, '\0', sizeof(hid6)); 9140Sstevel@tonic-gate if (addr.sa.sa_family == AF_INET6 && 9150Sstevel@tonic-gate anynet_pton(AF_INET6, &at[1], 9160Sstevel@tonic-gate &hid6.sin6_addr) == 1) 9170Sstevel@tonic-gate { 9180Sstevel@tonic-gate addr.sin6.sin6_addr = hid6.sin6_addr; 9190Sstevel@tonic-gate addr.sin6.sin6_port = port; 9200Sstevel@tonic-gate found = true; 9210Sstevel@tonic-gate } 9220Sstevel@tonic-gate # endif /* NETINET6 */ 9230Sstevel@tonic-gate *end = ']'; 9240Sstevel@tonic-gate if (!found) 9250Sstevel@tonic-gate { 9260Sstevel@tonic-gate if (tTd(64, 5)) 9270Sstevel@tonic-gate sm_dprintf("X%s: Invalid numeric domain spec \"%s\"\n", 9280Sstevel@tonic-gate m->mf_name, at); 9290Sstevel@tonic-gate if (parseonly) 9300Sstevel@tonic-gate syserr("X%s: Invalid numeric domain spec \"%s\"", 9310Sstevel@tonic-gate m->mf_name, at); 9320Sstevel@tonic-gate else if (MilterLogLevel > 0) 9330Sstevel@tonic-gate sm_syslog(LOG_ERR, e->e_id, 9340Sstevel@tonic-gate "Milter (%s): Invalid numeric domain spec \"%s\"", 9350Sstevel@tonic-gate m->mf_name, at); 9360Sstevel@tonic-gate milter_error(m, e); 9370Sstevel@tonic-gate return -1; 9380Sstevel@tonic-gate } 9390Sstevel@tonic-gate } 9400Sstevel@tonic-gate else 9410Sstevel@tonic-gate { 9420Sstevel@tonic-gate if (tTd(64, 5)) 9430Sstevel@tonic-gate sm_dprintf("X%s: Invalid numeric domain spec \"%s\"\n", 9440Sstevel@tonic-gate m->mf_name, at); 9450Sstevel@tonic-gate if (parseonly) 9460Sstevel@tonic-gate syserr("X%s: Invalid numeric domain spec \"%s\"", 9470Sstevel@tonic-gate m->mf_name, at); 9480Sstevel@tonic-gate else if (MilterLogLevel > 0) 9490Sstevel@tonic-gate sm_syslog(LOG_ERR, e->e_id, 9500Sstevel@tonic-gate "Milter (%s): Invalid numeric domain spec \"%s\"", 9510Sstevel@tonic-gate m->mf_name, at); 9520Sstevel@tonic-gate milter_error(m, e); 9530Sstevel@tonic-gate return -1; 9540Sstevel@tonic-gate } 9550Sstevel@tonic-gate } 9560Sstevel@tonic-gate else 9570Sstevel@tonic-gate { 9580Sstevel@tonic-gate hp = sm_gethostbyname(at, addr.sa.sa_family); 9590Sstevel@tonic-gate if (hp == NULL) 9600Sstevel@tonic-gate { 9610Sstevel@tonic-gate save_errno = errno; 9620Sstevel@tonic-gate if (tTd(64, 5)) 9630Sstevel@tonic-gate sm_dprintf("X%s: Unknown host name %s\n", 9640Sstevel@tonic-gate m->mf_name, at); 9650Sstevel@tonic-gate errno = save_errno; 9660Sstevel@tonic-gate if (parseonly) 9670Sstevel@tonic-gate syserr("X%s: Unknown host name %s", 9680Sstevel@tonic-gate m->mf_name, at); 9690Sstevel@tonic-gate else if (MilterLogLevel > 0) 9700Sstevel@tonic-gate sm_syslog(LOG_ERR, e->e_id, 9710Sstevel@tonic-gate "Milter (%s): Unknown host name %s", 9720Sstevel@tonic-gate m->mf_name, at); 9730Sstevel@tonic-gate milter_error(m, e); 9740Sstevel@tonic-gate return -1; 9750Sstevel@tonic-gate } 9760Sstevel@tonic-gate addr.sa.sa_family = hp->h_addrtype; 9770Sstevel@tonic-gate switch (hp->h_addrtype) 9780Sstevel@tonic-gate { 9790Sstevel@tonic-gate # if NETINET 9800Sstevel@tonic-gate case AF_INET: 9810Sstevel@tonic-gate memmove(&addr.sin.sin_addr, 9820Sstevel@tonic-gate hp->h_addr, INADDRSZ); 9830Sstevel@tonic-gate addr.sin.sin_port = port; 984*3544Sjbeck addrlen = sizeof(struct sockaddr_in); 9850Sstevel@tonic-gate addrno = 1; 9860Sstevel@tonic-gate break; 9870Sstevel@tonic-gate # endif /* NETINET */ 9880Sstevel@tonic-gate 9890Sstevel@tonic-gate # if NETINET6 9900Sstevel@tonic-gate case AF_INET6: 9910Sstevel@tonic-gate memmove(&addr.sin6.sin6_addr, 9920Sstevel@tonic-gate hp->h_addr, IN6ADDRSZ); 9930Sstevel@tonic-gate addr.sin6.sin6_port = port; 994*3544Sjbeck addrlen = sizeof(struct sockaddr_in6); 9950Sstevel@tonic-gate addrno = 1; 9960Sstevel@tonic-gate break; 9970Sstevel@tonic-gate # endif /* NETINET6 */ 9980Sstevel@tonic-gate 9990Sstevel@tonic-gate default: 10000Sstevel@tonic-gate if (tTd(64, 5)) 10010Sstevel@tonic-gate sm_dprintf("X%s: Unknown protocol for %s (%d)\n", 10020Sstevel@tonic-gate m->mf_name, at, 10030Sstevel@tonic-gate hp->h_addrtype); 10040Sstevel@tonic-gate if (parseonly) 10050Sstevel@tonic-gate syserr("X%s: Unknown protocol for %s (%d)", 10060Sstevel@tonic-gate m->mf_name, at, hp->h_addrtype); 10070Sstevel@tonic-gate else if (MilterLogLevel > 0) 10080Sstevel@tonic-gate sm_syslog(LOG_ERR, e->e_id, 10090Sstevel@tonic-gate "Milter (%s): Unknown protocol for %s (%d)", 10100Sstevel@tonic-gate m->mf_name, at, 10110Sstevel@tonic-gate hp->h_addrtype); 10120Sstevel@tonic-gate milter_error(m, e); 10130Sstevel@tonic-gate # if NETINET6 10140Sstevel@tonic-gate freehostent(hp); 10150Sstevel@tonic-gate # endif /* NETINET6 */ 10160Sstevel@tonic-gate return -1; 10170Sstevel@tonic-gate } 10180Sstevel@tonic-gate } 10190Sstevel@tonic-gate } 10200Sstevel@tonic-gate else 10210Sstevel@tonic-gate # endif /* NETINET || NETINET6 */ 10220Sstevel@tonic-gate { 10230Sstevel@tonic-gate if (tTd(64, 5)) 10240Sstevel@tonic-gate sm_dprintf("X%s: unknown socket protocol\n", 10250Sstevel@tonic-gate m->mf_name); 10260Sstevel@tonic-gate if (parseonly) 10270Sstevel@tonic-gate syserr("X%s: unknown socket protocol", m->mf_name); 10280Sstevel@tonic-gate else if (MilterLogLevel > 0) 10290Sstevel@tonic-gate sm_syslog(LOG_ERR, e->e_id, 10300Sstevel@tonic-gate "Milter (%s): unknown socket protocol", 10310Sstevel@tonic-gate m->mf_name); 10320Sstevel@tonic-gate milter_error(m, e); 10330Sstevel@tonic-gate return -1; 10340Sstevel@tonic-gate } 10350Sstevel@tonic-gate 10360Sstevel@tonic-gate /* just parsing through? */ 10370Sstevel@tonic-gate if (parseonly) 10380Sstevel@tonic-gate { 10390Sstevel@tonic-gate m->mf_state = SMFS_READY; 10400Sstevel@tonic-gate # if NETINET6 10410Sstevel@tonic-gate if (hp != NULL) 10420Sstevel@tonic-gate freehostent(hp); 10430Sstevel@tonic-gate # endif /* NETINET6 */ 10440Sstevel@tonic-gate return 0; 10450Sstevel@tonic-gate } 10460Sstevel@tonic-gate 10470Sstevel@tonic-gate /* sanity check */ 10480Sstevel@tonic-gate if (m->mf_state != SMFS_READY && 10490Sstevel@tonic-gate m->mf_state != SMFS_CLOSED) 10500Sstevel@tonic-gate { 10510Sstevel@tonic-gate /* shouldn't happen */ 10520Sstevel@tonic-gate if (tTd(64, 1)) 10530Sstevel@tonic-gate sm_dprintf("Milter (%s): Trying to open filter in state %c\n", 10540Sstevel@tonic-gate m->mf_name, (char) m->mf_state); 10550Sstevel@tonic-gate milter_error(m, e); 10560Sstevel@tonic-gate # if NETINET6 10570Sstevel@tonic-gate if (hp != NULL) 10580Sstevel@tonic-gate freehostent(hp); 10590Sstevel@tonic-gate # endif /* NETINET6 */ 10600Sstevel@tonic-gate return -1; 10610Sstevel@tonic-gate } 10620Sstevel@tonic-gate 10630Sstevel@tonic-gate /* nope, actually connecting */ 10640Sstevel@tonic-gate for (;;) 10650Sstevel@tonic-gate { 10660Sstevel@tonic-gate sock = socket(addr.sa.sa_family, SOCK_STREAM, 0); 10670Sstevel@tonic-gate if (sock < 0) 10680Sstevel@tonic-gate { 10690Sstevel@tonic-gate save_errno = errno; 10700Sstevel@tonic-gate if (tTd(64, 5)) 10710Sstevel@tonic-gate sm_dprintf("Milter (%s): error creating socket: %s\n", 10720Sstevel@tonic-gate m->mf_name, 10730Sstevel@tonic-gate sm_errstring(save_errno)); 10740Sstevel@tonic-gate if (MilterLogLevel > 0) 10750Sstevel@tonic-gate sm_syslog(LOG_ERR, e->e_id, 10760Sstevel@tonic-gate "Milter (%s): error creating socket: %s", 10770Sstevel@tonic-gate m->mf_name, sm_errstring(save_errno)); 10780Sstevel@tonic-gate milter_error(m, e); 10790Sstevel@tonic-gate # if NETINET6 10800Sstevel@tonic-gate if (hp != NULL) 10810Sstevel@tonic-gate freehostent(hp); 10820Sstevel@tonic-gate # endif /* NETINET6 */ 10830Sstevel@tonic-gate return -1; 10840Sstevel@tonic-gate } 10850Sstevel@tonic-gate 10860Sstevel@tonic-gate if (setjmp(MilterConnectTimeout) == 0) 10870Sstevel@tonic-gate { 10880Sstevel@tonic-gate SM_EVENT *ev = NULL; 10890Sstevel@tonic-gate int i; 10900Sstevel@tonic-gate 10910Sstevel@tonic-gate if (m->mf_timeout[SMFTO_CONNECT] > 0) 10920Sstevel@tonic-gate ev = sm_setevent(m->mf_timeout[SMFTO_CONNECT], 10930Sstevel@tonic-gate milter_connect_timeout, 0); 10940Sstevel@tonic-gate 10950Sstevel@tonic-gate i = connect(sock, (struct sockaddr *) &addr, addrlen); 10960Sstevel@tonic-gate save_errno = errno; 10970Sstevel@tonic-gate if (ev != NULL) 10980Sstevel@tonic-gate sm_clrevent(ev); 10990Sstevel@tonic-gate errno = save_errno; 11000Sstevel@tonic-gate if (i >= 0) 11010Sstevel@tonic-gate break; 11020Sstevel@tonic-gate } 11030Sstevel@tonic-gate 11040Sstevel@tonic-gate /* couldn't connect.... try next address */ 11050Sstevel@tonic-gate save_errno = errno; 11060Sstevel@tonic-gate p = CurHostName; 11070Sstevel@tonic-gate CurHostName = at; 11080Sstevel@tonic-gate if (tTd(64, 5)) 11090Sstevel@tonic-gate sm_dprintf("milter_open (%s): open %s failed: %s\n", 11100Sstevel@tonic-gate m->mf_name, at, sm_errstring(save_errno)); 11110Sstevel@tonic-gate if (MilterLogLevel > 13) 11120Sstevel@tonic-gate sm_syslog(LOG_INFO, e->e_id, 11130Sstevel@tonic-gate "Milter (%s): open %s failed: %s", 11140Sstevel@tonic-gate m->mf_name, at, sm_errstring(save_errno)); 11150Sstevel@tonic-gate CurHostName = p; 11160Sstevel@tonic-gate (void) close(sock); 11170Sstevel@tonic-gate 11180Sstevel@tonic-gate /* try next address */ 11190Sstevel@tonic-gate if (hp != NULL && hp->h_addr_list[addrno] != NULL) 11200Sstevel@tonic-gate { 11210Sstevel@tonic-gate switch (addr.sa.sa_family) 11220Sstevel@tonic-gate { 11230Sstevel@tonic-gate # if NETINET 11240Sstevel@tonic-gate case AF_INET: 11250Sstevel@tonic-gate memmove(&addr.sin.sin_addr, 11260Sstevel@tonic-gate hp->h_addr_list[addrno++], 11270Sstevel@tonic-gate INADDRSZ); 11280Sstevel@tonic-gate break; 11290Sstevel@tonic-gate # endif /* NETINET */ 11300Sstevel@tonic-gate 11310Sstevel@tonic-gate # if NETINET6 11320Sstevel@tonic-gate case AF_INET6: 11330Sstevel@tonic-gate memmove(&addr.sin6.sin6_addr, 11340Sstevel@tonic-gate hp->h_addr_list[addrno++], 11350Sstevel@tonic-gate IN6ADDRSZ); 11360Sstevel@tonic-gate break; 11370Sstevel@tonic-gate # endif /* NETINET6 */ 11380Sstevel@tonic-gate 11390Sstevel@tonic-gate default: 11400Sstevel@tonic-gate if (tTd(64, 5)) 11410Sstevel@tonic-gate sm_dprintf("X%s: Unknown protocol for %s (%d)\n", 11420Sstevel@tonic-gate m->mf_name, at, 11430Sstevel@tonic-gate hp->h_addrtype); 11440Sstevel@tonic-gate if (MilterLogLevel > 0) 11450Sstevel@tonic-gate sm_syslog(LOG_ERR, e->e_id, 11460Sstevel@tonic-gate "Milter (%s): Unknown protocol for %s (%d)", 11470Sstevel@tonic-gate m->mf_name, at, 11480Sstevel@tonic-gate hp->h_addrtype); 11490Sstevel@tonic-gate milter_error(m, e); 11500Sstevel@tonic-gate # if NETINET6 11510Sstevel@tonic-gate freehostent(hp); 11520Sstevel@tonic-gate # endif /* NETINET6 */ 11530Sstevel@tonic-gate return -1; 11540Sstevel@tonic-gate } 11550Sstevel@tonic-gate continue; 11560Sstevel@tonic-gate } 11570Sstevel@tonic-gate p = CurHostName; 11580Sstevel@tonic-gate CurHostName = at; 11590Sstevel@tonic-gate if (tTd(64, 5)) 11600Sstevel@tonic-gate sm_dprintf("X%s: error connecting to filter: %s\n", 11610Sstevel@tonic-gate m->mf_name, sm_errstring(save_errno)); 11620Sstevel@tonic-gate if (MilterLogLevel > 0) 11630Sstevel@tonic-gate sm_syslog(LOG_ERR, e->e_id, 11640Sstevel@tonic-gate "Milter (%s): error connecting to filter: %s", 11650Sstevel@tonic-gate m->mf_name, sm_errstring(save_errno)); 11660Sstevel@tonic-gate CurHostName = p; 11670Sstevel@tonic-gate milter_error(m, e); 11680Sstevel@tonic-gate # if NETINET6 11690Sstevel@tonic-gate if (hp != NULL) 11700Sstevel@tonic-gate freehostent(hp); 11710Sstevel@tonic-gate # endif /* NETINET6 */ 11720Sstevel@tonic-gate return -1; 11730Sstevel@tonic-gate } 11740Sstevel@tonic-gate m->mf_state = SMFS_OPEN; 11750Sstevel@tonic-gate # if NETINET6 11760Sstevel@tonic-gate if (hp != NULL) 11770Sstevel@tonic-gate { 11780Sstevel@tonic-gate freehostent(hp); 11790Sstevel@tonic-gate hp = NULL; 11800Sstevel@tonic-gate } 11810Sstevel@tonic-gate # endif /* NETINET6 */ 1182*3544Sjbeck # if MILTER_NO_NAGLE && !defined(TCP_CORK) 11830Sstevel@tonic-gate { 11840Sstevel@tonic-gate int nodelay = 1; 11850Sstevel@tonic-gate 11860Sstevel@tonic-gate setsockopt(m->mf_sock, IPPROTO_TCP, TCP_NODELAY, 11870Sstevel@tonic-gate (char *)&nodelay, sizeof(nodelay)); 11880Sstevel@tonic-gate } 1189*3544Sjbeck # endif /* MILTER_NO_NAGLE && !defined(TCP_CORK) */ 11900Sstevel@tonic-gate return sock; 11910Sstevel@tonic-gate } 11920Sstevel@tonic-gate 11930Sstevel@tonic-gate static void 11940Sstevel@tonic-gate milter_connect_timeout(ignore) 11950Sstevel@tonic-gate int ignore; 11960Sstevel@tonic-gate { 11970Sstevel@tonic-gate /* 11980Sstevel@tonic-gate ** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD 11990Sstevel@tonic-gate ** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE 12000Sstevel@tonic-gate ** DOING. 12010Sstevel@tonic-gate */ 12020Sstevel@tonic-gate 12030Sstevel@tonic-gate errno = ETIMEDOUT; 12040Sstevel@tonic-gate longjmp(MilterConnectTimeout, 1); 12050Sstevel@tonic-gate } 1206*3544Sjbeck 12070Sstevel@tonic-gate /* 12080Sstevel@tonic-gate ** MILTER_SETUP -- setup structure for a mail filter 12090Sstevel@tonic-gate ** 12100Sstevel@tonic-gate ** Parameters: 12110Sstevel@tonic-gate ** line -- the options line. 12120Sstevel@tonic-gate ** 12130Sstevel@tonic-gate ** Returns: 12140Sstevel@tonic-gate ** none 12150Sstevel@tonic-gate */ 12160Sstevel@tonic-gate 12170Sstevel@tonic-gate void 12180Sstevel@tonic-gate milter_setup(line) 12190Sstevel@tonic-gate char *line; 12200Sstevel@tonic-gate { 12210Sstevel@tonic-gate char fcode; 1222*3544Sjbeck char *p; 1223*3544Sjbeck struct milter *m; 12240Sstevel@tonic-gate STAB *s; 12250Sstevel@tonic-gate 12260Sstevel@tonic-gate /* collect the filter name */ 12270Sstevel@tonic-gate for (p = line; 12280Sstevel@tonic-gate *p != '\0' && *p != ',' && !(isascii(*p) && isspace(*p)); 12290Sstevel@tonic-gate p++) 12300Sstevel@tonic-gate continue; 12310Sstevel@tonic-gate if (*p != '\0') 12320Sstevel@tonic-gate *p++ = '\0'; 12330Sstevel@tonic-gate if (line[0] == '\0') 12340Sstevel@tonic-gate { 12350Sstevel@tonic-gate syserr("name required for mail filter"); 12360Sstevel@tonic-gate return; 12370Sstevel@tonic-gate } 1238*3544Sjbeck m = (struct milter *) xalloc(sizeof(*m)); 1239*3544Sjbeck memset((char *) m, '\0', sizeof(*m)); 12400Sstevel@tonic-gate m->mf_name = newstr(line); 12410Sstevel@tonic-gate m->mf_state = SMFS_READY; 12420Sstevel@tonic-gate m->mf_sock = -1; 12430Sstevel@tonic-gate m->mf_timeout[SMFTO_CONNECT] = (time_t) 300; 12440Sstevel@tonic-gate m->mf_timeout[SMFTO_WRITE] = (time_t) 10; 12450Sstevel@tonic-gate m->mf_timeout[SMFTO_READ] = (time_t) 10; 12460Sstevel@tonic-gate m->mf_timeout[SMFTO_EOM] = (time_t) 300; 1247*3544Sjbeck #if MILTER_CHECK 1248*3544Sjbeck m->mf_mta_prot_version = SMFI_PROT_VERSION; 1249*3544Sjbeck m->mf_mta_prot_flags = SMFI_CURR_PROT; 1250*3544Sjbeck m->mf_mta_actions = SMFI_CURR_ACTS; 1251*3544Sjbeck #endif /* MILTER_CHECK */ 12520Sstevel@tonic-gate 12530Sstevel@tonic-gate /* now scan through and assign info from the fields */ 12540Sstevel@tonic-gate while (*p != '\0') 12550Sstevel@tonic-gate { 12560Sstevel@tonic-gate char *delimptr; 12570Sstevel@tonic-gate 12580Sstevel@tonic-gate while (*p != '\0' && 12590Sstevel@tonic-gate (*p == ',' || (isascii(*p) && isspace(*p)))) 12600Sstevel@tonic-gate p++; 12610Sstevel@tonic-gate 12620Sstevel@tonic-gate /* p now points to field code */ 12630Sstevel@tonic-gate fcode = *p; 12640Sstevel@tonic-gate while (*p != '\0' && *p != '=' && *p != ',') 12650Sstevel@tonic-gate p++; 12660Sstevel@tonic-gate if (*p++ != '=') 12670Sstevel@tonic-gate { 12680Sstevel@tonic-gate syserr("X%s: `=' expected", m->mf_name); 12690Sstevel@tonic-gate return; 12700Sstevel@tonic-gate } 12710Sstevel@tonic-gate while (isascii(*p) && isspace(*p)) 12720Sstevel@tonic-gate p++; 12730Sstevel@tonic-gate 12740Sstevel@tonic-gate /* p now points to the field body */ 12750Sstevel@tonic-gate p = munchstring(p, &delimptr, ','); 12760Sstevel@tonic-gate 12770Sstevel@tonic-gate /* install the field into the filter struct */ 12780Sstevel@tonic-gate switch (fcode) 12790Sstevel@tonic-gate { 12800Sstevel@tonic-gate case 'S': /* socket */ 12810Sstevel@tonic-gate if (p == NULL) 12820Sstevel@tonic-gate m->mf_conn = NULL; 12830Sstevel@tonic-gate else 12840Sstevel@tonic-gate m->mf_conn = newstr(p); 12850Sstevel@tonic-gate break; 12860Sstevel@tonic-gate 12870Sstevel@tonic-gate case 'F': /* Milter flags configured on MTA */ 12880Sstevel@tonic-gate for (; *p != '\0'; p++) 12890Sstevel@tonic-gate { 12900Sstevel@tonic-gate if (!(isascii(*p) && isspace(*p))) 12910Sstevel@tonic-gate setbitn(bitidx(*p), m->mf_flags); 12920Sstevel@tonic-gate } 12930Sstevel@tonic-gate break; 12940Sstevel@tonic-gate 12950Sstevel@tonic-gate case 'T': /* timeouts */ 12960Sstevel@tonic-gate milter_parse_timeouts(p, m); 12970Sstevel@tonic-gate break; 12980Sstevel@tonic-gate 1299*3544Sjbeck #if MILTER_CHECK 1300*3544Sjbeck case 'a': 1301*3544Sjbeck m->mf_mta_actions = strtoul(p, NULL, 0); 1302*3544Sjbeck break; 1303*3544Sjbeck case 'f': 1304*3544Sjbeck m->mf_mta_prot_flags = strtoul(p, NULL, 0); 1305*3544Sjbeck break; 1306*3544Sjbeck case 'v': 1307*3544Sjbeck m->mf_mta_prot_version = strtoul(p, NULL, 0); 1308*3544Sjbeck break; 1309*3544Sjbeck #endif /* MILTER_CHECK */ 1310*3544Sjbeck 13110Sstevel@tonic-gate default: 13120Sstevel@tonic-gate syserr("X%s: unknown filter equate %c=", 13130Sstevel@tonic-gate m->mf_name, fcode); 13140Sstevel@tonic-gate break; 13150Sstevel@tonic-gate } 13160Sstevel@tonic-gate p = delimptr; 13170Sstevel@tonic-gate } 13180Sstevel@tonic-gate 13190Sstevel@tonic-gate /* early check for errors */ 13200Sstevel@tonic-gate (void) milter_open(m, true, CurEnv); 13210Sstevel@tonic-gate 13220Sstevel@tonic-gate /* enter the filter into the symbol table */ 13230Sstevel@tonic-gate s = stab(m->mf_name, ST_MILTER, ST_ENTER); 13240Sstevel@tonic-gate if (s->s_milter != NULL) 13250Sstevel@tonic-gate syserr("X%s: duplicate filter definition", m->mf_name); 13260Sstevel@tonic-gate else 13270Sstevel@tonic-gate s->s_milter = m; 13280Sstevel@tonic-gate } 1329*3544Sjbeck 13300Sstevel@tonic-gate /* 13310Sstevel@tonic-gate ** MILTER_CONFIG -- parse option list into an array and check config 13320Sstevel@tonic-gate ** 13330Sstevel@tonic-gate ** Called when reading configuration file. 13340Sstevel@tonic-gate ** 13350Sstevel@tonic-gate ** Parameters: 13360Sstevel@tonic-gate ** spec -- the filter list. 13370Sstevel@tonic-gate ** list -- the array to fill in. 13380Sstevel@tonic-gate ** max -- the maximum number of entries in list. 13390Sstevel@tonic-gate ** 13400Sstevel@tonic-gate ** Returns: 13410Sstevel@tonic-gate ** none 13420Sstevel@tonic-gate */ 13430Sstevel@tonic-gate 13440Sstevel@tonic-gate void 13450Sstevel@tonic-gate milter_config(spec, list, max) 13460Sstevel@tonic-gate char *spec; 13470Sstevel@tonic-gate struct milter **list; 13480Sstevel@tonic-gate int max; 13490Sstevel@tonic-gate { 13500Sstevel@tonic-gate int numitems = 0; 1351*3544Sjbeck char *p; 13520Sstevel@tonic-gate 13530Sstevel@tonic-gate /* leave one for the NULL signifying the end of the list */ 13540Sstevel@tonic-gate max--; 13550Sstevel@tonic-gate 13560Sstevel@tonic-gate for (p = spec; p != NULL; ) 13570Sstevel@tonic-gate { 13580Sstevel@tonic-gate STAB *s; 13590Sstevel@tonic-gate 13600Sstevel@tonic-gate while (isascii(*p) && isspace(*p)) 13610Sstevel@tonic-gate p++; 13620Sstevel@tonic-gate if (*p == '\0') 13630Sstevel@tonic-gate break; 13640Sstevel@tonic-gate spec = p; 13650Sstevel@tonic-gate 13660Sstevel@tonic-gate if (numitems >= max) 13670Sstevel@tonic-gate { 13680Sstevel@tonic-gate syserr("Too many filters defined, %d max", max); 13690Sstevel@tonic-gate if (max > 0) 13700Sstevel@tonic-gate list[0] = NULL; 13710Sstevel@tonic-gate return; 13720Sstevel@tonic-gate } 13730Sstevel@tonic-gate p = strpbrk(p, ";,"); 13740Sstevel@tonic-gate if (p != NULL) 13750Sstevel@tonic-gate *p++ = '\0'; 13760Sstevel@tonic-gate 13770Sstevel@tonic-gate s = stab(spec, ST_MILTER, ST_FIND); 13780Sstevel@tonic-gate if (s == NULL) 13790Sstevel@tonic-gate { 13800Sstevel@tonic-gate syserr("InputFilter %s not defined", spec); 13810Sstevel@tonic-gate ExitStat = EX_CONFIG; 13820Sstevel@tonic-gate return; 13830Sstevel@tonic-gate } 13840Sstevel@tonic-gate list[numitems++] = s->s_milter; 13850Sstevel@tonic-gate } 13860Sstevel@tonic-gate list[numitems] = NULL; 13870Sstevel@tonic-gate 13880Sstevel@tonic-gate /* if not set, set to LogLevel */ 13890Sstevel@tonic-gate if (MilterLogLevel == -1) 13900Sstevel@tonic-gate MilterLogLevel = LogLevel; 13910Sstevel@tonic-gate } 1392*3544Sjbeck 13930Sstevel@tonic-gate /* 13940Sstevel@tonic-gate ** MILTER_PARSE_TIMEOUTS -- parse timeout list 13950Sstevel@tonic-gate ** 13960Sstevel@tonic-gate ** Called when reading configuration file. 13970Sstevel@tonic-gate ** 13980Sstevel@tonic-gate ** Parameters: 13990Sstevel@tonic-gate ** spec -- the timeout list. 14000Sstevel@tonic-gate ** m -- milter to set. 14010Sstevel@tonic-gate ** 14020Sstevel@tonic-gate ** Returns: 14030Sstevel@tonic-gate ** none 14040Sstevel@tonic-gate */ 14050Sstevel@tonic-gate 14060Sstevel@tonic-gate static void 14070Sstevel@tonic-gate milter_parse_timeouts(spec, m) 14080Sstevel@tonic-gate char *spec; 14090Sstevel@tonic-gate struct milter *m; 14100Sstevel@tonic-gate { 14110Sstevel@tonic-gate char fcode; 14120Sstevel@tonic-gate int tcode; 1413*3544Sjbeck char *p; 14140Sstevel@tonic-gate 14150Sstevel@tonic-gate p = spec; 14160Sstevel@tonic-gate 14170Sstevel@tonic-gate /* now scan through and assign info from the fields */ 14180Sstevel@tonic-gate while (*p != '\0') 14190Sstevel@tonic-gate { 14200Sstevel@tonic-gate char *delimptr; 14210Sstevel@tonic-gate 14220Sstevel@tonic-gate while (*p != '\0' && 14230Sstevel@tonic-gate (*p == ';' || (isascii(*p) && isspace(*p)))) 14240Sstevel@tonic-gate p++; 14250Sstevel@tonic-gate 14260Sstevel@tonic-gate /* p now points to field code */ 14270Sstevel@tonic-gate fcode = *p; 14280Sstevel@tonic-gate while (*p != '\0' && *p != ':') 14290Sstevel@tonic-gate p++; 14300Sstevel@tonic-gate if (*p++ != ':') 14310Sstevel@tonic-gate { 14320Sstevel@tonic-gate syserr("X%s, T=: `:' expected", m->mf_name); 14330Sstevel@tonic-gate return; 14340Sstevel@tonic-gate } 14350Sstevel@tonic-gate while (isascii(*p) && isspace(*p)) 14360Sstevel@tonic-gate p++; 14370Sstevel@tonic-gate 14380Sstevel@tonic-gate /* p now points to the field body */ 14390Sstevel@tonic-gate p = munchstring(p, &delimptr, ';'); 14400Sstevel@tonic-gate tcode = -1; 14410Sstevel@tonic-gate 14420Sstevel@tonic-gate /* install the field into the filter struct */ 14430Sstevel@tonic-gate switch (fcode) 14440Sstevel@tonic-gate { 14450Sstevel@tonic-gate case 'C': 14460Sstevel@tonic-gate tcode = SMFTO_CONNECT; 14470Sstevel@tonic-gate break; 14480Sstevel@tonic-gate 14490Sstevel@tonic-gate case 'S': 14500Sstevel@tonic-gate tcode = SMFTO_WRITE; 14510Sstevel@tonic-gate break; 14520Sstevel@tonic-gate 14530Sstevel@tonic-gate case 'R': 14540Sstevel@tonic-gate tcode = SMFTO_READ; 14550Sstevel@tonic-gate break; 14560Sstevel@tonic-gate 14570Sstevel@tonic-gate case 'E': 14580Sstevel@tonic-gate tcode = SMFTO_EOM; 14590Sstevel@tonic-gate break; 14600Sstevel@tonic-gate 14610Sstevel@tonic-gate default: 14620Sstevel@tonic-gate if (tTd(64, 5)) 14630Sstevel@tonic-gate sm_dprintf("X%s: %c unknown\n", 14640Sstevel@tonic-gate m->mf_name, fcode); 14650Sstevel@tonic-gate syserr("X%s: unknown filter timeout %c", 14660Sstevel@tonic-gate m->mf_name, fcode); 14670Sstevel@tonic-gate break; 14680Sstevel@tonic-gate } 14690Sstevel@tonic-gate if (tcode >= 0) 14700Sstevel@tonic-gate { 14710Sstevel@tonic-gate m->mf_timeout[tcode] = convtime(p, 's'); 14720Sstevel@tonic-gate if (tTd(64, 5)) 14730Sstevel@tonic-gate sm_dprintf("X%s: %c=%ld\n", 14740Sstevel@tonic-gate m->mf_name, fcode, 14750Sstevel@tonic-gate (u_long) m->mf_timeout[tcode]); 14760Sstevel@tonic-gate } 14770Sstevel@tonic-gate p = delimptr; 14780Sstevel@tonic-gate } 14790Sstevel@tonic-gate } 1480*3544Sjbeck 1481*3544Sjbeck /* 1482*3544Sjbeck ** MILTER_SET_MACROS -- set milter macros 1483*3544Sjbeck ** 1484*3544Sjbeck ** Parameters: 1485*3544Sjbeck ** name -- name of milter. 1486*3544Sjbeck ** macros -- where to store macros. 1487*3544Sjbeck ** val -- the value of the option. 1488*3544Sjbeck ** nummac -- current number of macros 1489*3544Sjbeck ** 1490*3544Sjbeck ** Returns: 1491*3544Sjbeck ** new number of macros 1492*3544Sjbeck */ 1493*3544Sjbeck 1494*3544Sjbeck static int 1495*3544Sjbeck milter_set_macros(name, macros, val, nummac) 1496*3544Sjbeck char *name; 1497*3544Sjbeck char **macros; 1498*3544Sjbeck char *val; 1499*3544Sjbeck int nummac; 1500*3544Sjbeck { 1501*3544Sjbeck char *p; 1502*3544Sjbeck 1503*3544Sjbeck p = newstr(val); 1504*3544Sjbeck while (*p != '\0') 1505*3544Sjbeck { 1506*3544Sjbeck char *macro; 1507*3544Sjbeck 1508*3544Sjbeck /* Skip leading commas, spaces */ 1509*3544Sjbeck while (*p != '\0' && 1510*3544Sjbeck (*p == ',' || (isascii(*p) && isspace(*p)))) 1511*3544Sjbeck p++; 1512*3544Sjbeck 1513*3544Sjbeck if (*p == '\0') 1514*3544Sjbeck break; 1515*3544Sjbeck 1516*3544Sjbeck /* Find end of macro */ 1517*3544Sjbeck macro = p; 1518*3544Sjbeck while (*p != '\0' && *p != ',' && 1519*3544Sjbeck isascii(*p) && !isspace(*p)) 1520*3544Sjbeck p++; 1521*3544Sjbeck if (*p != '\0') 1522*3544Sjbeck *p++ = '\0'; 1523*3544Sjbeck 1524*3544Sjbeck if (nummac >= MAXFILTERMACROS) 1525*3544Sjbeck { 1526*3544Sjbeck syserr("milter_set_option: too many macros in Milter.%s (max %d)", 1527*3544Sjbeck name, MAXFILTERMACROS); 1528*3544Sjbeck macros[nummac] = NULL; 1529*3544Sjbeck return -1; 1530*3544Sjbeck } 1531*3544Sjbeck macros[nummac++] = macro; 1532*3544Sjbeck } 1533*3544Sjbeck macros[nummac] = NULL; 1534*3544Sjbeck return nummac; 1535*3544Sjbeck } 1536*3544Sjbeck 15370Sstevel@tonic-gate /* 15380Sstevel@tonic-gate ** MILTER_SET_OPTION -- set an individual milter option 15390Sstevel@tonic-gate ** 15400Sstevel@tonic-gate ** Parameters: 15410Sstevel@tonic-gate ** name -- the name of the option. 15420Sstevel@tonic-gate ** val -- the value of the option. 15430Sstevel@tonic-gate ** sticky -- if set, don't let other setoptions override 15440Sstevel@tonic-gate ** this value. 15450Sstevel@tonic-gate ** 15460Sstevel@tonic-gate ** Returns: 15470Sstevel@tonic-gate ** none. 15480Sstevel@tonic-gate */ 15490Sstevel@tonic-gate 15500Sstevel@tonic-gate /* set if Milter sub-option is stuck */ 15510Sstevel@tonic-gate static BITMAP256 StickyMilterOpt; 15520Sstevel@tonic-gate 15530Sstevel@tonic-gate static struct milteropt 15540Sstevel@tonic-gate { 15550Sstevel@tonic-gate char *mo_name; /* long name of milter option */ 15560Sstevel@tonic-gate unsigned char mo_code; /* code for option */ 15570Sstevel@tonic-gate } MilterOptTab[] = 15580Sstevel@tonic-gate { 1559*3544Sjbeck # define MO_MACROS_CONNECT SMFIM_CONNECT 15600Sstevel@tonic-gate { "macros.connect", MO_MACROS_CONNECT }, 1561*3544Sjbeck # define MO_MACROS_HELO SMFIM_HELO 15620Sstevel@tonic-gate { "macros.helo", MO_MACROS_HELO }, 1563*3544Sjbeck # define MO_MACROS_ENVFROM SMFIM_ENVFROM 15640Sstevel@tonic-gate { "macros.envfrom", MO_MACROS_ENVFROM }, 1565*3544Sjbeck # define MO_MACROS_ENVRCPT SMFIM_ENVRCPT 15660Sstevel@tonic-gate { "macros.envrcpt", MO_MACROS_ENVRCPT }, 1567*3544Sjbeck # define MO_MACROS_DATA SMFIM_DATA 15680Sstevel@tonic-gate { "macros.data", MO_MACROS_DATA }, 1569*3544Sjbeck # define MO_MACROS_EOM SMFIM_EOM 15700Sstevel@tonic-gate { "macros.eom", MO_MACROS_EOM }, 1571*3544Sjbeck # define MO_MACROS_EOH SMFIM_EOH 1572*3544Sjbeck { "macros.eoh", MO_MACROS_EOH }, 1573*3544Sjbeck 15740Sstevel@tonic-gate # define MO_LOGLEVEL 0x07 15750Sstevel@tonic-gate { "loglevel", MO_LOGLEVEL }, 15760Sstevel@tonic-gate # if _FFR_MAXDATASIZE 1577*3544Sjbeck # define MO_MAXDATASIZE 0x08 15780Sstevel@tonic-gate { "maxdatasize", MO_MAXDATASIZE }, 15790Sstevel@tonic-gate # endif /* _FFR_MAXDATASIZE */ 1580*3544Sjbeck { NULL, (unsigned char)-1 }, 15810Sstevel@tonic-gate }; 15820Sstevel@tonic-gate 15830Sstevel@tonic-gate void 15840Sstevel@tonic-gate milter_set_option(name, val, sticky) 15850Sstevel@tonic-gate char *name; 15860Sstevel@tonic-gate char *val; 15870Sstevel@tonic-gate bool sticky; 15880Sstevel@tonic-gate { 1589*3544Sjbeck int nummac, r; 1590*3544Sjbeck struct milteropt *mo; 15910Sstevel@tonic-gate char **macros = NULL; 15920Sstevel@tonic-gate 1593*3544Sjbeck nummac = 0; 15940Sstevel@tonic-gate if (tTd(37, 2) || tTd(64, 5)) 15950Sstevel@tonic-gate sm_dprintf("milter_set_option(%s = %s)", name, val); 15960Sstevel@tonic-gate 15970Sstevel@tonic-gate if (name == NULL) 15980Sstevel@tonic-gate { 15990Sstevel@tonic-gate syserr("milter_set_option: invalid Milter option, must specify suboption"); 16000Sstevel@tonic-gate return; 16010Sstevel@tonic-gate } 16020Sstevel@tonic-gate 16030Sstevel@tonic-gate for (mo = MilterOptTab; mo->mo_name != NULL; mo++) 16040Sstevel@tonic-gate { 16050Sstevel@tonic-gate if (sm_strcasecmp(mo->mo_name, name) == 0) 16060Sstevel@tonic-gate break; 16070Sstevel@tonic-gate } 16080Sstevel@tonic-gate 16090Sstevel@tonic-gate if (mo->mo_name == NULL) 16100Sstevel@tonic-gate { 16110Sstevel@tonic-gate syserr("milter_set_option: invalid Milter option %s", name); 16120Sstevel@tonic-gate return; 16130Sstevel@tonic-gate } 16140Sstevel@tonic-gate 16150Sstevel@tonic-gate /* 16160Sstevel@tonic-gate ** See if this option is preset for us. 16170Sstevel@tonic-gate */ 16180Sstevel@tonic-gate 16190Sstevel@tonic-gate if (!sticky && bitnset(mo->mo_code, StickyMilterOpt)) 16200Sstevel@tonic-gate { 16210Sstevel@tonic-gate if (tTd(37, 2) || tTd(64,5)) 16220Sstevel@tonic-gate sm_dprintf(" (ignored)\n"); 16230Sstevel@tonic-gate return; 16240Sstevel@tonic-gate } 16250Sstevel@tonic-gate 16260Sstevel@tonic-gate if (tTd(37, 2) || tTd(64,5)) 16270Sstevel@tonic-gate sm_dprintf("\n"); 16280Sstevel@tonic-gate 16290Sstevel@tonic-gate switch (mo->mo_code) 16300Sstevel@tonic-gate { 16310Sstevel@tonic-gate case MO_LOGLEVEL: 16320Sstevel@tonic-gate MilterLogLevel = atoi(val); 16330Sstevel@tonic-gate break; 16340Sstevel@tonic-gate 16350Sstevel@tonic-gate #if _FFR_MAXDATASIZE 16360Sstevel@tonic-gate case MO_MAXDATASIZE: 16370Sstevel@tonic-gate MilterMaxDataSize = (size_t)atol(val); 16380Sstevel@tonic-gate break; 16390Sstevel@tonic-gate #endif /* _FFR_MAXDATASIZE */ 16400Sstevel@tonic-gate 16410Sstevel@tonic-gate case MO_MACROS_CONNECT: 16420Sstevel@tonic-gate if (macros == NULL) 16430Sstevel@tonic-gate macros = MilterConnectMacros; 16440Sstevel@tonic-gate /* FALLTHROUGH */ 16450Sstevel@tonic-gate 16460Sstevel@tonic-gate case MO_MACROS_HELO: 16470Sstevel@tonic-gate if (macros == NULL) 16480Sstevel@tonic-gate macros = MilterHeloMacros; 16490Sstevel@tonic-gate /* FALLTHROUGH */ 16500Sstevel@tonic-gate 16510Sstevel@tonic-gate case MO_MACROS_ENVFROM: 16520Sstevel@tonic-gate if (macros == NULL) 16530Sstevel@tonic-gate macros = MilterEnvFromMacros; 16540Sstevel@tonic-gate /* FALLTHROUGH */ 16550Sstevel@tonic-gate 16560Sstevel@tonic-gate case MO_MACROS_ENVRCPT: 16570Sstevel@tonic-gate if (macros == NULL) 16580Sstevel@tonic-gate macros = MilterEnvRcptMacros; 16590Sstevel@tonic-gate /* FALLTHROUGH */ 16600Sstevel@tonic-gate 1661*3544Sjbeck case MO_MACROS_EOH: 1662*3544Sjbeck if (macros == NULL) 1663*3544Sjbeck macros = MilterEOHMacros; 1664*3544Sjbeck /* FALLTHROUGH */ 1665*3544Sjbeck 16660Sstevel@tonic-gate case MO_MACROS_EOM: 16670Sstevel@tonic-gate if (macros == NULL) 16680Sstevel@tonic-gate macros = MilterEOMMacros; 16690Sstevel@tonic-gate /* FALLTHROUGH */ 16700Sstevel@tonic-gate 16710Sstevel@tonic-gate case MO_MACROS_DATA: 16720Sstevel@tonic-gate if (macros == NULL) 16730Sstevel@tonic-gate macros = MilterDataMacros; 16740Sstevel@tonic-gate 1675*3544Sjbeck r = milter_set_macros(name, macros, val, nummac); 1676*3544Sjbeck if (r >= 0) 1677*3544Sjbeck nummac = r; 16780Sstevel@tonic-gate break; 16790Sstevel@tonic-gate 16800Sstevel@tonic-gate default: 16810Sstevel@tonic-gate syserr("milter_set_option: invalid Milter option %s", name); 16820Sstevel@tonic-gate break; 16830Sstevel@tonic-gate } 16840Sstevel@tonic-gate if (sticky) 16850Sstevel@tonic-gate setbitn(mo->mo_code, StickyMilterOpt); 16860Sstevel@tonic-gate } 1687*3544Sjbeck 16880Sstevel@tonic-gate /* 16890Sstevel@tonic-gate ** MILTER_REOPEN_DF -- open & truncate the data file (for replbody) 16900Sstevel@tonic-gate ** 16910Sstevel@tonic-gate ** Parameters: 16920Sstevel@tonic-gate ** e -- current envelope. 16930Sstevel@tonic-gate ** 16940Sstevel@tonic-gate ** Returns: 16950Sstevel@tonic-gate ** 0 if succesful, -1 otherwise 16960Sstevel@tonic-gate */ 16970Sstevel@tonic-gate 16980Sstevel@tonic-gate static int 16990Sstevel@tonic-gate milter_reopen_df(e) 17000Sstevel@tonic-gate ENVELOPE *e; 17010Sstevel@tonic-gate { 17020Sstevel@tonic-gate char dfname[MAXPATHLEN]; 17030Sstevel@tonic-gate 1704*3544Sjbeck (void) sm_strlcpy(dfname, queuename(e, DATAFL_LETTER), sizeof(dfname)); 17050Sstevel@tonic-gate 17060Sstevel@tonic-gate /* 17070Sstevel@tonic-gate ** In SuperSafe == SAFE_REALLY mode, e->e_dfp is a read-only FP so 17080Sstevel@tonic-gate ** close and reopen writable (later close and reopen 17090Sstevel@tonic-gate ** read only again). 17100Sstevel@tonic-gate ** 17110Sstevel@tonic-gate ** In SuperSafe != SAFE_REALLY mode, e->e_dfp still points at the 17120Sstevel@tonic-gate ** buffered file I/O descriptor, still open for writing so there 17130Sstevel@tonic-gate ** isn't any work to do here (except checking for consistency). 17140Sstevel@tonic-gate */ 17150Sstevel@tonic-gate 17160Sstevel@tonic-gate if (SuperSafe == SAFE_REALLY) 17170Sstevel@tonic-gate { 17180Sstevel@tonic-gate /* close read-only data file */ 17190Sstevel@tonic-gate if (bitset(EF_HAS_DF, e->e_flags) && e->e_dfp != NULL) 17200Sstevel@tonic-gate { 17210Sstevel@tonic-gate (void) sm_io_close(e->e_dfp, SM_TIME_DEFAULT); 17220Sstevel@tonic-gate e->e_flags &= ~EF_HAS_DF; 17230Sstevel@tonic-gate } 17240Sstevel@tonic-gate 17250Sstevel@tonic-gate /* open writable */ 17260Sstevel@tonic-gate if ((e->e_dfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, dfname, 17270Sstevel@tonic-gate SM_IO_RDWR_B, NULL)) == NULL) 17280Sstevel@tonic-gate { 17290Sstevel@tonic-gate MILTER_DF_ERROR("milter_reopen_df: sm_io_open %s: %s"); 17300Sstevel@tonic-gate return -1; 17310Sstevel@tonic-gate } 17320Sstevel@tonic-gate } 17330Sstevel@tonic-gate else if (e->e_dfp == NULL) 17340Sstevel@tonic-gate { 17350Sstevel@tonic-gate /* shouldn't happen */ 17360Sstevel@tonic-gate errno = ENOENT; 17370Sstevel@tonic-gate MILTER_DF_ERROR("milter_reopen_df: NULL e_dfp (%s: %s)"); 17380Sstevel@tonic-gate return -1; 17390Sstevel@tonic-gate } 17400Sstevel@tonic-gate return 0; 17410Sstevel@tonic-gate } 1742*3544Sjbeck 17430Sstevel@tonic-gate /* 17440Sstevel@tonic-gate ** MILTER_RESET_DF -- re-open read-only the data file (for replbody) 17450Sstevel@tonic-gate ** 17460Sstevel@tonic-gate ** Parameters: 17470Sstevel@tonic-gate ** e -- current envelope. 17480Sstevel@tonic-gate ** 17490Sstevel@tonic-gate ** Returns: 17500Sstevel@tonic-gate ** 0 if succesful, -1 otherwise 17510Sstevel@tonic-gate */ 17520Sstevel@tonic-gate 17530Sstevel@tonic-gate static int 17540Sstevel@tonic-gate milter_reset_df(e) 17550Sstevel@tonic-gate ENVELOPE *e; 17560Sstevel@tonic-gate { 17570Sstevel@tonic-gate int afd; 17580Sstevel@tonic-gate char dfname[MAXPATHLEN]; 17590Sstevel@tonic-gate 1760*3544Sjbeck (void) sm_strlcpy(dfname, queuename(e, DATAFL_LETTER), sizeof(dfname)); 17610Sstevel@tonic-gate 17620Sstevel@tonic-gate if (sm_io_flush(e->e_dfp, SM_TIME_DEFAULT) != 0 || 17630Sstevel@tonic-gate sm_io_error(e->e_dfp)) 17640Sstevel@tonic-gate { 17650Sstevel@tonic-gate MILTER_DF_ERROR("milter_reset_df: error writing/flushing %s: %s"); 17660Sstevel@tonic-gate return -1; 17670Sstevel@tonic-gate } 17680Sstevel@tonic-gate else if (SuperSafe != SAFE_REALLY) 17690Sstevel@tonic-gate { 17700Sstevel@tonic-gate /* skip next few clauses */ 17710Sstevel@tonic-gate /* EMPTY */ 17720Sstevel@tonic-gate } 17730Sstevel@tonic-gate else if ((afd = sm_io_getinfo(e->e_dfp, SM_IO_WHAT_FD, NULL)) >= 0 17740Sstevel@tonic-gate && fsync(afd) < 0) 17750Sstevel@tonic-gate { 17760Sstevel@tonic-gate MILTER_DF_ERROR("milter_reset_df: error sync'ing %s: %s"); 17770Sstevel@tonic-gate return -1; 17780Sstevel@tonic-gate } 17790Sstevel@tonic-gate else if (sm_io_close(e->e_dfp, SM_TIME_DEFAULT) < 0) 17800Sstevel@tonic-gate { 17810Sstevel@tonic-gate MILTER_DF_ERROR("milter_reset_df: error closing %s: %s"); 17820Sstevel@tonic-gate return -1; 17830Sstevel@tonic-gate } 17840Sstevel@tonic-gate else if ((e->e_dfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, dfname, 17850Sstevel@tonic-gate SM_IO_RDONLY_B, NULL)) == NULL) 17860Sstevel@tonic-gate { 17870Sstevel@tonic-gate MILTER_DF_ERROR("milter_reset_df: error reopening %s: %s"); 17880Sstevel@tonic-gate return -1; 17890Sstevel@tonic-gate } 17900Sstevel@tonic-gate else 17910Sstevel@tonic-gate e->e_flags |= EF_HAS_DF; 17920Sstevel@tonic-gate return 0; 17930Sstevel@tonic-gate } 1794*3544Sjbeck 17950Sstevel@tonic-gate /* 17960Sstevel@tonic-gate ** MILTER_CAN_DELRCPTS -- can any milter filters delete recipients? 17970Sstevel@tonic-gate ** 17980Sstevel@tonic-gate ** Parameters: 17990Sstevel@tonic-gate ** none 18000Sstevel@tonic-gate ** 18010Sstevel@tonic-gate ** Returns: 18020Sstevel@tonic-gate ** true if any filter deletes recipients, false otherwise 18030Sstevel@tonic-gate */ 18040Sstevel@tonic-gate 18050Sstevel@tonic-gate bool 18060Sstevel@tonic-gate milter_can_delrcpts() 18070Sstevel@tonic-gate { 18080Sstevel@tonic-gate bool can = false; 18090Sstevel@tonic-gate int i; 18100Sstevel@tonic-gate 18110Sstevel@tonic-gate if (tTd(64, 10)) 18120Sstevel@tonic-gate sm_dprintf("milter_can_delrcpts:"); 18130Sstevel@tonic-gate 18140Sstevel@tonic-gate for (i = 0; InputFilters[i] != NULL; i++) 18150Sstevel@tonic-gate { 18160Sstevel@tonic-gate struct milter *m = InputFilters[i]; 18170Sstevel@tonic-gate 18180Sstevel@tonic-gate if (bitset(SMFIF_DELRCPT, m->mf_fflags)) 18190Sstevel@tonic-gate { 18200Sstevel@tonic-gate can = true; 18210Sstevel@tonic-gate break; 18220Sstevel@tonic-gate } 18230Sstevel@tonic-gate } 18240Sstevel@tonic-gate if (tTd(64, 10)) 18250Sstevel@tonic-gate sm_dprintf("%s\n", can ? "true" : "false"); 18260Sstevel@tonic-gate 18270Sstevel@tonic-gate return can; 18280Sstevel@tonic-gate } 1829*3544Sjbeck 18300Sstevel@tonic-gate /* 18310Sstevel@tonic-gate ** MILTER_QUIT_FILTER -- close down a single filter 18320Sstevel@tonic-gate ** 18330Sstevel@tonic-gate ** Parameters: 18340Sstevel@tonic-gate ** m -- milter structure of filter to close down. 18350Sstevel@tonic-gate ** e -- current envelope. 18360Sstevel@tonic-gate ** 18370Sstevel@tonic-gate ** Returns: 18380Sstevel@tonic-gate ** none 18390Sstevel@tonic-gate */ 18400Sstevel@tonic-gate 18410Sstevel@tonic-gate static void 18420Sstevel@tonic-gate milter_quit_filter(m, e) 18430Sstevel@tonic-gate struct milter *m; 18440Sstevel@tonic-gate ENVELOPE *e; 18450Sstevel@tonic-gate { 18460Sstevel@tonic-gate if (tTd(64, 10)) 18470Sstevel@tonic-gate sm_dprintf("milter_quit_filter(%s)\n", m->mf_name); 18480Sstevel@tonic-gate if (MilterLogLevel > 18) 18490Sstevel@tonic-gate sm_syslog(LOG_INFO, e->e_id, "Milter (%s): quit filter", 18500Sstevel@tonic-gate m->mf_name); 18510Sstevel@tonic-gate 18520Sstevel@tonic-gate /* Never replace error state */ 18530Sstevel@tonic-gate if (m->mf_state == SMFS_ERROR) 18540Sstevel@tonic-gate return; 18550Sstevel@tonic-gate 18560Sstevel@tonic-gate if (m->mf_sock < 0 || 18570Sstevel@tonic-gate m->mf_state == SMFS_CLOSED || 18580Sstevel@tonic-gate m->mf_state == SMFS_READY) 18590Sstevel@tonic-gate { 18600Sstevel@tonic-gate m->mf_sock = -1; 18610Sstevel@tonic-gate m->mf_state = SMFS_CLOSED; 18620Sstevel@tonic-gate return; 18630Sstevel@tonic-gate } 18640Sstevel@tonic-gate 18650Sstevel@tonic-gate (void) milter_write(m, SMFIC_QUIT, (char *) NULL, 0, 1866*3544Sjbeck m->mf_timeout[SMFTO_WRITE], e, "quit_filter"); 18670Sstevel@tonic-gate if (m->mf_sock >= 0) 18680Sstevel@tonic-gate { 18690Sstevel@tonic-gate (void) close(m->mf_sock); 18700Sstevel@tonic-gate m->mf_sock = -1; 18710Sstevel@tonic-gate } 18720Sstevel@tonic-gate if (m->mf_state != SMFS_ERROR) 18730Sstevel@tonic-gate m->mf_state = SMFS_CLOSED; 18740Sstevel@tonic-gate } 1875*3544Sjbeck 18760Sstevel@tonic-gate /* 18770Sstevel@tonic-gate ** MILTER_ABORT_FILTER -- tell filter to abort current message 18780Sstevel@tonic-gate ** 18790Sstevel@tonic-gate ** Parameters: 18800Sstevel@tonic-gate ** m -- milter structure of filter to abort. 18810Sstevel@tonic-gate ** e -- current envelope. 18820Sstevel@tonic-gate ** 18830Sstevel@tonic-gate ** Returns: 18840Sstevel@tonic-gate ** none 18850Sstevel@tonic-gate */ 18860Sstevel@tonic-gate 18870Sstevel@tonic-gate static void 18880Sstevel@tonic-gate milter_abort_filter(m, e) 18890Sstevel@tonic-gate struct milter *m; 18900Sstevel@tonic-gate ENVELOPE *e; 18910Sstevel@tonic-gate { 18920Sstevel@tonic-gate if (tTd(64, 10)) 18930Sstevel@tonic-gate sm_dprintf("milter_abort_filter(%s)\n", m->mf_name); 18940Sstevel@tonic-gate if (MilterLogLevel > 10) 18950Sstevel@tonic-gate sm_syslog(LOG_INFO, e->e_id, "Milter (%s): abort filter", 18960Sstevel@tonic-gate m->mf_name); 18970Sstevel@tonic-gate 18980Sstevel@tonic-gate if (m->mf_sock < 0 || 18990Sstevel@tonic-gate m->mf_state != SMFS_INMSG) 19000Sstevel@tonic-gate return; 19010Sstevel@tonic-gate 19020Sstevel@tonic-gate (void) milter_write(m, SMFIC_ABORT, (char *) NULL, 0, 1903*3544Sjbeck m->mf_timeout[SMFTO_WRITE], e, "abort_filter"); 19040Sstevel@tonic-gate if (m->mf_state != SMFS_ERROR) 19050Sstevel@tonic-gate m->mf_state = SMFS_DONE; 19060Sstevel@tonic-gate } 1907*3544Sjbeck 19080Sstevel@tonic-gate /* 19090Sstevel@tonic-gate ** MILTER_SEND_MACROS -- provide macros to the filters 19100Sstevel@tonic-gate ** 19110Sstevel@tonic-gate ** Parameters: 19120Sstevel@tonic-gate ** m -- milter to send macros to. 19130Sstevel@tonic-gate ** macros -- macros to send for filter smfi_getsymval(). 19140Sstevel@tonic-gate ** cmd -- which command the macros are associated with. 19150Sstevel@tonic-gate ** e -- current envelope (for macro access). 19160Sstevel@tonic-gate ** 19170Sstevel@tonic-gate ** Returns: 19180Sstevel@tonic-gate ** none 19190Sstevel@tonic-gate */ 19200Sstevel@tonic-gate 19210Sstevel@tonic-gate static void 19220Sstevel@tonic-gate milter_send_macros(m, macros, cmd, e) 19230Sstevel@tonic-gate struct milter *m; 19240Sstevel@tonic-gate char **macros; 1925*3544Sjbeck int cmd; 19260Sstevel@tonic-gate ENVELOPE *e; 19270Sstevel@tonic-gate { 19280Sstevel@tonic-gate int i; 19290Sstevel@tonic-gate int mid; 1930*3544Sjbeck char command = (char) cmd; 19310Sstevel@tonic-gate char *v; 19320Sstevel@tonic-gate char *buf, *bp; 19330Sstevel@tonic-gate char exp[MAXLINE]; 19340Sstevel@tonic-gate ssize_t s; 19350Sstevel@tonic-gate 19360Sstevel@tonic-gate /* sanity check */ 19370Sstevel@tonic-gate if (macros == NULL || macros[0] == NULL) 19380Sstevel@tonic-gate return; 19390Sstevel@tonic-gate 19400Sstevel@tonic-gate /* put together data */ 19410Sstevel@tonic-gate s = 1; /* for the command character */ 19420Sstevel@tonic-gate for (i = 0; macros[i] != NULL; i++) 19430Sstevel@tonic-gate { 19440Sstevel@tonic-gate mid = macid(macros[i]); 19450Sstevel@tonic-gate if (mid == 0) 19460Sstevel@tonic-gate continue; 19470Sstevel@tonic-gate v = macvalue(mid, e); 19480Sstevel@tonic-gate if (v == NULL) 19490Sstevel@tonic-gate continue; 19500Sstevel@tonic-gate expand(v, exp, sizeof(exp), e); 19510Sstevel@tonic-gate s += strlen(macros[i]) + 1 + strlen(exp) + 1; 19520Sstevel@tonic-gate } 19530Sstevel@tonic-gate 19540Sstevel@tonic-gate if (s < 0) 19550Sstevel@tonic-gate return; 19560Sstevel@tonic-gate 19570Sstevel@tonic-gate buf = (char *) xalloc(s); 19580Sstevel@tonic-gate bp = buf; 1959*3544Sjbeck *bp++ = command; 19600Sstevel@tonic-gate for (i = 0; macros[i] != NULL; i++) 19610Sstevel@tonic-gate { 19620Sstevel@tonic-gate mid = macid(macros[i]); 19630Sstevel@tonic-gate if (mid == 0) 19640Sstevel@tonic-gate continue; 19650Sstevel@tonic-gate v = macvalue(mid, e); 19660Sstevel@tonic-gate if (v == NULL) 19670Sstevel@tonic-gate continue; 19680Sstevel@tonic-gate expand(v, exp, sizeof(exp), e); 19690Sstevel@tonic-gate 19700Sstevel@tonic-gate if (tTd(64, 10)) 19710Sstevel@tonic-gate sm_dprintf("milter_send_macros(%s, %c): %s=%s\n", 1972*3544Sjbeck m->mf_name, command, macros[i], exp); 19730Sstevel@tonic-gate 19740Sstevel@tonic-gate (void) sm_strlcpy(bp, macros[i], s - (bp - buf)); 19750Sstevel@tonic-gate bp += strlen(bp) + 1; 19760Sstevel@tonic-gate (void) sm_strlcpy(bp, exp, s - (bp - buf)); 19770Sstevel@tonic-gate bp += strlen(bp) + 1; 19780Sstevel@tonic-gate } 19790Sstevel@tonic-gate (void) milter_write(m, SMFIC_MACRO, buf, s, 1980*3544Sjbeck m->mf_timeout[SMFTO_WRITE], e, "send_macros"); 19810Sstevel@tonic-gate sm_free(buf); 19820Sstevel@tonic-gate } 19830Sstevel@tonic-gate 19840Sstevel@tonic-gate /* 19850Sstevel@tonic-gate ** MILTER_SEND_COMMAND -- send a command and return the response for a filter 19860Sstevel@tonic-gate ** 19870Sstevel@tonic-gate ** Parameters: 19880Sstevel@tonic-gate ** m -- current milter filter 1989*3544Sjbeck ** cmd -- command to send. 19900Sstevel@tonic-gate ** data -- optional command data. 19910Sstevel@tonic-gate ** sz -- length of buf. 19920Sstevel@tonic-gate ** e -- current envelope (for e->e_id). 19930Sstevel@tonic-gate ** state -- return state word. 19940Sstevel@tonic-gate ** 19950Sstevel@tonic-gate ** Returns: 19960Sstevel@tonic-gate ** response string (may be NULL) 19970Sstevel@tonic-gate */ 19980Sstevel@tonic-gate 19990Sstevel@tonic-gate static char * 2000*3544Sjbeck milter_send_command(m, cmd, data, sz, e, state, where) 20010Sstevel@tonic-gate struct milter *m; 2002*3544Sjbeck int cmd; 20030Sstevel@tonic-gate void *data; 20040Sstevel@tonic-gate ssize_t sz; 20050Sstevel@tonic-gate ENVELOPE *e; 20060Sstevel@tonic-gate char *state; 2007*3544Sjbeck const char *where; 20080Sstevel@tonic-gate { 20090Sstevel@tonic-gate char rcmd; 20100Sstevel@tonic-gate ssize_t rlen; 20110Sstevel@tonic-gate unsigned long skipflag; 20120Sstevel@tonic-gate unsigned long norespflag = 0; 2013*3544Sjbeck char command = (char) cmd; 20140Sstevel@tonic-gate char *action; 20150Sstevel@tonic-gate char *defresponse; 20160Sstevel@tonic-gate char *response; 20170Sstevel@tonic-gate 20180Sstevel@tonic-gate if (tTd(64, 10)) 20190Sstevel@tonic-gate sm_dprintf("milter_send_command(%s): cmd %c len %ld\n", 20200Sstevel@tonic-gate m->mf_name, (char) command, (long) sz); 20210Sstevel@tonic-gate 20220Sstevel@tonic-gate /* find skip flag and default failure */ 20230Sstevel@tonic-gate switch (command) 20240Sstevel@tonic-gate { 20250Sstevel@tonic-gate case SMFIC_CONNECT: 20260Sstevel@tonic-gate skipflag = SMFIP_NOCONNECT; 2027*3544Sjbeck norespflag = SMFIP_NR_CONN; 20280Sstevel@tonic-gate action = "connect"; 20290Sstevel@tonic-gate defresponse = "554 Command rejected"; 20300Sstevel@tonic-gate break; 20310Sstevel@tonic-gate 20320Sstevel@tonic-gate case SMFIC_HELO: 20330Sstevel@tonic-gate skipflag = SMFIP_NOHELO; 2034*3544Sjbeck norespflag = SMFIP_NR_HELO; 20350Sstevel@tonic-gate action = "helo"; 20360Sstevel@tonic-gate defresponse = "550 Command rejected"; 20370Sstevel@tonic-gate break; 20380Sstevel@tonic-gate 20390Sstevel@tonic-gate case SMFIC_MAIL: 20400Sstevel@tonic-gate skipflag = SMFIP_NOMAIL; 2041*3544Sjbeck norespflag = SMFIP_NR_MAIL; 20420Sstevel@tonic-gate action = "mail"; 20430Sstevel@tonic-gate defresponse = "550 5.7.1 Command rejected"; 20440Sstevel@tonic-gate break; 20450Sstevel@tonic-gate 20460Sstevel@tonic-gate case SMFIC_RCPT: 20470Sstevel@tonic-gate skipflag = SMFIP_NORCPT; 2048*3544Sjbeck norespflag = SMFIP_NR_RCPT; 20490Sstevel@tonic-gate action = "rcpt"; 20500Sstevel@tonic-gate defresponse = "550 5.7.1 Command rejected"; 20510Sstevel@tonic-gate break; 20520Sstevel@tonic-gate 20530Sstevel@tonic-gate case SMFIC_HEADER: 20540Sstevel@tonic-gate skipflag = SMFIP_NOHDRS; 2055*3544Sjbeck norespflag = SMFIP_NR_HDR; 20560Sstevel@tonic-gate action = "header"; 20570Sstevel@tonic-gate defresponse = "550 5.7.1 Command rejected"; 20580Sstevel@tonic-gate break; 20590Sstevel@tonic-gate 20600Sstevel@tonic-gate case SMFIC_BODY: 20610Sstevel@tonic-gate skipflag = SMFIP_NOBODY; 2062*3544Sjbeck norespflag = SMFIP_NR_BODY; 20630Sstevel@tonic-gate action = "body"; 20640Sstevel@tonic-gate defresponse = "554 5.7.1 Command rejected"; 20650Sstevel@tonic-gate break; 20660Sstevel@tonic-gate 20670Sstevel@tonic-gate case SMFIC_EOH: 20680Sstevel@tonic-gate skipflag = SMFIP_NOEOH; 2069*3544Sjbeck norespflag = SMFIP_NR_EOH; 20700Sstevel@tonic-gate action = "eoh"; 20710Sstevel@tonic-gate defresponse = "550 5.7.1 Command rejected"; 20720Sstevel@tonic-gate break; 20730Sstevel@tonic-gate 20740Sstevel@tonic-gate case SMFIC_UNKNOWN: 2075616Sjbeck skipflag = SMFIP_NOUNKNOWN; 2076*3544Sjbeck norespflag = SMFIP_NR_UNKN; 20770Sstevel@tonic-gate action = "unknown"; 20780Sstevel@tonic-gate defresponse = "550 5.7.1 Command rejected"; 20790Sstevel@tonic-gate break; 2080*3544Sjbeck 2081616Sjbeck case SMFIC_DATA: 2082616Sjbeck skipflag = SMFIP_NODATA; 2083*3544Sjbeck norespflag = SMFIP_NR_DATA; 2084616Sjbeck action = "data"; 2085616Sjbeck defresponse = "550 5.7.1 Command rejected"; 2086616Sjbeck break; 2087616Sjbeck 20880Sstevel@tonic-gate case SMFIC_BODYEOB: 20890Sstevel@tonic-gate case SMFIC_OPTNEG: 20900Sstevel@tonic-gate case SMFIC_MACRO: 20910Sstevel@tonic-gate case SMFIC_ABORT: 20920Sstevel@tonic-gate case SMFIC_QUIT: 20930Sstevel@tonic-gate /* NOTE: not handled by milter_send_command() */ 20940Sstevel@tonic-gate /* FALLTHROUGH */ 20950Sstevel@tonic-gate 20960Sstevel@tonic-gate default: 20970Sstevel@tonic-gate skipflag = 0; 20980Sstevel@tonic-gate action = "default"; 20990Sstevel@tonic-gate defresponse = "550 5.7.1 Command rejected"; 21000Sstevel@tonic-gate break; 21010Sstevel@tonic-gate } 21020Sstevel@tonic-gate 2103*3544Sjbeck if (tTd(64, 10)) 2104*3544Sjbeck sm_dprintf("milter_send_command(%s): skip=%lx, pflags=%x\n", 2105*3544Sjbeck m->mf_name, skipflag, m->mf_pflags); 2106*3544Sjbeck 21070Sstevel@tonic-gate /* check if filter wants this command */ 2108*3544Sjbeck if (skipflag != 0 && bitset(skipflag, m->mf_pflags)) 21090Sstevel@tonic-gate return NULL; 21100Sstevel@tonic-gate 21110Sstevel@tonic-gate /* send the command to the filter */ 21120Sstevel@tonic-gate (void) milter_write(m, command, data, sz, 2113*3544Sjbeck m->mf_timeout[SMFTO_WRITE], e, where); 21140Sstevel@tonic-gate if (m->mf_state == SMFS_ERROR) 21150Sstevel@tonic-gate { 21160Sstevel@tonic-gate MILTER_CHECK_ERROR(false, return NULL); 21170Sstevel@tonic-gate return NULL; 21180Sstevel@tonic-gate } 21190Sstevel@tonic-gate 21200Sstevel@tonic-gate /* check if filter sends response to this command */ 21210Sstevel@tonic-gate if (norespflag != 0 && bitset(norespflag, m->mf_pflags)) 21220Sstevel@tonic-gate return NULL; 21230Sstevel@tonic-gate 21240Sstevel@tonic-gate /* get the response from the filter */ 21250Sstevel@tonic-gate response = milter_read(m, &rcmd, &rlen, 2126*3544Sjbeck m->mf_timeout[SMFTO_READ], e, where); 21270Sstevel@tonic-gate if (m->mf_state == SMFS_ERROR) 21280Sstevel@tonic-gate { 21290Sstevel@tonic-gate MILTER_CHECK_ERROR(false, return NULL); 21300Sstevel@tonic-gate return NULL; 21310Sstevel@tonic-gate } 21320Sstevel@tonic-gate 21330Sstevel@tonic-gate if (tTd(64, 10)) 21340Sstevel@tonic-gate sm_dprintf("milter_send_command(%s): returned %c\n", 21350Sstevel@tonic-gate m->mf_name, (char) rcmd); 21360Sstevel@tonic-gate 21370Sstevel@tonic-gate switch (rcmd) 21380Sstevel@tonic-gate { 21390Sstevel@tonic-gate case SMFIR_REPLYCODE: 21400Sstevel@tonic-gate MILTER_CHECK_REPLYCODE(defresponse); 21410Sstevel@tonic-gate if (MilterLogLevel > 10) 2142*3544Sjbeck sm_syslog(LOG_INFO, e->e_id, 2143*3544Sjbeck "milter=%s, action=%s, reject=%s", 21440Sstevel@tonic-gate m->mf_name, action, response); 21450Sstevel@tonic-gate *state = rcmd; 21460Sstevel@tonic-gate break; 21470Sstevel@tonic-gate 21480Sstevel@tonic-gate case SMFIR_REJECT: 21490Sstevel@tonic-gate if (MilterLogLevel > 10) 2150*3544Sjbeck sm_syslog(LOG_INFO, e->e_id, 2151*3544Sjbeck "milter=%s, action=%s, reject", 21520Sstevel@tonic-gate m->mf_name, action); 21530Sstevel@tonic-gate *state = rcmd; 21540Sstevel@tonic-gate break; 21550Sstevel@tonic-gate 21560Sstevel@tonic-gate case SMFIR_DISCARD: 21570Sstevel@tonic-gate if (MilterLogLevel > 10) 2158*3544Sjbeck sm_syslog(LOG_INFO, e->e_id, 2159*3544Sjbeck "milter=%s, action=%s, discard", 21600Sstevel@tonic-gate m->mf_name, action); 21610Sstevel@tonic-gate *state = rcmd; 21620Sstevel@tonic-gate break; 21630Sstevel@tonic-gate 21640Sstevel@tonic-gate case SMFIR_TEMPFAIL: 21650Sstevel@tonic-gate if (MilterLogLevel > 10) 2166*3544Sjbeck sm_syslog(LOG_INFO, e->e_id, 2167*3544Sjbeck "milter=%s, action=%s, tempfail", 21680Sstevel@tonic-gate m->mf_name, action); 21690Sstevel@tonic-gate *state = rcmd; 21700Sstevel@tonic-gate break; 21710Sstevel@tonic-gate 21720Sstevel@tonic-gate case SMFIR_ACCEPT: 21730Sstevel@tonic-gate /* this filter is done with message/connection */ 21740Sstevel@tonic-gate if (command == SMFIC_HELO || 21750Sstevel@tonic-gate command == SMFIC_CONNECT) 21760Sstevel@tonic-gate m->mf_state = SMFS_CLOSABLE; 21770Sstevel@tonic-gate else 21780Sstevel@tonic-gate m->mf_state = SMFS_DONE; 21790Sstevel@tonic-gate if (MilterLogLevel > 10) 2180*3544Sjbeck sm_syslog(LOG_INFO, e->e_id, 2181*3544Sjbeck "milter=%s, action=%s, accepted", 21820Sstevel@tonic-gate m->mf_name, action); 21830Sstevel@tonic-gate break; 21840Sstevel@tonic-gate 21850Sstevel@tonic-gate case SMFIR_CONTINUE: 21860Sstevel@tonic-gate /* if MAIL command is ok, filter is in message state */ 21870Sstevel@tonic-gate if (command == SMFIC_MAIL) 21880Sstevel@tonic-gate m->mf_state = SMFS_INMSG; 21890Sstevel@tonic-gate if (MilterLogLevel > 12) 2190*3544Sjbeck sm_syslog(LOG_INFO, e->e_id, 2191*3544Sjbeck "milter=%s, action=%s, continue", 21920Sstevel@tonic-gate m->mf_name, action); 21930Sstevel@tonic-gate break; 21940Sstevel@tonic-gate 2195*3544Sjbeck case SMFIR_SKIP: 2196*3544Sjbeck if (MilterLogLevel > 12) 2197*3544Sjbeck sm_syslog(LOG_INFO, e->e_id, 2198*3544Sjbeck "milter=%s, action=%s, skip", 2199*3544Sjbeck m->mf_name, action); 2200*3544Sjbeck m->mf_state = SMFS_SKIP; 2201*3544Sjbeck break; 2202*3544Sjbeck 22030Sstevel@tonic-gate default: 22040Sstevel@tonic-gate /* Invalid response to command */ 22050Sstevel@tonic-gate if (MilterLogLevel > 0) 22060Sstevel@tonic-gate sm_syslog(LOG_ERR, e->e_id, 22070Sstevel@tonic-gate "milter_send_command(%s): action=%s returned bogus response %c", 22080Sstevel@tonic-gate m->mf_name, action, rcmd); 22090Sstevel@tonic-gate milter_error(m, e); 22100Sstevel@tonic-gate break; 22110Sstevel@tonic-gate } 22120Sstevel@tonic-gate 2213*3544Sjbeck if (*state != SMFIR_REPLYCODE && response != NULL) 22140Sstevel@tonic-gate { 22150Sstevel@tonic-gate sm_free(response); /* XXX */ 22160Sstevel@tonic-gate response = NULL; 22170Sstevel@tonic-gate } 22180Sstevel@tonic-gate return response; 22190Sstevel@tonic-gate } 22200Sstevel@tonic-gate 22210Sstevel@tonic-gate /* 22220Sstevel@tonic-gate ** MILTER_COMMAND -- send a command and return the response for each filter 22230Sstevel@tonic-gate ** 22240Sstevel@tonic-gate ** Parameters: 2225*3544Sjbeck ** cmd -- command to send. 22260Sstevel@tonic-gate ** data -- optional command data. 22270Sstevel@tonic-gate ** sz -- length of buf. 22280Sstevel@tonic-gate ** macros -- macros to send for filter smfi_getsymval(). 22290Sstevel@tonic-gate ** e -- current envelope (for macro access). 22300Sstevel@tonic-gate ** state -- return state word. 2231*3544Sjbeck ** where -- description of calling function (logging). 2232*3544Sjbeck ** cmd_error -- did the SMTP command cause an error? 22330Sstevel@tonic-gate ** 22340Sstevel@tonic-gate ** Returns: 22350Sstevel@tonic-gate ** response string (may be NULL) 22360Sstevel@tonic-gate */ 22370Sstevel@tonic-gate 22380Sstevel@tonic-gate static char * 2239*3544Sjbeck milter_command(cmd, data, sz, macros, e, state, where, cmd_error) 2240*3544Sjbeck int cmd; 22410Sstevel@tonic-gate void *data; 22420Sstevel@tonic-gate ssize_t sz; 22430Sstevel@tonic-gate char **macros; 22440Sstevel@tonic-gate ENVELOPE *e; 22450Sstevel@tonic-gate char *state; 2246*3544Sjbeck const char *where; 2247*3544Sjbeck bool cmd_error; 22480Sstevel@tonic-gate { 22490Sstevel@tonic-gate int i; 2250*3544Sjbeck char command = (char) cmd; 22510Sstevel@tonic-gate char *response = NULL; 22520Sstevel@tonic-gate time_t tn = 0; 22530Sstevel@tonic-gate 22540Sstevel@tonic-gate if (tTd(64, 10)) 22550Sstevel@tonic-gate sm_dprintf("milter_command: cmd %c len %ld\n", 2256*3544Sjbeck command, (long) sz); 22570Sstevel@tonic-gate 22580Sstevel@tonic-gate *state = SMFIR_CONTINUE; 22590Sstevel@tonic-gate for (i = 0; InputFilters[i] != NULL; i++) 22600Sstevel@tonic-gate { 22610Sstevel@tonic-gate struct milter *m = InputFilters[i]; 22620Sstevel@tonic-gate 22630Sstevel@tonic-gate /* previous problem? */ 22640Sstevel@tonic-gate if (m->mf_state == SMFS_ERROR) 22650Sstevel@tonic-gate { 22660Sstevel@tonic-gate MILTER_CHECK_ERROR(false, continue); 22670Sstevel@tonic-gate break; 22680Sstevel@tonic-gate } 22690Sstevel@tonic-gate 22700Sstevel@tonic-gate /* sanity check */ 22710Sstevel@tonic-gate if (m->mf_sock < 0 || 22720Sstevel@tonic-gate (m->mf_state != SMFS_OPEN && m->mf_state != SMFS_INMSG)) 22730Sstevel@tonic-gate continue; 22740Sstevel@tonic-gate 22750Sstevel@tonic-gate /* send macros (regardless of whether we send command) */ 22760Sstevel@tonic-gate if (macros != NULL && macros[0] != NULL) 22770Sstevel@tonic-gate { 22780Sstevel@tonic-gate milter_send_macros(m, macros, command, e); 22790Sstevel@tonic-gate if (m->mf_state == SMFS_ERROR) 22800Sstevel@tonic-gate { 22810Sstevel@tonic-gate MILTER_CHECK_ERROR(false, continue); 22820Sstevel@tonic-gate break; 22830Sstevel@tonic-gate } 22840Sstevel@tonic-gate } 22850Sstevel@tonic-gate 22860Sstevel@tonic-gate if (MilterLogLevel > 21) 22870Sstevel@tonic-gate tn = curtime(); 22880Sstevel@tonic-gate 2289*3544Sjbeck /* 2290*3544Sjbeck ** send the command if 2291*3544Sjbeck ** there is no error 2292*3544Sjbeck ** or it's RCPT and the client asked for it: 2293*3544Sjbeck ** !cmd_error || 2294*3544Sjbeck ** where == "rcpt" && m->mf_pflags & SMFIP_RCPT_REJ != 0 2295*3544Sjbeck ** negate that condition and use continue 2296*3544Sjbeck */ 2297*3544Sjbeck 2298*3544Sjbeck if (cmd_error && 2299*3544Sjbeck (strcmp(where, "rcpt") != 0 || 2300*3544Sjbeck (m->mf_pflags & SMFIP_RCPT_REJ) == 0)) 2301*3544Sjbeck continue; 2302*3544Sjbeck 2303*3544Sjbeck response = milter_send_command(m, command, data, sz, e, state, 2304*3544Sjbeck where); 23050Sstevel@tonic-gate 23060Sstevel@tonic-gate if (MilterLogLevel > 21) 23070Sstevel@tonic-gate { 23080Sstevel@tonic-gate /* log the time it took for the command per filter */ 23090Sstevel@tonic-gate sm_syslog(LOG_INFO, e->e_id, 23100Sstevel@tonic-gate "Milter (%s): time command (%c), %d", 23110Sstevel@tonic-gate m->mf_name, command, (int) (tn - curtime())); 23120Sstevel@tonic-gate } 23130Sstevel@tonic-gate 23140Sstevel@tonic-gate if (*state != SMFIR_CONTINUE) 23150Sstevel@tonic-gate break; 23160Sstevel@tonic-gate } 23170Sstevel@tonic-gate return response; 23180Sstevel@tonic-gate } 2319*3544Sjbeck 2320*3544Sjbeck static int milter_getsymlist __P((struct milter *, char *, int, int)); 2321*3544Sjbeck 2322*3544Sjbeck static int 2323*3544Sjbeck milter_getsymlist(m, buf, rlen, offset) 2324*3544Sjbeck struct milter *m; 2325*3544Sjbeck char *buf; 2326*3544Sjbeck int rlen; 2327*3544Sjbeck int offset; 2328*3544Sjbeck { 2329*3544Sjbeck int i, r, nummac; 2330*3544Sjbeck mi_int32 v; 2331*3544Sjbeck 2332*3544Sjbeck SM_ASSERT(m != NULL); 2333*3544Sjbeck SM_ASSERT(buf != NULL); 2334*3544Sjbeck 2335*3544Sjbeck while (offset + MILTER_LEN_BYTES < rlen) 2336*3544Sjbeck { 2337*3544Sjbeck size_t len; 2338*3544Sjbeck char **macros; 2339*3544Sjbeck 2340*3544Sjbeck nummac = 0; 2341*3544Sjbeck (void) memcpy((char *) &v, buf + offset, MILTER_LEN_BYTES); 2342*3544Sjbeck i = ntohl(v); 2343*3544Sjbeck if (i < SMFIM_FIRST || i > SMFIM_LAST) 2344*3544Sjbeck return -1; 2345*3544Sjbeck offset += MILTER_LEN_BYTES; 2346*3544Sjbeck macros = NULL; 2347*3544Sjbeck 2348*3544Sjbeck switch (i) 2349*3544Sjbeck { 2350*3544Sjbeck case MO_MACROS_CONNECT: 2351*3544Sjbeck if (macros == NULL) 2352*3544Sjbeck macros = MilterConnectMacros; 2353*3544Sjbeck /* FALLTHROUGH */ 2354*3544Sjbeck 2355*3544Sjbeck case MO_MACROS_HELO: 2356*3544Sjbeck if (macros == NULL) 2357*3544Sjbeck macros = MilterHeloMacros; 2358*3544Sjbeck /* FALLTHROUGH */ 2359*3544Sjbeck 2360*3544Sjbeck case MO_MACROS_ENVFROM: 2361*3544Sjbeck if (macros == NULL) 2362*3544Sjbeck macros = MilterEnvFromMacros; 2363*3544Sjbeck /* FALLTHROUGH */ 2364*3544Sjbeck 2365*3544Sjbeck case MO_MACROS_ENVRCPT: 2366*3544Sjbeck if (macros == NULL) 2367*3544Sjbeck macros = MilterEnvRcptMacros; 2368*3544Sjbeck /* FALLTHROUGH */ 2369*3544Sjbeck 2370*3544Sjbeck case MO_MACROS_EOM: 2371*3544Sjbeck if (macros == NULL) 2372*3544Sjbeck macros = MilterEOMMacros; 2373*3544Sjbeck /* FALLTHROUGH */ 2374*3544Sjbeck 2375*3544Sjbeck case MO_MACROS_EOH: 2376*3544Sjbeck if (macros == NULL) 2377*3544Sjbeck macros = MilterEOHMacros; 2378*3544Sjbeck /* FALLTHROUGH */ 2379*3544Sjbeck 2380*3544Sjbeck case MO_MACROS_DATA: 2381*3544Sjbeck if (macros == NULL) 2382*3544Sjbeck macros = MilterDataMacros; 2383*3544Sjbeck 2384*3544Sjbeck len = strlen(buf + offset); 2385*3544Sjbeck if (len > 0) 2386*3544Sjbeck { 2387*3544Sjbeck r = milter_set_macros(m->mf_name, macros, 2388*3544Sjbeck buf + offset, nummac); 2389*3544Sjbeck if (r >= 0) 2390*3544Sjbeck nummac = r; 2391*3544Sjbeck } 2392*3544Sjbeck break; 2393*3544Sjbeck 2394*3544Sjbeck default: 2395*3544Sjbeck return -1; 2396*3544Sjbeck } 2397*3544Sjbeck if (len == 0) 2398*3544Sjbeck return -1; 2399*3544Sjbeck offset += len + 1; 2400*3544Sjbeck } 2401*3544Sjbeck 2402*3544Sjbeck return 0; 2403*3544Sjbeck } 2404*3544Sjbeck 24050Sstevel@tonic-gate /* 24060Sstevel@tonic-gate ** MILTER_NEGOTIATE -- get version and flags from filter 24070Sstevel@tonic-gate ** 24080Sstevel@tonic-gate ** Parameters: 24090Sstevel@tonic-gate ** m -- milter filter structure. 24100Sstevel@tonic-gate ** e -- current envelope. 24110Sstevel@tonic-gate ** 24120Sstevel@tonic-gate ** Returns: 24130Sstevel@tonic-gate ** 0 on success, -1 otherwise 24140Sstevel@tonic-gate */ 24150Sstevel@tonic-gate 24160Sstevel@tonic-gate static int 24170Sstevel@tonic-gate milter_negotiate(m, e) 24180Sstevel@tonic-gate struct milter *m; 24190Sstevel@tonic-gate ENVELOPE *e; 24200Sstevel@tonic-gate { 24210Sstevel@tonic-gate char rcmd; 2422*3544Sjbeck mi_int32 fvers, fflags, pflags; 2423*3544Sjbeck mi_int32 mta_prot_vers, mta_prot_flags, mta_actions; 2424616Sjbeck ssize_t rlen; 24250Sstevel@tonic-gate char *response; 24260Sstevel@tonic-gate char data[MILTER_OPTLEN]; 24270Sstevel@tonic-gate 24280Sstevel@tonic-gate /* sanity check */ 24290Sstevel@tonic-gate if (m->mf_sock < 0 || m->mf_state != SMFS_OPEN) 24300Sstevel@tonic-gate { 24310Sstevel@tonic-gate if (MilterLogLevel > 0) 24320Sstevel@tonic-gate sm_syslog(LOG_ERR, e->e_id, 24330Sstevel@tonic-gate "Milter (%s): negotiate, impossible state", 24340Sstevel@tonic-gate m->mf_name); 24350Sstevel@tonic-gate milter_error(m, e); 24360Sstevel@tonic-gate return -1; 24370Sstevel@tonic-gate } 24380Sstevel@tonic-gate 2439*3544Sjbeck #if MILTER_CHECK 2440*3544Sjbeck mta_prot_vers = m->mf_mta_prot_version; 2441*3544Sjbeck mta_prot_flags = m->mf_mta_prot_flags; 2442*3544Sjbeck mta_actions = m->mf_mta_actions; 2443*3544Sjbeck #else /* MILTER_CHECK */ 2444*3544Sjbeck mta_prot_vers = SMFI_PROT_VERSION; 2445*3544Sjbeck mta_prot_flags = SMFI_CURR_PROT; 2446*3544Sjbeck mta_actions = SMFI_CURR_ACTS; 2447*3544Sjbeck #endif /* MILTER_CHECK */ 2448*3544Sjbeck 2449*3544Sjbeck fvers = htonl(mta_prot_vers); 2450*3544Sjbeck pflags = htonl(mta_prot_flags); 2451*3544Sjbeck fflags = htonl(mta_actions); 24520Sstevel@tonic-gate (void) memcpy(data, (char *) &fvers, MILTER_LEN_BYTES); 24530Sstevel@tonic-gate (void) memcpy(data + MILTER_LEN_BYTES, 24540Sstevel@tonic-gate (char *) &fflags, MILTER_LEN_BYTES); 24550Sstevel@tonic-gate (void) memcpy(data + (MILTER_LEN_BYTES * 2), 24560Sstevel@tonic-gate (char *) &pflags, MILTER_LEN_BYTES); 2457*3544Sjbeck (void) milter_write(m, SMFIC_OPTNEG, data, sizeof(data), 2458*3544Sjbeck m->mf_timeout[SMFTO_WRITE], e, "negotiate"); 24590Sstevel@tonic-gate 24600Sstevel@tonic-gate if (m->mf_state == SMFS_ERROR) 24610Sstevel@tonic-gate return -1; 24620Sstevel@tonic-gate 2463*3544Sjbeck if (tTd(64, 5)) 2464*3544Sjbeck sm_dprintf("milter_negotiate(%s): send: version %lu, fflags 0x%lx, pflags 0x%lx\n", 2465*3544Sjbeck m->mf_name, ntohl(fvers), ntohl(fflags), ntohl(pflags)); 2466*3544Sjbeck 2467*3544Sjbeck response = milter_read(m, &rcmd, &rlen, m->mf_timeout[SMFTO_READ], e, 2468*3544Sjbeck "negotiate"); 24690Sstevel@tonic-gate if (m->mf_state == SMFS_ERROR) 24700Sstevel@tonic-gate return -1; 24710Sstevel@tonic-gate 24720Sstevel@tonic-gate if (rcmd != SMFIC_OPTNEG) 24730Sstevel@tonic-gate { 24740Sstevel@tonic-gate if (tTd(64, 5)) 24750Sstevel@tonic-gate sm_dprintf("milter_negotiate(%s): returned %c instead of %c\n", 24760Sstevel@tonic-gate m->mf_name, rcmd, SMFIC_OPTNEG); 24770Sstevel@tonic-gate if (MilterLogLevel > 0) 24780Sstevel@tonic-gate sm_syslog(LOG_ERR, e->e_id, 24790Sstevel@tonic-gate "Milter (%s): negotiate: returned %c instead of %c", 24800Sstevel@tonic-gate m->mf_name, rcmd, SMFIC_OPTNEG); 24810Sstevel@tonic-gate if (response != NULL) 24820Sstevel@tonic-gate sm_free(response); /* XXX */ 24830Sstevel@tonic-gate milter_error(m, e); 24840Sstevel@tonic-gate return -1; 24850Sstevel@tonic-gate } 24860Sstevel@tonic-gate 24870Sstevel@tonic-gate /* Make sure we have enough bytes for the version */ 24880Sstevel@tonic-gate if (response == NULL || rlen < MILTER_LEN_BYTES) 24890Sstevel@tonic-gate { 24900Sstevel@tonic-gate if (tTd(64, 5)) 24910Sstevel@tonic-gate sm_dprintf("milter_negotiate(%s): did not return valid info\n", 24920Sstevel@tonic-gate m->mf_name); 24930Sstevel@tonic-gate if (MilterLogLevel > 0) 24940Sstevel@tonic-gate sm_syslog(LOG_ERR, e->e_id, 24950Sstevel@tonic-gate "Milter (%s): negotiate: did not return valid info", 24960Sstevel@tonic-gate m->mf_name); 24970Sstevel@tonic-gate if (response != NULL) 24980Sstevel@tonic-gate sm_free(response); /* XXX */ 24990Sstevel@tonic-gate milter_error(m, e); 25000Sstevel@tonic-gate return -1; 25010Sstevel@tonic-gate } 25020Sstevel@tonic-gate 25030Sstevel@tonic-gate /* extract information */ 25040Sstevel@tonic-gate (void) memcpy((char *) &fvers, response, MILTER_LEN_BYTES); 25050Sstevel@tonic-gate 25060Sstevel@tonic-gate /* Now make sure we have enough for the feature bitmap */ 2507*3544Sjbeck if (rlen < MILTER_OPTLEN) 25080Sstevel@tonic-gate { 25090Sstevel@tonic-gate if (tTd(64, 5)) 25100Sstevel@tonic-gate sm_dprintf("milter_negotiate(%s): did not return enough info\n", 25110Sstevel@tonic-gate m->mf_name); 25120Sstevel@tonic-gate if (MilterLogLevel > 0) 25130Sstevel@tonic-gate sm_syslog(LOG_ERR, e->e_id, 25140Sstevel@tonic-gate "Milter (%s): negotiate: did not return enough info", 25150Sstevel@tonic-gate m->mf_name); 25160Sstevel@tonic-gate if (response != NULL) 25170Sstevel@tonic-gate sm_free(response); /* XXX */ 25180Sstevel@tonic-gate milter_error(m, e); 25190Sstevel@tonic-gate return -1; 25200Sstevel@tonic-gate } 25210Sstevel@tonic-gate 25220Sstevel@tonic-gate (void) memcpy((char *) &fflags, response + MILTER_LEN_BYTES, 25230Sstevel@tonic-gate MILTER_LEN_BYTES); 25240Sstevel@tonic-gate (void) memcpy((char *) &pflags, response + (MILTER_LEN_BYTES * 2), 25250Sstevel@tonic-gate MILTER_LEN_BYTES); 25260Sstevel@tonic-gate 25270Sstevel@tonic-gate m->mf_fvers = ntohl(fvers); 25280Sstevel@tonic-gate m->mf_fflags = ntohl(fflags); 25290Sstevel@tonic-gate m->mf_pflags = ntohl(pflags); 25300Sstevel@tonic-gate 25310Sstevel@tonic-gate /* check for version compatibility */ 25320Sstevel@tonic-gate if (m->mf_fvers == 1 || 25330Sstevel@tonic-gate m->mf_fvers > SMFI_VERSION) 25340Sstevel@tonic-gate { 25350Sstevel@tonic-gate if (tTd(64, 5)) 25360Sstevel@tonic-gate sm_dprintf("milter_negotiate(%s): version %d != MTA milter version %d\n", 25370Sstevel@tonic-gate m->mf_name, m->mf_fvers, SMFI_VERSION); 25380Sstevel@tonic-gate if (MilterLogLevel > 0) 25390Sstevel@tonic-gate sm_syslog(LOG_ERR, e->e_id, 25400Sstevel@tonic-gate "Milter (%s): negotiate: version %d != MTA milter version %d", 25410Sstevel@tonic-gate m->mf_name, m->mf_fvers, SMFI_VERSION); 25420Sstevel@tonic-gate milter_error(m, e); 2543*3544Sjbeck goto error; 25440Sstevel@tonic-gate } 25450Sstevel@tonic-gate 25460Sstevel@tonic-gate /* check for filter feature mismatch */ 2547*3544Sjbeck if ((m->mf_fflags & mta_actions) != m->mf_fflags) 25480Sstevel@tonic-gate { 25490Sstevel@tonic-gate if (tTd(64, 5)) 25500Sstevel@tonic-gate sm_dprintf("milter_negotiate(%s): filter abilities 0x%x != MTA milter abilities 0x%lx\n", 25510Sstevel@tonic-gate m->mf_name, m->mf_fflags, 2552*3544Sjbeck (unsigned long) mta_actions); 25530Sstevel@tonic-gate if (MilterLogLevel > 0) 25540Sstevel@tonic-gate sm_syslog(LOG_ERR, e->e_id, 25550Sstevel@tonic-gate "Milter (%s): negotiate: filter abilities 0x%x != MTA milter abilities 0x%lx", 25560Sstevel@tonic-gate m->mf_name, m->mf_fflags, 2557*3544Sjbeck (unsigned long) mta_actions); 25580Sstevel@tonic-gate milter_error(m, e); 2559*3544Sjbeck goto error; 25600Sstevel@tonic-gate } 25610Sstevel@tonic-gate 25620Sstevel@tonic-gate /* check for protocol feature mismatch */ 2563*3544Sjbeck if ((m->mf_pflags & mta_prot_flags) != m->mf_pflags) 25640Sstevel@tonic-gate { 25650Sstevel@tonic-gate if (tTd(64, 5)) 25660Sstevel@tonic-gate sm_dprintf("milter_negotiate(%s): protocol abilities 0x%x != MTA milter abilities 0x%lx\n", 25670Sstevel@tonic-gate m->mf_name, m->mf_pflags, 2568*3544Sjbeck (unsigned long) mta_prot_flags); 25690Sstevel@tonic-gate if (MilterLogLevel > 0) 25700Sstevel@tonic-gate sm_syslog(LOG_ERR, e->e_id, 25710Sstevel@tonic-gate "Milter (%s): negotiate: protocol abilities 0x%x != MTA milter abilities 0x%lx", 25720Sstevel@tonic-gate m->mf_name, m->mf_pflags, 2573*3544Sjbeck (unsigned long) mta_prot_flags); 25740Sstevel@tonic-gate milter_error(m, e); 2575*3544Sjbeck goto error; 25760Sstevel@tonic-gate } 25770Sstevel@tonic-gate 2578616Sjbeck if (m->mf_fvers <= 2) 2579616Sjbeck m->mf_pflags |= SMFIP_NOUNKNOWN; 2580616Sjbeck if (m->mf_fvers <= 3) 2581616Sjbeck m->mf_pflags |= SMFIP_NODATA; 2582616Sjbeck 2583*3544Sjbeck if (rlen > MILTER_OPTLEN) 2584*3544Sjbeck { 2585*3544Sjbeck milter_getsymlist(m, response, rlen, MILTER_OPTLEN); 2586*3544Sjbeck } 2587*3544Sjbeck 25880Sstevel@tonic-gate if (tTd(64, 5)) 2589*3544Sjbeck sm_dprintf("milter_negotiate(%s): received: version %u, fflags 0x%x, pflags 0x%x\n", 25900Sstevel@tonic-gate m->mf_name, m->mf_fvers, m->mf_fflags, m->mf_pflags); 25910Sstevel@tonic-gate return 0; 2592*3544Sjbeck 2593*3544Sjbeck error: 2594*3544Sjbeck if (response != NULL) 2595*3544Sjbeck sm_free(response); /* XXX */ 2596*3544Sjbeck return -1; 25970Sstevel@tonic-gate } 2598*3544Sjbeck 25990Sstevel@tonic-gate /* 26000Sstevel@tonic-gate ** MILTER_PER_CONNECTION_CHECK -- checks on per-connection commands 26010Sstevel@tonic-gate ** 26020Sstevel@tonic-gate ** Reduce code duplication by putting these checks in one place 26030Sstevel@tonic-gate ** 26040Sstevel@tonic-gate ** Parameters: 26050Sstevel@tonic-gate ** e -- current envelope. 26060Sstevel@tonic-gate ** 26070Sstevel@tonic-gate ** Returns: 26080Sstevel@tonic-gate ** none 26090Sstevel@tonic-gate */ 26100Sstevel@tonic-gate 26110Sstevel@tonic-gate static void 26120Sstevel@tonic-gate milter_per_connection_check(e) 26130Sstevel@tonic-gate ENVELOPE *e; 26140Sstevel@tonic-gate { 26150Sstevel@tonic-gate int i; 26160Sstevel@tonic-gate 26170Sstevel@tonic-gate /* see if we are done with any of the filters */ 26180Sstevel@tonic-gate for (i = 0; InputFilters[i] != NULL; i++) 26190Sstevel@tonic-gate { 26200Sstevel@tonic-gate struct milter *m = InputFilters[i]; 26210Sstevel@tonic-gate 26220Sstevel@tonic-gate if (m->mf_state == SMFS_CLOSABLE) 26230Sstevel@tonic-gate milter_quit_filter(m, e); 26240Sstevel@tonic-gate } 26250Sstevel@tonic-gate } 2626*3544Sjbeck 26270Sstevel@tonic-gate /* 26280Sstevel@tonic-gate ** MILTER_ERROR -- Put a milter filter into error state 26290Sstevel@tonic-gate ** 26300Sstevel@tonic-gate ** Parameters: 26310Sstevel@tonic-gate ** m -- the broken filter. 26320Sstevel@tonic-gate ** e -- current envelope. 26330Sstevel@tonic-gate ** 26340Sstevel@tonic-gate ** Returns: 26350Sstevel@tonic-gate ** none 26360Sstevel@tonic-gate */ 26370Sstevel@tonic-gate 26380Sstevel@tonic-gate static void 26390Sstevel@tonic-gate milter_error(m, e) 26400Sstevel@tonic-gate struct milter *m; 26410Sstevel@tonic-gate ENVELOPE *e; 26420Sstevel@tonic-gate { 26430Sstevel@tonic-gate /* 26440Sstevel@tonic-gate ** We could send a quit here but we may have gotten here due to 26450Sstevel@tonic-gate ** an I/O error so we don't want to try to make things worse. 26460Sstevel@tonic-gate */ 26470Sstevel@tonic-gate 26480Sstevel@tonic-gate if (m->mf_sock >= 0) 26490Sstevel@tonic-gate { 26500Sstevel@tonic-gate (void) close(m->mf_sock); 26510Sstevel@tonic-gate m->mf_sock = -1; 26520Sstevel@tonic-gate } 26530Sstevel@tonic-gate m->mf_state = SMFS_ERROR; 26540Sstevel@tonic-gate 26550Sstevel@tonic-gate if (MilterLogLevel > 0) 26560Sstevel@tonic-gate sm_syslog(LOG_INFO, e->e_id, "Milter (%s): to error state", 26570Sstevel@tonic-gate m->mf_name); 26580Sstevel@tonic-gate } 2659*3544Sjbeck 26600Sstevel@tonic-gate /* 26610Sstevel@tonic-gate ** MILTER_HEADERS -- send headers to a single milter filter 26620Sstevel@tonic-gate ** 26630Sstevel@tonic-gate ** Parameters: 26640Sstevel@tonic-gate ** m -- current filter. 26650Sstevel@tonic-gate ** e -- current envelope. 26660Sstevel@tonic-gate ** state -- return state from response. 26670Sstevel@tonic-gate ** 26680Sstevel@tonic-gate ** Returns: 26690Sstevel@tonic-gate ** response string (may be NULL) 26700Sstevel@tonic-gate */ 26710Sstevel@tonic-gate 26720Sstevel@tonic-gate static char * 26730Sstevel@tonic-gate milter_headers(m, e, state) 26740Sstevel@tonic-gate struct milter *m; 26750Sstevel@tonic-gate ENVELOPE *e; 26760Sstevel@tonic-gate char *state; 26770Sstevel@tonic-gate { 26780Sstevel@tonic-gate char *response = NULL; 26790Sstevel@tonic-gate HDR *h; 26800Sstevel@tonic-gate 26810Sstevel@tonic-gate if (MilterLogLevel > 17) 26820Sstevel@tonic-gate sm_syslog(LOG_INFO, e->e_id, "Milter (%s): headers, send", 26830Sstevel@tonic-gate m->mf_name); 26840Sstevel@tonic-gate 26850Sstevel@tonic-gate for (h = e->e_header; h != NULL; h = h->h_link) 26860Sstevel@tonic-gate { 2687*3544Sjbeck int len_n, len_v, len_t, len_f; 2688*3544Sjbeck char *buf, *hv; 26890Sstevel@tonic-gate 26900Sstevel@tonic-gate /* don't send over deleted headers */ 26910Sstevel@tonic-gate if (h->h_value == NULL) 26920Sstevel@tonic-gate { 26930Sstevel@tonic-gate /* strip H_USER so not counted in milter_changeheader() */ 26940Sstevel@tonic-gate h->h_flags &= ~H_USER; 26950Sstevel@tonic-gate continue; 26960Sstevel@tonic-gate } 26970Sstevel@tonic-gate 26980Sstevel@tonic-gate /* skip auto-generated */ 26990Sstevel@tonic-gate if (!bitset(H_USER, h->h_flags)) 27000Sstevel@tonic-gate continue; 27010Sstevel@tonic-gate 27020Sstevel@tonic-gate if (tTd(64, 10)) 2703*3544Sjbeck sm_dprintf("milter_headers: %s:%s\n", 27040Sstevel@tonic-gate h->h_field, h->h_value); 27050Sstevel@tonic-gate if (MilterLogLevel > 21) 27060Sstevel@tonic-gate sm_syslog(LOG_INFO, e->e_id, "Milter (%s): header, %s", 27070Sstevel@tonic-gate m->mf_name, h->h_field); 27080Sstevel@tonic-gate 2709*3544Sjbeck if (bitset(SMFIP_HDR_LEADSPC, m->mf_pflags) 2710*3544Sjbeck || *(h->h_value) != ' ') 2711*3544Sjbeck hv = h->h_value; 2712*3544Sjbeck else 2713*3544Sjbeck hv = h->h_value + 1; 2714*3544Sjbeck len_f = strlen(h->h_field) + 1; 2715*3544Sjbeck len_t = len_f + strlen(hv) + 1; 2716*3544Sjbeck if (len_t < 0) 27170Sstevel@tonic-gate continue; 2718*3544Sjbeck buf = (char *) xalloc(len_t); 2719*3544Sjbeck 2720*3544Sjbeck /* 2721*3544Sjbeck ** Note: currently the call to dequote_internal_chars() 2722*3544Sjbeck ** is not required as h_field is supposed to be 7-bit US-ASCII. 2723*3544Sjbeck */ 2724*3544Sjbeck 2725*3544Sjbeck len_n = dequote_internal_chars(h->h_field, buf, len_f); 2726*3544Sjbeck SM_ASSERT(len_n < len_f); 2727*3544Sjbeck len_v = dequote_internal_chars(hv, buf + len_n + 1, 2728*3544Sjbeck len_t - len_n - 1); 2729*3544Sjbeck SM_ASSERT(len_t >= len_n + 1 + len_v + 1); 2730*3544Sjbeck len_t = len_n + 1 + len_v + 1; 27310Sstevel@tonic-gate 27320Sstevel@tonic-gate /* send it over */ 27330Sstevel@tonic-gate response = milter_send_command(m, SMFIC_HEADER, buf, 2734*3544Sjbeck len_t, e, state, "header"); 2735*3544Sjbeck sm_free(buf); 27360Sstevel@tonic-gate if (m->mf_state == SMFS_ERROR || 27370Sstevel@tonic-gate m->mf_state == SMFS_DONE || 27380Sstevel@tonic-gate *state != SMFIR_CONTINUE) 27390Sstevel@tonic-gate break; 27400Sstevel@tonic-gate } 27410Sstevel@tonic-gate if (MilterLogLevel > 17) 27420Sstevel@tonic-gate sm_syslog(LOG_INFO, e->e_id, "Milter (%s): headers, sent", 27430Sstevel@tonic-gate m->mf_name); 27440Sstevel@tonic-gate return response; 27450Sstevel@tonic-gate } 2746*3544Sjbeck 27470Sstevel@tonic-gate /* 27480Sstevel@tonic-gate ** MILTER_BODY -- send the body to a filter 27490Sstevel@tonic-gate ** 27500Sstevel@tonic-gate ** Parameters: 27510Sstevel@tonic-gate ** m -- current filter. 27520Sstevel@tonic-gate ** e -- current envelope. 27530Sstevel@tonic-gate ** state -- return state from response. 27540Sstevel@tonic-gate ** 27550Sstevel@tonic-gate ** Returns: 27560Sstevel@tonic-gate ** response string (may be NULL) 27570Sstevel@tonic-gate */ 27580Sstevel@tonic-gate 27590Sstevel@tonic-gate static char * 27600Sstevel@tonic-gate milter_body(m, e, state) 27610Sstevel@tonic-gate struct milter *m; 27620Sstevel@tonic-gate ENVELOPE *e; 27630Sstevel@tonic-gate char *state; 27640Sstevel@tonic-gate { 27650Sstevel@tonic-gate char bufchar = '\0'; 27660Sstevel@tonic-gate char prevchar = '\0'; 27670Sstevel@tonic-gate int c; 27680Sstevel@tonic-gate char *response = NULL; 27690Sstevel@tonic-gate char *bp; 27700Sstevel@tonic-gate char buf[MILTER_CHUNK_SIZE]; 27710Sstevel@tonic-gate 27720Sstevel@tonic-gate if (tTd(64, 10)) 27730Sstevel@tonic-gate sm_dprintf("milter_body\n"); 27740Sstevel@tonic-gate 27750Sstevel@tonic-gate if (bfrewind(e->e_dfp) < 0) 27760Sstevel@tonic-gate { 27770Sstevel@tonic-gate ExitStat = EX_IOERR; 27780Sstevel@tonic-gate *state = SMFIR_TEMPFAIL; 27790Sstevel@tonic-gate syserr("milter_body: %s/%cf%s: rewind error", 27800Sstevel@tonic-gate qid_printqueue(e->e_qgrp, e->e_qdir), 27810Sstevel@tonic-gate DATAFL_LETTER, e->e_id); 27820Sstevel@tonic-gate return NULL; 27830Sstevel@tonic-gate } 27840Sstevel@tonic-gate 27850Sstevel@tonic-gate if (MilterLogLevel > 17) 27860Sstevel@tonic-gate sm_syslog(LOG_INFO, e->e_id, "Milter (%s): body, send", 27870Sstevel@tonic-gate m->mf_name); 27880Sstevel@tonic-gate bp = buf; 27890Sstevel@tonic-gate while ((c = sm_io_getc(e->e_dfp, SM_TIME_DEFAULT)) != SM_IO_EOF) 27900Sstevel@tonic-gate { 27910Sstevel@tonic-gate /* Change LF to CRLF */ 27920Sstevel@tonic-gate if (c == '\n') 27930Sstevel@tonic-gate { 2794*3544Sjbeck #if !_FFR_MILTER_CONVERT_ALL_LF_TO_CRLF 27950Sstevel@tonic-gate /* Not a CRLF already? */ 27960Sstevel@tonic-gate if (prevchar != '\r') 2797*3544Sjbeck #endif /* !_FFR_MILTER_CONVERT_ALL_LF_TO_CRLF */ 27980Sstevel@tonic-gate { 27990Sstevel@tonic-gate /* Room for CR now? */ 2800*3544Sjbeck if (bp + 2 > &buf[sizeof(buf)]) 28010Sstevel@tonic-gate { 28020Sstevel@tonic-gate /* No room, buffer LF */ 28030Sstevel@tonic-gate bufchar = c; 28040Sstevel@tonic-gate 28050Sstevel@tonic-gate /* and send CR now */ 28060Sstevel@tonic-gate c = '\r'; 28070Sstevel@tonic-gate } 28080Sstevel@tonic-gate else 28090Sstevel@tonic-gate { 28100Sstevel@tonic-gate /* Room to do it now */ 28110Sstevel@tonic-gate *bp++ = '\r'; 28120Sstevel@tonic-gate prevchar = '\r'; 28130Sstevel@tonic-gate } 28140Sstevel@tonic-gate } 28150Sstevel@tonic-gate } 28160Sstevel@tonic-gate *bp++ = (char) c; 28170Sstevel@tonic-gate prevchar = c; 2818*3544Sjbeck if (bp >= &buf[sizeof(buf)]) 28190Sstevel@tonic-gate { 28200Sstevel@tonic-gate /* send chunk */ 28210Sstevel@tonic-gate response = milter_send_command(m, SMFIC_BODY, buf, 2822*3544Sjbeck bp - buf, e, state, 2823*3544Sjbeck "body chunk"); 28240Sstevel@tonic-gate bp = buf; 28250Sstevel@tonic-gate if (bufchar != '\0') 28260Sstevel@tonic-gate { 28270Sstevel@tonic-gate *bp++ = bufchar; 28280Sstevel@tonic-gate bufchar = '\0'; 28290Sstevel@tonic-gate prevchar = bufchar; 28300Sstevel@tonic-gate } 28310Sstevel@tonic-gate } 28320Sstevel@tonic-gate if (m->mf_state == SMFS_ERROR || 28330Sstevel@tonic-gate m->mf_state == SMFS_DONE || 2834*3544Sjbeck m->mf_state == SMFS_SKIP || 28350Sstevel@tonic-gate *state != SMFIR_CONTINUE) 28360Sstevel@tonic-gate break; 28370Sstevel@tonic-gate } 28380Sstevel@tonic-gate 28390Sstevel@tonic-gate /* check for read errors */ 28400Sstevel@tonic-gate if (sm_io_error(e->e_dfp)) 28410Sstevel@tonic-gate { 28420Sstevel@tonic-gate ExitStat = EX_IOERR; 28430Sstevel@tonic-gate if (*state == SMFIR_CONTINUE || 2844*3544Sjbeck *state == SMFIR_ACCEPT || 2845*3544Sjbeck m->mf_state == SMFS_SKIP) 28460Sstevel@tonic-gate { 28470Sstevel@tonic-gate *state = SMFIR_TEMPFAIL; 28480Sstevel@tonic-gate if (response != NULL) 28490Sstevel@tonic-gate { 28500Sstevel@tonic-gate sm_free(response); /* XXX */ 28510Sstevel@tonic-gate response = NULL; 28520Sstevel@tonic-gate } 28530Sstevel@tonic-gate } 28540Sstevel@tonic-gate syserr("milter_body: %s/%cf%s: read error", 28550Sstevel@tonic-gate qid_printqueue(e->e_qgrp, e->e_qdir), 28560Sstevel@tonic-gate DATAFL_LETTER, e->e_id); 28570Sstevel@tonic-gate return response; 28580Sstevel@tonic-gate } 28590Sstevel@tonic-gate 28600Sstevel@tonic-gate /* send last body chunk */ 28610Sstevel@tonic-gate if (bp > buf && 28620Sstevel@tonic-gate m->mf_state != SMFS_ERROR && 28630Sstevel@tonic-gate m->mf_state != SMFS_DONE && 2864*3544Sjbeck m->mf_state != SMFS_SKIP && 28650Sstevel@tonic-gate *state == SMFIR_CONTINUE) 28660Sstevel@tonic-gate { 28670Sstevel@tonic-gate /* send chunk */ 28680Sstevel@tonic-gate response = milter_send_command(m, SMFIC_BODY, buf, bp - buf, 2869*3544Sjbeck e, state, "last body chunk"); 28700Sstevel@tonic-gate bp = buf; 28710Sstevel@tonic-gate } 28720Sstevel@tonic-gate if (MilterLogLevel > 17) 28730Sstevel@tonic-gate sm_syslog(LOG_INFO, e->e_id, "Milter (%s): body, sent", 28740Sstevel@tonic-gate m->mf_name); 2875*3544Sjbeck if (m->mf_state == SMFS_SKIP) 2876*3544Sjbeck { 2877*3544Sjbeck *state = SMFIR_CONTINUE; 2878*3544Sjbeck m->mf_state = SMFS_READY; 2879*3544Sjbeck } 2880*3544Sjbeck 28810Sstevel@tonic-gate return response; 28820Sstevel@tonic-gate } 28830Sstevel@tonic-gate 28840Sstevel@tonic-gate /* 28850Sstevel@tonic-gate ** Actions 28860Sstevel@tonic-gate */ 28870Sstevel@tonic-gate 28880Sstevel@tonic-gate /* 2889*3544Sjbeck ** ADDLEADINGSPACE -- Add a leading space to a string 2890*3544Sjbeck ** 2891*3544Sjbeck ** Parameters: 2892*3544Sjbeck ** str -- string 2893*3544Sjbeck ** rp -- resource pool for allocations 2894*3544Sjbeck ** 2895*3544Sjbeck ** Returns: 2896*3544Sjbeck ** pointer to new string 2897*3544Sjbeck */ 2898*3544Sjbeck 2899*3544Sjbeck static char *addleadingspace __P((char *, SM_RPOOL_T *)); 2900*3544Sjbeck 2901*3544Sjbeck static char * 2902*3544Sjbeck addleadingspace(str, rp) 2903*3544Sjbeck char *str; 2904*3544Sjbeck SM_RPOOL_T *rp; 2905*3544Sjbeck { 2906*3544Sjbeck size_t l; 2907*3544Sjbeck char *new; 2908*3544Sjbeck 2909*3544Sjbeck SM_ASSERT(str != NULL); 2910*3544Sjbeck l = strlen(str); 2911*3544Sjbeck SM_ASSERT(l + 2 > l); 2912*3544Sjbeck new = sm_rpool_malloc_x(rp, l + 2); 2913*3544Sjbeck new[0] = ' '; 2914*3544Sjbeck new[1] = '\0'; 2915*3544Sjbeck sm_strlcpy(new + 1, str, l + 1); 2916*3544Sjbeck return new; 2917*3544Sjbeck } 2918*3544Sjbeck 2919*3544Sjbeck /* 29200Sstevel@tonic-gate ** MILTER_ADDHEADER -- Add the supplied header to the message 29210Sstevel@tonic-gate ** 29220Sstevel@tonic-gate ** Parameters: 2923*3544Sjbeck ** m -- current filter. 29240Sstevel@tonic-gate ** response -- encoded form of header/value. 29250Sstevel@tonic-gate ** rlen -- length of response. 29260Sstevel@tonic-gate ** e -- current envelope. 29270Sstevel@tonic-gate ** 29280Sstevel@tonic-gate ** Returns: 29290Sstevel@tonic-gate ** none 29300Sstevel@tonic-gate */ 29310Sstevel@tonic-gate 29320Sstevel@tonic-gate static void 2933*3544Sjbeck milter_addheader(m, response, rlen, e) 2934*3544Sjbeck struct milter *m; 29350Sstevel@tonic-gate char *response; 29360Sstevel@tonic-gate ssize_t rlen; 29370Sstevel@tonic-gate ENVELOPE *e; 29380Sstevel@tonic-gate { 2939*3544Sjbeck int mh_v_len; 2940*3544Sjbeck char *val, *mh_value; 29410Sstevel@tonic-gate HDR *h; 29420Sstevel@tonic-gate 29430Sstevel@tonic-gate if (tTd(64, 10)) 29440Sstevel@tonic-gate sm_dprintf("milter_addheader: "); 29450Sstevel@tonic-gate 29460Sstevel@tonic-gate /* sanity checks */ 29470Sstevel@tonic-gate if (response == NULL) 29480Sstevel@tonic-gate { 29490Sstevel@tonic-gate if (tTd(64, 10)) 29500Sstevel@tonic-gate sm_dprintf("NULL response\n"); 29510Sstevel@tonic-gate return; 29520Sstevel@tonic-gate } 29530Sstevel@tonic-gate 29540Sstevel@tonic-gate if (rlen < 2 || strlen(response) + 1 >= (size_t) rlen) 29550Sstevel@tonic-gate { 29560Sstevel@tonic-gate if (tTd(64, 10)) 2957*3544Sjbeck sm_dprintf("didn't follow protocol (total len %d != rlen %d)\n", 2958*3544Sjbeck (int) strlen(response), (int) (rlen - 1)); 29590Sstevel@tonic-gate return; 29600Sstevel@tonic-gate } 29610Sstevel@tonic-gate 29620Sstevel@tonic-gate /* Find separating NUL */ 29630Sstevel@tonic-gate val = response + strlen(response) + 1; 29640Sstevel@tonic-gate 29650Sstevel@tonic-gate /* another sanity check */ 29660Sstevel@tonic-gate if (strlen(response) + strlen(val) + 2 != (size_t) rlen) 29670Sstevel@tonic-gate { 29680Sstevel@tonic-gate if (tTd(64, 10)) 29690Sstevel@tonic-gate sm_dprintf("didn't follow protocol (part len)\n"); 29700Sstevel@tonic-gate return; 29710Sstevel@tonic-gate } 29720Sstevel@tonic-gate 29730Sstevel@tonic-gate if (*response == '\0') 29740Sstevel@tonic-gate { 29750Sstevel@tonic-gate if (tTd(64, 10)) 29760Sstevel@tonic-gate sm_dprintf("empty field name\n"); 29770Sstevel@tonic-gate return; 29780Sstevel@tonic-gate } 29790Sstevel@tonic-gate 29800Sstevel@tonic-gate for (h = e->e_header; h != NULL; h = h->h_link) 29810Sstevel@tonic-gate { 29820Sstevel@tonic-gate if (sm_strcasecmp(h->h_field, response) == 0 && 29830Sstevel@tonic-gate !bitset(H_USER, h->h_flags) && 29840Sstevel@tonic-gate !bitset(H_TRACE, h->h_flags)) 29850Sstevel@tonic-gate break; 29860Sstevel@tonic-gate } 29870Sstevel@tonic-gate 2988*3544Sjbeck mh_v_len = 0; 2989*3544Sjbeck mh_value = quote_internal_chars(val, NULL, &mh_v_len); 2990*3544Sjbeck 29910Sstevel@tonic-gate /* add to e_msgsize */ 29920Sstevel@tonic-gate e->e_msgsize += strlen(response) + 2 + strlen(val); 29930Sstevel@tonic-gate 29940Sstevel@tonic-gate if (h != NULL) 29950Sstevel@tonic-gate { 29960Sstevel@tonic-gate if (tTd(64, 10)) 29970Sstevel@tonic-gate sm_dprintf("Replace default header %s value with %s\n", 2998*3544Sjbeck h->h_field, mh_value); 29990Sstevel@tonic-gate if (MilterLogLevel > 8) 30000Sstevel@tonic-gate sm_syslog(LOG_INFO, e->e_id, 30010Sstevel@tonic-gate "Milter change: default header %s value with %s", 3002*3544Sjbeck h->h_field, mh_value); 3003*3544Sjbeck if (bitset(SMFIP_HDR_LEADSPC, m->mf_pflags)) 3004*3544Sjbeck h->h_value = mh_value; 3005*3544Sjbeck else 3006*3544Sjbeck { 3007*3544Sjbeck h->h_value = addleadingspace (mh_value, e->e_rpool); 3008*3544Sjbeck SM_FREE(mh_value); 3009*3544Sjbeck } 30100Sstevel@tonic-gate h->h_flags |= H_USER; 30110Sstevel@tonic-gate } 30120Sstevel@tonic-gate else 30130Sstevel@tonic-gate { 30140Sstevel@tonic-gate if (tTd(64, 10)) 3015*3544Sjbeck sm_dprintf("Add %s: %s\n", response, mh_value); 30160Sstevel@tonic-gate if (MilterLogLevel > 8) 3017*3544Sjbeck sm_syslog(LOG_INFO, e->e_id, 3018*3544Sjbeck "Milter add: header: %s: %s", 3019*3544Sjbeck response, mh_value); 3020*3544Sjbeck addheader(newstr(response), mh_value, H_USER, e, 3021*3544Sjbeck !bitset(SMFIP_HDR_LEADSPC, m->mf_pflags)); 3022*3544Sjbeck SM_FREE(mh_value); 30230Sstevel@tonic-gate } 30240Sstevel@tonic-gate } 3025*3544Sjbeck 30260Sstevel@tonic-gate /* 30270Sstevel@tonic-gate ** MILTER_INSHEADER -- Insert the supplied header 30280Sstevel@tonic-gate ** 30290Sstevel@tonic-gate ** Parameters: 3030*3544Sjbeck ** m -- current filter. 30310Sstevel@tonic-gate ** response -- encoded form of header/value. 30320Sstevel@tonic-gate ** rlen -- length of response. 30330Sstevel@tonic-gate ** e -- current envelope. 30340Sstevel@tonic-gate ** 30350Sstevel@tonic-gate ** Returns: 30360Sstevel@tonic-gate ** none 30370Sstevel@tonic-gate ** 3038616Sjbeck ** Notes: 3039616Sjbeck ** Unlike milter_addheader(), this does not attempt to determine 3040616Sjbeck ** if the header already exists in the envelope, even a 3041616Sjbeck ** deleted version. It just blindly inserts. 30420Sstevel@tonic-gate */ 30430Sstevel@tonic-gate 30440Sstevel@tonic-gate static void 3045*3544Sjbeck milter_insheader(m, response, rlen, e) 3046*3544Sjbeck struct milter *m; 30470Sstevel@tonic-gate char *response; 30480Sstevel@tonic-gate ssize_t rlen; 30490Sstevel@tonic-gate ENVELOPE *e; 30500Sstevel@tonic-gate { 30510Sstevel@tonic-gate mi_int32 idx, i; 3052*3544Sjbeck int mh_v_len; 3053*3544Sjbeck char *field, *val, *mh_value; 30540Sstevel@tonic-gate 30550Sstevel@tonic-gate if (tTd(64, 10)) 30560Sstevel@tonic-gate sm_dprintf("milter_insheader: "); 30570Sstevel@tonic-gate 30580Sstevel@tonic-gate /* sanity checks */ 30590Sstevel@tonic-gate if (response == NULL) 30600Sstevel@tonic-gate { 30610Sstevel@tonic-gate if (tTd(64, 10)) 30620Sstevel@tonic-gate sm_dprintf("NULL response\n"); 30630Sstevel@tonic-gate return; 30640Sstevel@tonic-gate } 30650Sstevel@tonic-gate 30660Sstevel@tonic-gate if (rlen < 2 || strlen(response) + 1 >= (size_t) rlen) 30670Sstevel@tonic-gate { 30680Sstevel@tonic-gate if (tTd(64, 10)) 30690Sstevel@tonic-gate sm_dprintf("didn't follow protocol (total len)\n"); 30700Sstevel@tonic-gate return; 30710Sstevel@tonic-gate } 30720Sstevel@tonic-gate 30730Sstevel@tonic-gate /* decode */ 30740Sstevel@tonic-gate (void) memcpy((char *) &i, response, MILTER_LEN_BYTES); 30750Sstevel@tonic-gate idx = ntohl(i); 30760Sstevel@tonic-gate field = response + MILTER_LEN_BYTES; 30770Sstevel@tonic-gate val = field + strlen(field) + 1; 30780Sstevel@tonic-gate 30790Sstevel@tonic-gate /* another sanity check */ 30800Sstevel@tonic-gate if (MILTER_LEN_BYTES + strlen(field) + 1 + 30810Sstevel@tonic-gate strlen(val) + 1 != (size_t) rlen) 30820Sstevel@tonic-gate { 30830Sstevel@tonic-gate if (tTd(64, 10)) 30840Sstevel@tonic-gate sm_dprintf("didn't follow protocol (part len)\n"); 30850Sstevel@tonic-gate return; 30860Sstevel@tonic-gate } 30870Sstevel@tonic-gate 30880Sstevel@tonic-gate if (*field == '\0') 30890Sstevel@tonic-gate { 30900Sstevel@tonic-gate if (tTd(64, 10)) 30910Sstevel@tonic-gate sm_dprintf("empty field name\n"); 30920Sstevel@tonic-gate return; 30930Sstevel@tonic-gate } 30940Sstevel@tonic-gate 30950Sstevel@tonic-gate /* add to e_msgsize */ 30960Sstevel@tonic-gate e->e_msgsize += strlen(response) + 2 + strlen(val); 30970Sstevel@tonic-gate 30980Sstevel@tonic-gate if (tTd(64, 10)) 3099*3544Sjbeck sm_dprintf("Insert (%d) %s: %s\n", idx, field, val); 31000Sstevel@tonic-gate if (MilterLogLevel > 8) 31010Sstevel@tonic-gate sm_syslog(LOG_INFO, e->e_id, 3102616Sjbeck "Milter insert (%d): header: %s: %s", 31030Sstevel@tonic-gate idx, field, val); 3104*3544Sjbeck mh_v_len = 0; 3105*3544Sjbeck mh_value = quote_internal_chars(val, NULL, &mh_v_len); 3106*3544Sjbeck insheader(idx, newstr(field), mh_value, H_USER, e, 3107*3544Sjbeck !bitset(SMFIP_HDR_LEADSPC, m->mf_pflags)); 3108*3544Sjbeck SM_FREE(mh_value); 31090Sstevel@tonic-gate } 3110*3544Sjbeck 31110Sstevel@tonic-gate /* 31120Sstevel@tonic-gate ** MILTER_CHANGEHEADER -- Change the supplied header in the message 31130Sstevel@tonic-gate ** 31140Sstevel@tonic-gate ** Parameters: 3115*3544Sjbeck ** m -- current filter. 31160Sstevel@tonic-gate ** response -- encoded form of header/index/value. 31170Sstevel@tonic-gate ** rlen -- length of response. 31180Sstevel@tonic-gate ** e -- current envelope. 31190Sstevel@tonic-gate ** 31200Sstevel@tonic-gate ** Returns: 31210Sstevel@tonic-gate ** none 31220Sstevel@tonic-gate */ 31230Sstevel@tonic-gate 31240Sstevel@tonic-gate static void 3125*3544Sjbeck milter_changeheader(m, response, rlen, e) 3126*3544Sjbeck struct milter *m; 31270Sstevel@tonic-gate char *response; 31280Sstevel@tonic-gate ssize_t rlen; 31290Sstevel@tonic-gate ENVELOPE *e; 31300Sstevel@tonic-gate { 31310Sstevel@tonic-gate mi_int32 i, index; 3132*3544Sjbeck int mh_v_len; 3133*3544Sjbeck char *field, *val, *mh_value; 31340Sstevel@tonic-gate HDR *h, *sysheader; 31350Sstevel@tonic-gate 31360Sstevel@tonic-gate if (tTd(64, 10)) 31370Sstevel@tonic-gate sm_dprintf("milter_changeheader: "); 31380Sstevel@tonic-gate 31390Sstevel@tonic-gate /* sanity checks */ 31400Sstevel@tonic-gate if (response == NULL) 31410Sstevel@tonic-gate { 31420Sstevel@tonic-gate if (tTd(64, 10)) 31430Sstevel@tonic-gate sm_dprintf("NULL response\n"); 31440Sstevel@tonic-gate return; 31450Sstevel@tonic-gate } 31460Sstevel@tonic-gate 31470Sstevel@tonic-gate if (rlen < 2 || strlen(response) + 1 >= (size_t) rlen) 31480Sstevel@tonic-gate { 31490Sstevel@tonic-gate if (tTd(64, 10)) 31500Sstevel@tonic-gate sm_dprintf("didn't follow protocol (total len)\n"); 31510Sstevel@tonic-gate return; 31520Sstevel@tonic-gate } 31530Sstevel@tonic-gate 31540Sstevel@tonic-gate /* Find separating NUL */ 31550Sstevel@tonic-gate (void) memcpy((char *) &i, response, MILTER_LEN_BYTES); 31560Sstevel@tonic-gate index = ntohl(i); 31570Sstevel@tonic-gate field = response + MILTER_LEN_BYTES; 31580Sstevel@tonic-gate val = field + strlen(field) + 1; 31590Sstevel@tonic-gate 31600Sstevel@tonic-gate /* another sanity check */ 31610Sstevel@tonic-gate if (MILTER_LEN_BYTES + strlen(field) + 1 + 31620Sstevel@tonic-gate strlen(val) + 1 != (size_t) rlen) 31630Sstevel@tonic-gate { 31640Sstevel@tonic-gate if (tTd(64, 10)) 31650Sstevel@tonic-gate sm_dprintf("didn't follow protocol (part len)\n"); 31660Sstevel@tonic-gate return; 31670Sstevel@tonic-gate } 31680Sstevel@tonic-gate 31690Sstevel@tonic-gate if (*field == '\0') 31700Sstevel@tonic-gate { 31710Sstevel@tonic-gate if (tTd(64, 10)) 31720Sstevel@tonic-gate sm_dprintf("empty field name\n"); 31730Sstevel@tonic-gate return; 31740Sstevel@tonic-gate } 31750Sstevel@tonic-gate 3176*3544Sjbeck mh_v_len = 0; 3177*3544Sjbeck mh_value = quote_internal_chars(val, NULL, &mh_v_len); 3178*3544Sjbeck 31790Sstevel@tonic-gate sysheader = NULL; 31800Sstevel@tonic-gate for (h = e->e_header; h != NULL; h = h->h_link) 31810Sstevel@tonic-gate { 31820Sstevel@tonic-gate if (sm_strcasecmp(h->h_field, field) == 0) 31830Sstevel@tonic-gate { 3184*3544Sjbeck if (bitset(H_USER, h->h_flags) && --index <= 0) 31850Sstevel@tonic-gate { 31860Sstevel@tonic-gate sysheader = NULL; 31870Sstevel@tonic-gate break; 31880Sstevel@tonic-gate } 31890Sstevel@tonic-gate else if (!bitset(H_USER, h->h_flags) && 31900Sstevel@tonic-gate !bitset(H_TRACE, h->h_flags)) 31910Sstevel@tonic-gate { 31920Sstevel@tonic-gate /* 31930Sstevel@tonic-gate ** DRUMS msg-fmt draft says can only have 31940Sstevel@tonic-gate ** multiple occurences of trace fields, 31950Sstevel@tonic-gate ** so make sure we replace any non-trace, 31960Sstevel@tonic-gate ** non-user field. 31970Sstevel@tonic-gate */ 31980Sstevel@tonic-gate 31990Sstevel@tonic-gate sysheader = h; 32000Sstevel@tonic-gate } 32010Sstevel@tonic-gate } 32020Sstevel@tonic-gate } 32030Sstevel@tonic-gate 32040Sstevel@tonic-gate /* if not found as user-provided header at index, use sysheader */ 32050Sstevel@tonic-gate if (h == NULL) 32060Sstevel@tonic-gate h = sysheader; 32070Sstevel@tonic-gate 32080Sstevel@tonic-gate if (h == NULL) 32090Sstevel@tonic-gate { 32100Sstevel@tonic-gate if (*val == '\0') 32110Sstevel@tonic-gate { 32120Sstevel@tonic-gate if (tTd(64, 10)) 32130Sstevel@tonic-gate sm_dprintf("Delete (noop) %s\n", field); 32140Sstevel@tonic-gate if (MilterLogLevel > 8) 32150Sstevel@tonic-gate sm_syslog(LOG_INFO, e->e_id, 32160Sstevel@tonic-gate "Milter delete (noop): header: %s" 32170Sstevel@tonic-gate , field); 32180Sstevel@tonic-gate } 32190Sstevel@tonic-gate else 32200Sstevel@tonic-gate { 32210Sstevel@tonic-gate /* treat modify value with no existing header as add */ 32220Sstevel@tonic-gate if (tTd(64, 10)) 3223*3544Sjbeck sm_dprintf("Add %s: %s\n", field, mh_value); 32240Sstevel@tonic-gate if (MilterLogLevel > 8) 32250Sstevel@tonic-gate sm_syslog(LOG_INFO, e->e_id, 32260Sstevel@tonic-gate "Milter change (add): header: %s: %s" 3227*3544Sjbeck , field, mh_value); 3228*3544Sjbeck addheader(newstr(field), mh_value, H_USER, e, 3229*3544Sjbeck !bitset(SMFIP_HDR_LEADSPC, m->mf_pflags)); 32300Sstevel@tonic-gate } 32310Sstevel@tonic-gate return; 32320Sstevel@tonic-gate } 32330Sstevel@tonic-gate 32340Sstevel@tonic-gate if (tTd(64, 10)) 32350Sstevel@tonic-gate { 32360Sstevel@tonic-gate if (*val == '\0') 32370Sstevel@tonic-gate { 3238*3544Sjbeck sm_dprintf("Delete%s %s:%s\n", 32390Sstevel@tonic-gate h == sysheader ? " (default header)" : "", 32400Sstevel@tonic-gate field, 32410Sstevel@tonic-gate h->h_value == NULL ? "<NULL>" : h->h_value); 32420Sstevel@tonic-gate } 32430Sstevel@tonic-gate else 32440Sstevel@tonic-gate { 32450Sstevel@tonic-gate sm_dprintf("Change%s %s: from %s to %s\n", 32460Sstevel@tonic-gate h == sysheader ? " (default header)" : "", 32470Sstevel@tonic-gate field, 32480Sstevel@tonic-gate h->h_value == NULL ? "<NULL>" : h->h_value, 3249*3544Sjbeck mh_value); 32500Sstevel@tonic-gate } 32510Sstevel@tonic-gate } 32520Sstevel@tonic-gate 32530Sstevel@tonic-gate if (MilterLogLevel > 8) 32540Sstevel@tonic-gate { 32550Sstevel@tonic-gate if (*val == '\0') 32560Sstevel@tonic-gate { 32570Sstevel@tonic-gate sm_syslog(LOG_INFO, e->e_id, 3258*3544Sjbeck "Milter delete: header%s %s:%s", 32590Sstevel@tonic-gate h == sysheader ? " (default header)" : "", 32600Sstevel@tonic-gate field, 32610Sstevel@tonic-gate h->h_value == NULL ? "<NULL>" : h->h_value); 32620Sstevel@tonic-gate } 32630Sstevel@tonic-gate else 32640Sstevel@tonic-gate { 32650Sstevel@tonic-gate sm_syslog(LOG_INFO, e->e_id, 32660Sstevel@tonic-gate "Milter change: header%s %s: from %s to %s", 32670Sstevel@tonic-gate h == sysheader ? " (default header)" : "", 32680Sstevel@tonic-gate field, 32690Sstevel@tonic-gate h->h_value == NULL ? "<NULL>" : h->h_value, 3270*3544Sjbeck mh_value); 32710Sstevel@tonic-gate } 32720Sstevel@tonic-gate } 32730Sstevel@tonic-gate 32740Sstevel@tonic-gate if (h != sysheader && h->h_value != NULL) 32750Sstevel@tonic-gate { 32760Sstevel@tonic-gate size_t l; 32770Sstevel@tonic-gate 32780Sstevel@tonic-gate l = strlen(h->h_value); 32790Sstevel@tonic-gate if (l > e->e_msgsize) 32800Sstevel@tonic-gate e->e_msgsize = 0; 32810Sstevel@tonic-gate else 32820Sstevel@tonic-gate e->e_msgsize -= l; 32830Sstevel@tonic-gate /* rpool, don't free: sm_free(h->h_value); XXX */ 32840Sstevel@tonic-gate } 32850Sstevel@tonic-gate 32860Sstevel@tonic-gate if (*val == '\0') 32870Sstevel@tonic-gate { 32880Sstevel@tonic-gate /* Remove "Field: " from message size */ 32890Sstevel@tonic-gate if (h != sysheader) 32900Sstevel@tonic-gate { 32910Sstevel@tonic-gate size_t l; 32920Sstevel@tonic-gate 32930Sstevel@tonic-gate l = strlen(h->h_field) + 2; 32940Sstevel@tonic-gate if (l > e->e_msgsize) 32950Sstevel@tonic-gate e->e_msgsize = 0; 32960Sstevel@tonic-gate else 32970Sstevel@tonic-gate e->e_msgsize -= l; 32980Sstevel@tonic-gate } 32990Sstevel@tonic-gate h->h_value = NULL; 3300*3544Sjbeck SM_FREE(mh_value); 33010Sstevel@tonic-gate } 33020Sstevel@tonic-gate else 33030Sstevel@tonic-gate { 3304*3544Sjbeck if (bitset(SMFIP_HDR_LEADSPC, m->mf_pflags)) 3305*3544Sjbeck h->h_value = mh_value; 3306*3544Sjbeck else 3307*3544Sjbeck { 3308*3544Sjbeck h->h_value = addleadingspace (mh_value, e->e_rpool); 3309*3544Sjbeck SM_FREE(mh_value); 3310*3544Sjbeck } 33110Sstevel@tonic-gate h->h_flags |= H_USER; 33120Sstevel@tonic-gate e->e_msgsize += strlen(h->h_value); 33130Sstevel@tonic-gate } 33140Sstevel@tonic-gate } 3315*3544Sjbeck 3316*3544Sjbeck /* 3317*3544Sjbeck ** MILTER_SPLIT_RESPONSE -- Split response into fields. 3318*3544Sjbeck ** 3319*3544Sjbeck ** Parameters: 3320*3544Sjbeck ** response -- encoded repsonse. 3321*3544Sjbeck ** rlen -- length of response. 3322*3544Sjbeck ** pargc -- number of arguments (ouput) 3323*3544Sjbeck ** 3324*3544Sjbeck ** Returns: 3325*3544Sjbeck ** array of pointers to the individual strings 3326*3544Sjbeck */ 3327*3544Sjbeck 3328*3544Sjbeck static char **milter_split_response __P((char *, ssize_t, int *)); 3329*3544Sjbeck 3330*3544Sjbeck static char ** 3331*3544Sjbeck milter_split_response(response, rlen, pargc) 3332*3544Sjbeck char *response; 3333*3544Sjbeck ssize_t rlen; 3334*3544Sjbeck int *pargc; 3335*3544Sjbeck { 3336*3544Sjbeck char **s; 3337*3544Sjbeck size_t i; 3338*3544Sjbeck int elem, nelem; 3339*3544Sjbeck 3340*3544Sjbeck SM_ASSERT(response != NULL); 3341*3544Sjbeck SM_ASSERT(pargc != NULL); 3342*3544Sjbeck *pargc = 0; 3343*3544Sjbeck if (rlen < 2 || strlen(response) >= (size_t) rlen) 3344*3544Sjbeck { 3345*3544Sjbeck if (tTd(64, 10)) 3346*3544Sjbeck sm_dprintf("didn't follow protocol (total len %d != rlen %d)\n", 3347*3544Sjbeck (int) strlen(response), (int) (rlen - 1)); 3348*3544Sjbeck return NULL; 3349*3544Sjbeck } 3350*3544Sjbeck 3351*3544Sjbeck nelem = 0; 3352*3544Sjbeck for (i = 0; i < rlen; i++) 3353*3544Sjbeck { 3354*3544Sjbeck if (response[i] == '\0') 3355*3544Sjbeck ++nelem; 3356*3544Sjbeck } 3357*3544Sjbeck if (nelem == 0) 3358*3544Sjbeck return NULL; 3359*3544Sjbeck 3360*3544Sjbeck /* last entry is only for the name */ 3361*3544Sjbeck s = (char **)malloc(nelem * (sizeof(*s))); 3362*3544Sjbeck if (s == NULL) 3363*3544Sjbeck return NULL; 3364*3544Sjbeck s[0] = response; 3365*3544Sjbeck for (i = 0, elem = 0; i < rlen && elem < nelem; i++) 3366*3544Sjbeck { 3367*3544Sjbeck if (response[i] == '\0') 3368*3544Sjbeck { 3369*3544Sjbeck ++elem; 3370*3544Sjbeck if (i + 1 >= rlen) 3371*3544Sjbeck s[elem] = NULL; 3372*3544Sjbeck else 3373*3544Sjbeck s[elem] = &(response[i + 1]); 3374*3544Sjbeck } 3375*3544Sjbeck } 3376*3544Sjbeck *pargc = nelem; 3377*3544Sjbeck 3378*3544Sjbeck if (tTd(64, 10)) 3379*3544Sjbeck { 3380*3544Sjbeck for (elem = 0; elem < nelem; elem++) 3381*3544Sjbeck sm_dprintf("argv[%d]=\"%s\"\n", elem, s[elem]); 3382*3544Sjbeck } 3383*3544Sjbeck 3384*3544Sjbeck /* overwrite last entry (already done above, just paranoia) */ 3385*3544Sjbeck s[elem] = NULL; 3386*3544Sjbeck return s; 3387*3544Sjbeck } 3388*3544Sjbeck 3389*3544Sjbeck /* 3390*3544Sjbeck ** MILTER_CHGFROM -- Change the envelope sender address 3391*3544Sjbeck ** 3392*3544Sjbeck ** Parameters: 3393*3544Sjbeck ** response -- encoded form of recipient address. 3394*3544Sjbeck ** rlen -- length of response. 3395*3544Sjbeck ** e -- current envelope. 3396*3544Sjbeck ** 3397*3544Sjbeck ** Returns: 3398*3544Sjbeck ** none 3399*3544Sjbeck */ 3400*3544Sjbeck 3401*3544Sjbeck static void 3402*3544Sjbeck milter_chgfrom(response, rlen, e) 3403*3544Sjbeck char *response; 3404*3544Sjbeck ssize_t rlen; 3405*3544Sjbeck ENVELOPE *e; 3406*3544Sjbeck { 3407*3544Sjbeck int olderrors, argc; 3408*3544Sjbeck char **argv; 3409*3544Sjbeck 3410*3544Sjbeck if (tTd(64, 10)) 3411*3544Sjbeck sm_dprintf("milter_chgfrom: "); 3412*3544Sjbeck 3413*3544Sjbeck /* sanity checks */ 3414*3544Sjbeck if (response == NULL) 3415*3544Sjbeck { 3416*3544Sjbeck if (tTd(64, 10)) 3417*3544Sjbeck sm_dprintf("NULL response\n"); 3418*3544Sjbeck return; 3419*3544Sjbeck } 3420*3544Sjbeck 3421*3544Sjbeck if (*response == '\0' || 3422*3544Sjbeck strlen(response) + 1 > (size_t) rlen) 3423*3544Sjbeck { 3424*3544Sjbeck if (tTd(64, 10)) 3425*3544Sjbeck sm_dprintf("didn't follow protocol (total len %d != rlen %d)\n", 3426*3544Sjbeck (int) strlen(response), (int) (rlen - 1)); 3427*3544Sjbeck return; 3428*3544Sjbeck } 3429*3544Sjbeck 3430*3544Sjbeck if (tTd(64, 10)) 3431*3544Sjbeck sm_dprintf("%s\n", response); 3432*3544Sjbeck if (MilterLogLevel > 8) 3433*3544Sjbeck sm_syslog(LOG_INFO, e->e_id, "Milter chgfrom: %s", response); 3434*3544Sjbeck argv = milter_split_response(response, rlen, &argc); 3435*3544Sjbeck if (argc < 1 || argc > 2) 3436*3544Sjbeck { 3437*3544Sjbeck if (tTd(64, 10)) 3438*3544Sjbeck sm_dprintf("didn't follow protocol argc=%d\n", argc); 3439*3544Sjbeck return; 3440*3544Sjbeck } 3441*3544Sjbeck 3442*3544Sjbeck olderrors = Errors; 3443*3544Sjbeck setsender(argv[0], e, NULL, '\0', false); 3444*3544Sjbeck if (argc == 2) 3445*3544Sjbeck { 3446*3544Sjbeck reset_mail_esmtp_args(e); 3447*3544Sjbeck 3448*3544Sjbeck /* 3449*3544Sjbeck ** need "features" here: how to get those? via e? 3450*3544Sjbeck ** "fake" it for now: allow everything. 3451*3544Sjbeck */ 3452*3544Sjbeck 3453*3544Sjbeck parse_esmtp_args(e, NULL, argv[0], argv[1], "MAIL", NULL, 3454*3544Sjbeck mail_esmtp_args); 3455*3544Sjbeck } 3456*3544Sjbeck Errors = olderrors; 3457*3544Sjbeck return; 3458*3544Sjbeck } 3459*3544Sjbeck 3460*3544Sjbeck /* 3461*3544Sjbeck ** MILTER_ADDRCPT_PAR -- Add the supplied recipient to the message 3462*3544Sjbeck ** 3463*3544Sjbeck ** Parameters: 3464*3544Sjbeck ** response -- encoded form of recipient address. 3465*3544Sjbeck ** rlen -- length of response. 3466*3544Sjbeck ** e -- current envelope. 3467*3544Sjbeck ** 3468*3544Sjbeck ** Returns: 3469*3544Sjbeck ** none 3470*3544Sjbeck */ 3471*3544Sjbeck 3472*3544Sjbeck static void 3473*3544Sjbeck milter_addrcpt_par(response, rlen, e) 3474*3544Sjbeck char *response; 3475*3544Sjbeck ssize_t rlen; 3476*3544Sjbeck ENVELOPE *e; 3477*3544Sjbeck { 3478*3544Sjbeck int olderrors, argc; 3479*3544Sjbeck char *delimptr; 3480*3544Sjbeck char **argv; 3481*3544Sjbeck ADDRESS *a; 3482*3544Sjbeck 3483*3544Sjbeck if (tTd(64, 10)) 3484*3544Sjbeck sm_dprintf("milter_addrcpt_par: "); 3485*3544Sjbeck 3486*3544Sjbeck /* sanity checks */ 3487*3544Sjbeck if (response == NULL) 3488*3544Sjbeck { 3489*3544Sjbeck if (tTd(64, 10)) 3490*3544Sjbeck sm_dprintf("NULL response\n"); 3491*3544Sjbeck return; 3492*3544Sjbeck } 3493*3544Sjbeck 3494*3544Sjbeck if (tTd(64, 10)) 3495*3544Sjbeck sm_dprintf("%s\n", response); 3496*3544Sjbeck if (MilterLogLevel > 8) 3497*3544Sjbeck sm_syslog(LOG_INFO, e->e_id, "Milter add: rcpt: %s", response); 3498*3544Sjbeck 3499*3544Sjbeck argv = milter_split_response(response, rlen, &argc); 3500*3544Sjbeck if (argc < 1 || argc > 2) 3501*3544Sjbeck { 3502*3544Sjbeck if (tTd(64, 10)) 3503*3544Sjbeck sm_dprintf("didn't follow protocol argc=%d\n", argc); 3504*3544Sjbeck return; 3505*3544Sjbeck } 3506*3544Sjbeck olderrors = Errors; 3507*3544Sjbeck 3508*3544Sjbeck /* how to set ESMTP arguments? */ 3509*3544Sjbeck a = parseaddr(argv[0], NULLADDR, RF_COPYALL, ' ', &delimptr, e, true); 3510*3544Sjbeck 3511*3544Sjbeck if (a != NULL && olderrors == Errors) 3512*3544Sjbeck { 3513*3544Sjbeck parse_esmtp_args(e, a, argv[0], argv[1], "RCPT", NULL, 3514*3544Sjbeck rcpt_esmtp_args); 3515*3544Sjbeck if (olderrors == Errors) 3516*3544Sjbeck a = recipient(a, &e->e_sendqueue, 0, e); 3517*3544Sjbeck else 3518*3544Sjbeck sm_dprintf("olderrors=%d, Errors=%d\n", 3519*3544Sjbeck olderrors, Errors); 3520*3544Sjbeck } 3521*3544Sjbeck else 3522*3544Sjbeck { 3523*3544Sjbeck sm_dprintf("a=%p, olderrors=%d, Errors=%d\n", 3524*3544Sjbeck a, olderrors, Errors); 3525*3544Sjbeck } 3526*3544Sjbeck 3527*3544Sjbeck Errors = olderrors; 3528*3544Sjbeck return; 3529*3544Sjbeck } 3530*3544Sjbeck 35310Sstevel@tonic-gate /* 35320Sstevel@tonic-gate ** MILTER_ADDRCPT -- Add the supplied recipient to the message 35330Sstevel@tonic-gate ** 35340Sstevel@tonic-gate ** Parameters: 35350Sstevel@tonic-gate ** response -- encoded form of recipient address. 35360Sstevel@tonic-gate ** rlen -- length of response. 35370Sstevel@tonic-gate ** e -- current envelope. 35380Sstevel@tonic-gate ** 35390Sstevel@tonic-gate ** Returns: 35400Sstevel@tonic-gate ** none 35410Sstevel@tonic-gate */ 35420Sstevel@tonic-gate 35430Sstevel@tonic-gate static void 35440Sstevel@tonic-gate milter_addrcpt(response, rlen, e) 35450Sstevel@tonic-gate char *response; 35460Sstevel@tonic-gate ssize_t rlen; 35470Sstevel@tonic-gate ENVELOPE *e; 35480Sstevel@tonic-gate { 35490Sstevel@tonic-gate int olderrors; 35500Sstevel@tonic-gate 35510Sstevel@tonic-gate if (tTd(64, 10)) 35520Sstevel@tonic-gate sm_dprintf("milter_addrcpt: "); 35530Sstevel@tonic-gate 35540Sstevel@tonic-gate /* sanity checks */ 35550Sstevel@tonic-gate if (response == NULL) 35560Sstevel@tonic-gate { 35570Sstevel@tonic-gate if (tTd(64, 10)) 35580Sstevel@tonic-gate sm_dprintf("NULL response\n"); 35590Sstevel@tonic-gate return; 35600Sstevel@tonic-gate } 35610Sstevel@tonic-gate 35620Sstevel@tonic-gate if (*response == '\0' || 35630Sstevel@tonic-gate strlen(response) + 1 != (size_t) rlen) 35640Sstevel@tonic-gate { 35650Sstevel@tonic-gate if (tTd(64, 10)) 35660Sstevel@tonic-gate sm_dprintf("didn't follow protocol (total len %d != rlen %d)\n", 35670Sstevel@tonic-gate (int) strlen(response), (int) (rlen - 1)); 35680Sstevel@tonic-gate return; 35690Sstevel@tonic-gate } 35700Sstevel@tonic-gate 35710Sstevel@tonic-gate if (tTd(64, 10)) 35720Sstevel@tonic-gate sm_dprintf("%s\n", response); 35730Sstevel@tonic-gate if (MilterLogLevel > 8) 35740Sstevel@tonic-gate sm_syslog(LOG_INFO, e->e_id, "Milter add: rcpt: %s", response); 35750Sstevel@tonic-gate olderrors = Errors; 35760Sstevel@tonic-gate (void) sendtolist(response, NULLADDR, &e->e_sendqueue, 0, e); 35770Sstevel@tonic-gate Errors = olderrors; 35780Sstevel@tonic-gate return; 35790Sstevel@tonic-gate } 3580*3544Sjbeck 35810Sstevel@tonic-gate /* 35820Sstevel@tonic-gate ** MILTER_DELRCPT -- Delete the supplied recipient from the message 35830Sstevel@tonic-gate ** 35840Sstevel@tonic-gate ** Parameters: 35850Sstevel@tonic-gate ** response -- encoded form of recipient address. 35860Sstevel@tonic-gate ** rlen -- length of response. 35870Sstevel@tonic-gate ** e -- current envelope. 35880Sstevel@tonic-gate ** 35890Sstevel@tonic-gate ** Returns: 35900Sstevel@tonic-gate ** none 35910Sstevel@tonic-gate */ 35920Sstevel@tonic-gate 35930Sstevel@tonic-gate static void 35940Sstevel@tonic-gate milter_delrcpt(response, rlen, e) 35950Sstevel@tonic-gate char *response; 35960Sstevel@tonic-gate ssize_t rlen; 35970Sstevel@tonic-gate ENVELOPE *e; 35980Sstevel@tonic-gate { 35990Sstevel@tonic-gate if (tTd(64, 10)) 36000Sstevel@tonic-gate sm_dprintf("milter_delrcpt: "); 36010Sstevel@tonic-gate 36020Sstevel@tonic-gate /* sanity checks */ 36030Sstevel@tonic-gate if (response == NULL) 36040Sstevel@tonic-gate { 36050Sstevel@tonic-gate if (tTd(64, 10)) 36060Sstevel@tonic-gate sm_dprintf("NULL response\n"); 36070Sstevel@tonic-gate return; 36080Sstevel@tonic-gate } 36090Sstevel@tonic-gate 36100Sstevel@tonic-gate if (*response == '\0' || 36110Sstevel@tonic-gate strlen(response) + 1 != (size_t) rlen) 36120Sstevel@tonic-gate { 36130Sstevel@tonic-gate if (tTd(64, 10)) 3614*3544Sjbeck sm_dprintf("didn't follow protocol (total len %d != rlen %d)\n", 3615*3544Sjbeck (int) strlen(response), (int) (rlen - 1)); 36160Sstevel@tonic-gate return; 36170Sstevel@tonic-gate } 36180Sstevel@tonic-gate 36190Sstevel@tonic-gate if (tTd(64, 10)) 36200Sstevel@tonic-gate sm_dprintf("%s\n", response); 36210Sstevel@tonic-gate if (MilterLogLevel > 8) 36220Sstevel@tonic-gate sm_syslog(LOG_INFO, e->e_id, "Milter delete: rcpt %s", 36230Sstevel@tonic-gate response); 36240Sstevel@tonic-gate (void) removefromlist(response, &e->e_sendqueue, e); 36250Sstevel@tonic-gate return; 36260Sstevel@tonic-gate } 3627*3544Sjbeck 36280Sstevel@tonic-gate /* 36290Sstevel@tonic-gate ** MILTER_REPLBODY -- Replace the current data file with new body 36300Sstevel@tonic-gate ** 36310Sstevel@tonic-gate ** Parameters: 36320Sstevel@tonic-gate ** response -- encoded form of new body. 36330Sstevel@tonic-gate ** rlen -- length of response. 36340Sstevel@tonic-gate ** newfilter -- if first time called by a new filter 36350Sstevel@tonic-gate ** e -- current envelope. 36360Sstevel@tonic-gate ** 36370Sstevel@tonic-gate ** Returns: 36380Sstevel@tonic-gate ** 0 upon success, -1 upon failure 36390Sstevel@tonic-gate */ 36400Sstevel@tonic-gate 36410Sstevel@tonic-gate static int 36420Sstevel@tonic-gate milter_replbody(response, rlen, newfilter, e) 36430Sstevel@tonic-gate char *response; 36440Sstevel@tonic-gate ssize_t rlen; 36450Sstevel@tonic-gate bool newfilter; 36460Sstevel@tonic-gate ENVELOPE *e; 36470Sstevel@tonic-gate { 36480Sstevel@tonic-gate static char prevchar; 36490Sstevel@tonic-gate int i; 36500Sstevel@tonic-gate 36510Sstevel@tonic-gate if (tTd(64, 10)) 36520Sstevel@tonic-gate sm_dprintf("milter_replbody\n"); 36530Sstevel@tonic-gate 36540Sstevel@tonic-gate /* If a new filter, reset previous character and truncate data file */ 36550Sstevel@tonic-gate if (newfilter) 36560Sstevel@tonic-gate { 36570Sstevel@tonic-gate off_t prevsize; 36580Sstevel@tonic-gate char dfname[MAXPATHLEN]; 36590Sstevel@tonic-gate 36600Sstevel@tonic-gate (void) sm_strlcpy(dfname, queuename(e, DATAFL_LETTER), 3661*3544Sjbeck sizeof(dfname)); 36620Sstevel@tonic-gate 36630Sstevel@tonic-gate /* Reset prevchar */ 36640Sstevel@tonic-gate prevchar = '\0'; 36650Sstevel@tonic-gate 36660Sstevel@tonic-gate /* Get the current data file information */ 36670Sstevel@tonic-gate prevsize = sm_io_getinfo(e->e_dfp, SM_IO_WHAT_SIZE, NULL); 36680Sstevel@tonic-gate if (prevsize < 0) 36690Sstevel@tonic-gate prevsize = 0; 36700Sstevel@tonic-gate 36710Sstevel@tonic-gate /* truncate current data file */ 36720Sstevel@tonic-gate if (sm_io_getinfo(e->e_dfp, SM_IO_WHAT_ISTYPE, BF_FILE_TYPE)) 36730Sstevel@tonic-gate { 36740Sstevel@tonic-gate if (sm_io_setinfo(e->e_dfp, SM_BF_TRUNCATE, NULL) < 0) 36750Sstevel@tonic-gate { 36760Sstevel@tonic-gate MILTER_DF_ERROR("milter_replbody: sm_io truncate %s: %s"); 36770Sstevel@tonic-gate return -1; 36780Sstevel@tonic-gate } 36790Sstevel@tonic-gate } 36800Sstevel@tonic-gate else 36810Sstevel@tonic-gate { 36820Sstevel@tonic-gate int err; 36830Sstevel@tonic-gate 36840Sstevel@tonic-gate err = sm_io_error(e->e_dfp); 36850Sstevel@tonic-gate (void) sm_io_flush(e->e_dfp, SM_TIME_DEFAULT); 36860Sstevel@tonic-gate 36870Sstevel@tonic-gate /* 36880Sstevel@tonic-gate ** Clear error if tried to fflush() 36890Sstevel@tonic-gate ** a read-only file pointer and 36900Sstevel@tonic-gate ** there wasn't a previous error. 36910Sstevel@tonic-gate */ 36920Sstevel@tonic-gate 36930Sstevel@tonic-gate if (err == 0) 36940Sstevel@tonic-gate sm_io_clearerr(e->e_dfp); 36950Sstevel@tonic-gate 36960Sstevel@tonic-gate /* errno is set implicitly by fseek() before return */ 36970Sstevel@tonic-gate err = sm_io_seek(e->e_dfp, SM_TIME_DEFAULT, 36980Sstevel@tonic-gate 0, SEEK_SET); 36990Sstevel@tonic-gate if (err < 0) 37000Sstevel@tonic-gate { 37010Sstevel@tonic-gate MILTER_DF_ERROR("milter_replbody: sm_io_seek %s: %s"); 37020Sstevel@tonic-gate return -1; 37030Sstevel@tonic-gate } 37040Sstevel@tonic-gate # if NOFTRUNCATE 37050Sstevel@tonic-gate /* XXX: Not much we can do except rewind it */ 37060Sstevel@tonic-gate errno = EINVAL; 37070Sstevel@tonic-gate MILTER_DF_ERROR("milter_replbody: ftruncate not available on this platform (%s:%s)"); 37080Sstevel@tonic-gate return -1; 37090Sstevel@tonic-gate # else /* NOFTRUNCATE */ 37100Sstevel@tonic-gate err = ftruncate(sm_io_getinfo(e->e_dfp, 37110Sstevel@tonic-gate SM_IO_WHAT_FD, NULL), 37120Sstevel@tonic-gate 0); 37130Sstevel@tonic-gate if (err < 0) 37140Sstevel@tonic-gate { 37150Sstevel@tonic-gate MILTER_DF_ERROR("milter_replbody: sm_io ftruncate %s: %s"); 37160Sstevel@tonic-gate return -1; 37170Sstevel@tonic-gate } 37180Sstevel@tonic-gate # endif /* NOFTRUNCATE */ 37190Sstevel@tonic-gate } 37200Sstevel@tonic-gate 37210Sstevel@tonic-gate if (prevsize > e->e_msgsize) 37220Sstevel@tonic-gate e->e_msgsize = 0; 37230Sstevel@tonic-gate else 37240Sstevel@tonic-gate e->e_msgsize -= prevsize; 37250Sstevel@tonic-gate } 37260Sstevel@tonic-gate 37270Sstevel@tonic-gate if (newfilter && MilterLogLevel > 8) 37280Sstevel@tonic-gate sm_syslog(LOG_INFO, e->e_id, "Milter message: body replaced"); 37290Sstevel@tonic-gate 37300Sstevel@tonic-gate if (response == NULL) 37310Sstevel@tonic-gate { 37320Sstevel@tonic-gate /* Flush the buffered '\r' */ 37330Sstevel@tonic-gate if (prevchar == '\r') 37340Sstevel@tonic-gate { 37350Sstevel@tonic-gate (void) sm_io_putc(e->e_dfp, SM_TIME_DEFAULT, prevchar); 37360Sstevel@tonic-gate e->e_msgsize++; 37370Sstevel@tonic-gate } 37380Sstevel@tonic-gate return 0; 37390Sstevel@tonic-gate } 37400Sstevel@tonic-gate 37410Sstevel@tonic-gate for (i = 0; i < rlen; i++) 37420Sstevel@tonic-gate { 37430Sstevel@tonic-gate /* Buffered char from last chunk */ 37440Sstevel@tonic-gate if (i == 0 && prevchar == '\r') 37450Sstevel@tonic-gate { 37460Sstevel@tonic-gate /* Not CRLF, output prevchar */ 37470Sstevel@tonic-gate if (response[i] != '\n') 37480Sstevel@tonic-gate { 37490Sstevel@tonic-gate (void) sm_io_putc(e->e_dfp, SM_TIME_DEFAULT, 37500Sstevel@tonic-gate prevchar); 37510Sstevel@tonic-gate e->e_msgsize++; 37520Sstevel@tonic-gate } 37530Sstevel@tonic-gate prevchar = '\0'; 37540Sstevel@tonic-gate } 37550Sstevel@tonic-gate 37560Sstevel@tonic-gate /* Turn CRLF into LF */ 37570Sstevel@tonic-gate if (response[i] == '\r') 37580Sstevel@tonic-gate { 37590Sstevel@tonic-gate /* check if at end of chunk */ 37600Sstevel@tonic-gate if (i + 1 < rlen) 37610Sstevel@tonic-gate { 37620Sstevel@tonic-gate /* If LF, strip CR */ 37630Sstevel@tonic-gate if (response[i + 1] == '\n') 37640Sstevel@tonic-gate i++; 37650Sstevel@tonic-gate } 37660Sstevel@tonic-gate else 37670Sstevel@tonic-gate { 37680Sstevel@tonic-gate /* check next chunk */ 37690Sstevel@tonic-gate prevchar = '\r'; 37700Sstevel@tonic-gate continue; 37710Sstevel@tonic-gate } 37720Sstevel@tonic-gate } 37730Sstevel@tonic-gate (void) sm_io_putc(e->e_dfp, SM_TIME_DEFAULT, response[i]); 37740Sstevel@tonic-gate e->e_msgsize++; 37750Sstevel@tonic-gate } 37760Sstevel@tonic-gate return 0; 37770Sstevel@tonic-gate } 37780Sstevel@tonic-gate 37790Sstevel@tonic-gate /* 37800Sstevel@tonic-gate ** MTA callouts 37810Sstevel@tonic-gate */ 37820Sstevel@tonic-gate 37830Sstevel@tonic-gate /* 37840Sstevel@tonic-gate ** MILTER_INIT -- open and negotiate with all of the filters 37850Sstevel@tonic-gate ** 37860Sstevel@tonic-gate ** Parameters: 37870Sstevel@tonic-gate ** e -- current envelope. 37880Sstevel@tonic-gate ** state -- return state from response. 37890Sstevel@tonic-gate ** 37900Sstevel@tonic-gate ** Returns: 37910Sstevel@tonic-gate ** true iff at least one filter is active 37920Sstevel@tonic-gate */ 37930Sstevel@tonic-gate 37940Sstevel@tonic-gate /* ARGSUSED */ 37950Sstevel@tonic-gate bool 37960Sstevel@tonic-gate milter_init(e, state) 37970Sstevel@tonic-gate ENVELOPE *e; 37980Sstevel@tonic-gate char *state; 37990Sstevel@tonic-gate { 38000Sstevel@tonic-gate int i; 38010Sstevel@tonic-gate 38020Sstevel@tonic-gate if (tTd(64, 10)) 38030Sstevel@tonic-gate sm_dprintf("milter_init\n"); 38040Sstevel@tonic-gate 38050Sstevel@tonic-gate *state = SMFIR_CONTINUE; 38060Sstevel@tonic-gate if (InputFilters[0] == NULL) 38070Sstevel@tonic-gate { 38080Sstevel@tonic-gate if (MilterLogLevel > 10) 38090Sstevel@tonic-gate sm_syslog(LOG_INFO, e->e_id, 38100Sstevel@tonic-gate "Milter: no active filter"); 38110Sstevel@tonic-gate return false; 38120Sstevel@tonic-gate } 38130Sstevel@tonic-gate 38140Sstevel@tonic-gate for (i = 0; InputFilters[i] != NULL; i++) 38150Sstevel@tonic-gate { 38160Sstevel@tonic-gate struct milter *m = InputFilters[i]; 38170Sstevel@tonic-gate 38180Sstevel@tonic-gate m->mf_sock = milter_open(m, false, e); 38190Sstevel@tonic-gate if (m->mf_state == SMFS_ERROR) 38200Sstevel@tonic-gate { 38210Sstevel@tonic-gate MILTER_CHECK_ERROR(true, continue); 38220Sstevel@tonic-gate break; 38230Sstevel@tonic-gate } 38240Sstevel@tonic-gate 38250Sstevel@tonic-gate if (m->mf_sock < 0 || 38260Sstevel@tonic-gate milter_negotiate(m, e) < 0 || 38270Sstevel@tonic-gate m->mf_state == SMFS_ERROR) 38280Sstevel@tonic-gate { 38290Sstevel@tonic-gate if (tTd(64, 5)) 38300Sstevel@tonic-gate sm_dprintf("milter_init(%s): failed to %s\n", 38310Sstevel@tonic-gate m->mf_name, 38320Sstevel@tonic-gate m->mf_sock < 0 ? "open" : 38330Sstevel@tonic-gate "negotiate"); 38340Sstevel@tonic-gate if (MilterLogLevel > 0) 38350Sstevel@tonic-gate sm_syslog(LOG_ERR, e->e_id, 38360Sstevel@tonic-gate "Milter (%s): init failed to %s", 38370Sstevel@tonic-gate m->mf_name, 38380Sstevel@tonic-gate m->mf_sock < 0 ? "open" : 38390Sstevel@tonic-gate "negotiate"); 38400Sstevel@tonic-gate 38410Sstevel@tonic-gate /* if negotation failure, close socket */ 38420Sstevel@tonic-gate milter_error(m, e); 38430Sstevel@tonic-gate MILTER_CHECK_ERROR(true, continue); 38440Sstevel@tonic-gate continue; 38450Sstevel@tonic-gate } 38460Sstevel@tonic-gate if (MilterLogLevel > 9) 38470Sstevel@tonic-gate sm_syslog(LOG_INFO, e->e_id, 38480Sstevel@tonic-gate "Milter (%s): init success to %s", 38490Sstevel@tonic-gate m->mf_name, 38500Sstevel@tonic-gate m->mf_sock < 0 ? "open" : "negotiate"); 38510Sstevel@tonic-gate } 38520Sstevel@tonic-gate 38530Sstevel@tonic-gate /* 38540Sstevel@tonic-gate ** If something temp/perm failed with one of the filters, 38550Sstevel@tonic-gate ** we won't be using any of them, so clear any existing 38560Sstevel@tonic-gate ** connections. 38570Sstevel@tonic-gate */ 38580Sstevel@tonic-gate 38590Sstevel@tonic-gate if (*state != SMFIR_CONTINUE) 38600Sstevel@tonic-gate milter_quit(e); 38610Sstevel@tonic-gate 38620Sstevel@tonic-gate return true; 38630Sstevel@tonic-gate } 3864*3544Sjbeck 38650Sstevel@tonic-gate /* 38660Sstevel@tonic-gate ** MILTER_CONNECT -- send connection info to milter filters 38670Sstevel@tonic-gate ** 38680Sstevel@tonic-gate ** Parameters: 38690Sstevel@tonic-gate ** hostname -- hostname of remote machine. 38700Sstevel@tonic-gate ** addr -- address of remote machine. 38710Sstevel@tonic-gate ** e -- current envelope. 38720Sstevel@tonic-gate ** state -- return state from response. 38730Sstevel@tonic-gate ** 38740Sstevel@tonic-gate ** Returns: 38750Sstevel@tonic-gate ** response string (may be NULL) 38760Sstevel@tonic-gate */ 38770Sstevel@tonic-gate 38780Sstevel@tonic-gate char * 38790Sstevel@tonic-gate milter_connect(hostname, addr, e, state) 38800Sstevel@tonic-gate char *hostname; 38810Sstevel@tonic-gate SOCKADDR addr; 38820Sstevel@tonic-gate ENVELOPE *e; 38830Sstevel@tonic-gate char *state; 38840Sstevel@tonic-gate { 38850Sstevel@tonic-gate char family; 38860Sstevel@tonic-gate unsigned short port; 38870Sstevel@tonic-gate char *buf, *bp; 38880Sstevel@tonic-gate char *response; 38890Sstevel@tonic-gate char *sockinfo = NULL; 38900Sstevel@tonic-gate ssize_t s; 38910Sstevel@tonic-gate # if NETINET6 38920Sstevel@tonic-gate char buf6[INET6_ADDRSTRLEN]; 38930Sstevel@tonic-gate # endif /* NETINET6 */ 38940Sstevel@tonic-gate 38950Sstevel@tonic-gate if (tTd(64, 10)) 38960Sstevel@tonic-gate sm_dprintf("milter_connect(%s)\n", hostname); 38970Sstevel@tonic-gate if (MilterLogLevel > 9) 38980Sstevel@tonic-gate sm_syslog(LOG_INFO, e->e_id, "Milter: connect to filters"); 38990Sstevel@tonic-gate 39000Sstevel@tonic-gate /* gather data */ 39010Sstevel@tonic-gate switch (addr.sa.sa_family) 39020Sstevel@tonic-gate { 39030Sstevel@tonic-gate # if NETUNIX 39040Sstevel@tonic-gate case AF_UNIX: 39050Sstevel@tonic-gate family = SMFIA_UNIX; 39060Sstevel@tonic-gate port = htons(0); 39070Sstevel@tonic-gate sockinfo = addr.sunix.sun_path; 39080Sstevel@tonic-gate break; 39090Sstevel@tonic-gate # endif /* NETUNIX */ 39100Sstevel@tonic-gate 39110Sstevel@tonic-gate # if NETINET 39120Sstevel@tonic-gate case AF_INET: 39130Sstevel@tonic-gate family = SMFIA_INET; 39140Sstevel@tonic-gate port = addr.sin.sin_port; 39150Sstevel@tonic-gate sockinfo = (char *) inet_ntoa(addr.sin.sin_addr); 39160Sstevel@tonic-gate break; 39170Sstevel@tonic-gate # endif /* NETINET */ 39180Sstevel@tonic-gate 39190Sstevel@tonic-gate # if NETINET6 39200Sstevel@tonic-gate case AF_INET6: 39210Sstevel@tonic-gate if (IN6_IS_ADDR_V4MAPPED(&addr.sin6.sin6_addr)) 39220Sstevel@tonic-gate family = SMFIA_INET; 39230Sstevel@tonic-gate else 39240Sstevel@tonic-gate family = SMFIA_INET6; 39250Sstevel@tonic-gate port = addr.sin6.sin6_port; 39260Sstevel@tonic-gate sockinfo = anynet_ntop(&addr.sin6.sin6_addr, buf6, 3927*3544Sjbeck sizeof(buf6)); 39280Sstevel@tonic-gate if (sockinfo == NULL) 39290Sstevel@tonic-gate sockinfo = ""; 39300Sstevel@tonic-gate break; 39310Sstevel@tonic-gate # endif /* NETINET6 */ 39320Sstevel@tonic-gate 39330Sstevel@tonic-gate default: 39340Sstevel@tonic-gate family = SMFIA_UNKNOWN; 39350Sstevel@tonic-gate break; 39360Sstevel@tonic-gate } 39370Sstevel@tonic-gate 39380Sstevel@tonic-gate s = strlen(hostname) + 1 + sizeof(family); 39390Sstevel@tonic-gate if (family != SMFIA_UNKNOWN) 39400Sstevel@tonic-gate s += sizeof(port) + strlen(sockinfo) + 1; 39410Sstevel@tonic-gate 39420Sstevel@tonic-gate buf = (char *) xalloc(s); 39430Sstevel@tonic-gate bp = buf; 39440Sstevel@tonic-gate 39450Sstevel@tonic-gate /* put together data */ 39460Sstevel@tonic-gate (void) memcpy(bp, hostname, strlen(hostname)); 39470Sstevel@tonic-gate bp += strlen(hostname); 39480Sstevel@tonic-gate *bp++ = '\0'; 3949*3544Sjbeck (void) memcpy(bp, &family, sizeof(family)); 3950*3544Sjbeck bp += sizeof(family); 39510Sstevel@tonic-gate if (family != SMFIA_UNKNOWN) 39520Sstevel@tonic-gate { 3953*3544Sjbeck (void) memcpy(bp, &port, sizeof(port)); 3954*3544Sjbeck bp += sizeof(port); 39550Sstevel@tonic-gate 39560Sstevel@tonic-gate /* include trailing '\0' */ 39570Sstevel@tonic-gate (void) memcpy(bp, sockinfo, strlen(sockinfo) + 1); 39580Sstevel@tonic-gate } 39590Sstevel@tonic-gate 3960*3544Sjbeck response = milter_command(SMFIC_CONNECT, buf, s, MilterConnectMacros, 3961*3544Sjbeck e, state, "connect", false); 39620Sstevel@tonic-gate sm_free(buf); /* XXX */ 39630Sstevel@tonic-gate 39640Sstevel@tonic-gate /* 39650Sstevel@tonic-gate ** If this message connection is done for, 39660Sstevel@tonic-gate ** close the filters. 39670Sstevel@tonic-gate */ 39680Sstevel@tonic-gate 39690Sstevel@tonic-gate if (*state != SMFIR_CONTINUE) 39700Sstevel@tonic-gate { 39710Sstevel@tonic-gate if (MilterLogLevel > 9) 39720Sstevel@tonic-gate sm_syslog(LOG_INFO, e->e_id, "Milter: connect, ending"); 39730Sstevel@tonic-gate milter_quit(e); 39740Sstevel@tonic-gate } 39750Sstevel@tonic-gate else 39760Sstevel@tonic-gate milter_per_connection_check(e); 39770Sstevel@tonic-gate 39780Sstevel@tonic-gate /* 39790Sstevel@tonic-gate ** SMFIR_REPLYCODE can't work with connect due to 39800Sstevel@tonic-gate ** the requirements of SMTP. Therefore, ignore the 39810Sstevel@tonic-gate ** reply code text but keep the state it would reflect. 39820Sstevel@tonic-gate */ 39830Sstevel@tonic-gate 39840Sstevel@tonic-gate if (*state == SMFIR_REPLYCODE) 39850Sstevel@tonic-gate { 39860Sstevel@tonic-gate if (response != NULL && 39870Sstevel@tonic-gate *response == '4') 39880Sstevel@tonic-gate { 39890Sstevel@tonic-gate if (strncmp(response, "421 ", 4) == 0) 39900Sstevel@tonic-gate *state = SMFIR_SHUTDOWN; 39910Sstevel@tonic-gate else 39920Sstevel@tonic-gate *state = SMFIR_TEMPFAIL; 39930Sstevel@tonic-gate } 39940Sstevel@tonic-gate else 39950Sstevel@tonic-gate *state = SMFIR_REJECT; 39960Sstevel@tonic-gate if (response != NULL) 39970Sstevel@tonic-gate { 39980Sstevel@tonic-gate sm_free(response); /* XXX */ 39990Sstevel@tonic-gate response = NULL; 40000Sstevel@tonic-gate } 40010Sstevel@tonic-gate } 40020Sstevel@tonic-gate return response; 40030Sstevel@tonic-gate } 4004*3544Sjbeck 40050Sstevel@tonic-gate /* 40060Sstevel@tonic-gate ** MILTER_HELO -- send SMTP HELO/EHLO command info to milter filters 40070Sstevel@tonic-gate ** 40080Sstevel@tonic-gate ** Parameters: 40090Sstevel@tonic-gate ** helo -- argument to SMTP HELO/EHLO command. 40100Sstevel@tonic-gate ** e -- current envelope. 40110Sstevel@tonic-gate ** state -- return state from response. 40120Sstevel@tonic-gate ** 40130Sstevel@tonic-gate ** Returns: 40140Sstevel@tonic-gate ** response string (may be NULL) 40150Sstevel@tonic-gate */ 40160Sstevel@tonic-gate 40170Sstevel@tonic-gate char * 40180Sstevel@tonic-gate milter_helo(helo, e, state) 40190Sstevel@tonic-gate char *helo; 40200Sstevel@tonic-gate ENVELOPE *e; 40210Sstevel@tonic-gate char *state; 40220Sstevel@tonic-gate { 40230Sstevel@tonic-gate int i; 40240Sstevel@tonic-gate char *response; 40250Sstevel@tonic-gate 40260Sstevel@tonic-gate if (tTd(64, 10)) 40270Sstevel@tonic-gate sm_dprintf("milter_helo(%s)\n", helo); 40280Sstevel@tonic-gate 40290Sstevel@tonic-gate /* HELO/EHLO can come at any point */ 40300Sstevel@tonic-gate for (i = 0; InputFilters[i] != NULL; i++) 40310Sstevel@tonic-gate { 40320Sstevel@tonic-gate struct milter *m = InputFilters[i]; 40330Sstevel@tonic-gate 40340Sstevel@tonic-gate switch (m->mf_state) 40350Sstevel@tonic-gate { 40360Sstevel@tonic-gate case SMFS_INMSG: 40370Sstevel@tonic-gate /* abort in message filters */ 40380Sstevel@tonic-gate milter_abort_filter(m, e); 40390Sstevel@tonic-gate /* FALLTHROUGH */ 40400Sstevel@tonic-gate 40410Sstevel@tonic-gate case SMFS_DONE: 40420Sstevel@tonic-gate /* reset done filters */ 40430Sstevel@tonic-gate m->mf_state = SMFS_OPEN; 40440Sstevel@tonic-gate break; 40450Sstevel@tonic-gate } 40460Sstevel@tonic-gate } 40470Sstevel@tonic-gate 40480Sstevel@tonic-gate response = milter_command(SMFIC_HELO, helo, strlen(helo) + 1, 4049*3544Sjbeck MilterHeloMacros, e, state, "helo", false); 40500Sstevel@tonic-gate milter_per_connection_check(e); 40510Sstevel@tonic-gate return response; 40520Sstevel@tonic-gate } 4053*3544Sjbeck 40540Sstevel@tonic-gate /* 40550Sstevel@tonic-gate ** MILTER_ENVFROM -- send SMTP MAIL command info to milter filters 40560Sstevel@tonic-gate ** 40570Sstevel@tonic-gate ** Parameters: 40580Sstevel@tonic-gate ** args -- SMTP MAIL command args (args[0] == sender). 40590Sstevel@tonic-gate ** e -- current envelope. 40600Sstevel@tonic-gate ** state -- return state from response. 40610Sstevel@tonic-gate ** 40620Sstevel@tonic-gate ** Returns: 40630Sstevel@tonic-gate ** response string (may be NULL) 40640Sstevel@tonic-gate */ 40650Sstevel@tonic-gate 40660Sstevel@tonic-gate char * 40670Sstevel@tonic-gate milter_envfrom(args, e, state) 40680Sstevel@tonic-gate char **args; 40690Sstevel@tonic-gate ENVELOPE *e; 40700Sstevel@tonic-gate char *state; 40710Sstevel@tonic-gate { 40720Sstevel@tonic-gate int i; 40730Sstevel@tonic-gate char *buf, *bp; 40740Sstevel@tonic-gate char *response; 40750Sstevel@tonic-gate ssize_t s; 40760Sstevel@tonic-gate 40770Sstevel@tonic-gate if (tTd(64, 10)) 40780Sstevel@tonic-gate { 40790Sstevel@tonic-gate sm_dprintf("milter_envfrom:"); 40800Sstevel@tonic-gate for (i = 0; args[i] != NULL; i++) 40810Sstevel@tonic-gate sm_dprintf(" %s", args[i]); 40820Sstevel@tonic-gate sm_dprintf("\n"); 40830Sstevel@tonic-gate } 40840Sstevel@tonic-gate 40850Sstevel@tonic-gate /* sanity check */ 40860Sstevel@tonic-gate if (args[0] == NULL) 40870Sstevel@tonic-gate { 40880Sstevel@tonic-gate *state = SMFIR_REJECT; 40890Sstevel@tonic-gate if (MilterLogLevel > 10) 40900Sstevel@tonic-gate sm_syslog(LOG_INFO, e->e_id, 40910Sstevel@tonic-gate "Milter: reject, no sender"); 40920Sstevel@tonic-gate return NULL; 40930Sstevel@tonic-gate } 40940Sstevel@tonic-gate 40950Sstevel@tonic-gate /* new message, so ... */ 40960Sstevel@tonic-gate for (i = 0; InputFilters[i] != NULL; i++) 40970Sstevel@tonic-gate { 40980Sstevel@tonic-gate struct milter *m = InputFilters[i]; 40990Sstevel@tonic-gate 41000Sstevel@tonic-gate switch (m->mf_state) 41010Sstevel@tonic-gate { 41020Sstevel@tonic-gate case SMFS_INMSG: 41030Sstevel@tonic-gate /* abort in message filters */ 41040Sstevel@tonic-gate milter_abort_filter(m, e); 41050Sstevel@tonic-gate /* FALLTHROUGH */ 41060Sstevel@tonic-gate 41070Sstevel@tonic-gate case SMFS_DONE: 41080Sstevel@tonic-gate /* reset done filters */ 41090Sstevel@tonic-gate m->mf_state = SMFS_OPEN; 41100Sstevel@tonic-gate break; 41110Sstevel@tonic-gate } 41120Sstevel@tonic-gate } 41130Sstevel@tonic-gate 41140Sstevel@tonic-gate /* put together data */ 41150Sstevel@tonic-gate s = 0; 41160Sstevel@tonic-gate for (i = 0; args[i] != NULL; i++) 41170Sstevel@tonic-gate s += strlen(args[i]) + 1; 41180Sstevel@tonic-gate 41190Sstevel@tonic-gate if (s < 0) 41200Sstevel@tonic-gate { 41210Sstevel@tonic-gate *state = SMFIR_TEMPFAIL; 41220Sstevel@tonic-gate return NULL; 41230Sstevel@tonic-gate } 41240Sstevel@tonic-gate 41250Sstevel@tonic-gate buf = (char *) xalloc(s); 41260Sstevel@tonic-gate bp = buf; 41270Sstevel@tonic-gate for (i = 0; args[i] != NULL; i++) 41280Sstevel@tonic-gate { 41290Sstevel@tonic-gate (void) sm_strlcpy(bp, args[i], s - (bp - buf)); 41300Sstevel@tonic-gate bp += strlen(bp) + 1; 41310Sstevel@tonic-gate } 41320Sstevel@tonic-gate 41330Sstevel@tonic-gate if (MilterLogLevel > 14) 4134*3544Sjbeck sm_syslog(LOG_INFO, e->e_id, "Milter: sender: %s", buf); 41350Sstevel@tonic-gate 41360Sstevel@tonic-gate /* send it over */ 4137*3544Sjbeck response = milter_command(SMFIC_MAIL, buf, s, MilterEnvFromMacros, 4138*3544Sjbeck e, state, "mail", false); 41390Sstevel@tonic-gate sm_free(buf); /* XXX */ 41400Sstevel@tonic-gate 41410Sstevel@tonic-gate /* 41420Sstevel@tonic-gate ** If filter rejects/discards a per message command, 41430Sstevel@tonic-gate ** abort the other filters since we are done with the 41440Sstevel@tonic-gate ** current message. 41450Sstevel@tonic-gate */ 41460Sstevel@tonic-gate 41470Sstevel@tonic-gate MILTER_CHECK_DONE_MSG(); 41480Sstevel@tonic-gate if (MilterLogLevel > 10 && *state == SMFIR_REJECT) 4149*3544Sjbeck sm_syslog(LOG_INFO, e->e_id, "Milter: reject, sender"); 41500Sstevel@tonic-gate return response; 41510Sstevel@tonic-gate } 41520Sstevel@tonic-gate 41530Sstevel@tonic-gate /* 41540Sstevel@tonic-gate ** MILTER_ENVRCPT -- send SMTP RCPT command info to milter filters 41550Sstevel@tonic-gate ** 41560Sstevel@tonic-gate ** Parameters: 41570Sstevel@tonic-gate ** args -- SMTP MAIL command args (args[0] == recipient). 41580Sstevel@tonic-gate ** e -- current envelope. 41590Sstevel@tonic-gate ** state -- return state from response. 4160*3544Sjbeck ** rcpt_error -- does RCPT have an error? 41610Sstevel@tonic-gate ** 41620Sstevel@tonic-gate ** Returns: 41630Sstevel@tonic-gate ** response string (may be NULL) 41640Sstevel@tonic-gate */ 41650Sstevel@tonic-gate 41660Sstevel@tonic-gate char * 4167*3544Sjbeck milter_envrcpt(args, e, state, rcpt_error) 41680Sstevel@tonic-gate char **args; 41690Sstevel@tonic-gate ENVELOPE *e; 41700Sstevel@tonic-gate char *state; 4171*3544Sjbeck bool rcpt_error; 41720Sstevel@tonic-gate { 41730Sstevel@tonic-gate int i; 41740Sstevel@tonic-gate char *buf, *bp; 41750Sstevel@tonic-gate char *response; 41760Sstevel@tonic-gate ssize_t s; 41770Sstevel@tonic-gate 41780Sstevel@tonic-gate if (tTd(64, 10)) 41790Sstevel@tonic-gate { 41800Sstevel@tonic-gate sm_dprintf("milter_envrcpt:"); 41810Sstevel@tonic-gate for (i = 0; args[i] != NULL; i++) 41820Sstevel@tonic-gate sm_dprintf(" %s", args[i]); 41830Sstevel@tonic-gate sm_dprintf("\n"); 41840Sstevel@tonic-gate } 41850Sstevel@tonic-gate 41860Sstevel@tonic-gate /* sanity check */ 41870Sstevel@tonic-gate if (args[0] == NULL) 41880Sstevel@tonic-gate { 41890Sstevel@tonic-gate *state = SMFIR_REJECT; 41900Sstevel@tonic-gate if (MilterLogLevel > 10) 41910Sstevel@tonic-gate sm_syslog(LOG_INFO, e->e_id, "Milter: reject, no rcpt"); 41920Sstevel@tonic-gate return NULL; 41930Sstevel@tonic-gate } 41940Sstevel@tonic-gate 41950Sstevel@tonic-gate /* put together data */ 41960Sstevel@tonic-gate s = 0; 41970Sstevel@tonic-gate for (i = 0; args[i] != NULL; i++) 41980Sstevel@tonic-gate s += strlen(args[i]) + 1; 41990Sstevel@tonic-gate 42000Sstevel@tonic-gate if (s < 0) 42010Sstevel@tonic-gate { 42020Sstevel@tonic-gate *state = SMFIR_TEMPFAIL; 42030Sstevel@tonic-gate return NULL; 42040Sstevel@tonic-gate } 42050Sstevel@tonic-gate 42060Sstevel@tonic-gate buf = (char *) xalloc(s); 42070Sstevel@tonic-gate bp = buf; 42080Sstevel@tonic-gate for (i = 0; args[i] != NULL; i++) 42090Sstevel@tonic-gate { 42100Sstevel@tonic-gate (void) sm_strlcpy(bp, args[i], s - (bp - buf)); 42110Sstevel@tonic-gate bp += strlen(bp) + 1; 42120Sstevel@tonic-gate } 42130Sstevel@tonic-gate 42140Sstevel@tonic-gate if (MilterLogLevel > 14) 42150Sstevel@tonic-gate sm_syslog(LOG_INFO, e->e_id, "Milter: rcpts: %s", buf); 42160Sstevel@tonic-gate 42170Sstevel@tonic-gate /* send it over */ 4218*3544Sjbeck response = milter_command(SMFIC_RCPT, buf, s, MilterEnvRcptMacros, 4219*3544Sjbeck e, state, "rcpt", rcpt_error); 42200Sstevel@tonic-gate sm_free(buf); /* XXX */ 42210Sstevel@tonic-gate return response; 42220Sstevel@tonic-gate } 42230Sstevel@tonic-gate 42240Sstevel@tonic-gate /* 42250Sstevel@tonic-gate ** MILTER_DATA_CMD -- send SMTP DATA command info to milter filters 42260Sstevel@tonic-gate ** 42270Sstevel@tonic-gate ** Parameters: 42280Sstevel@tonic-gate ** e -- current envelope. 42290Sstevel@tonic-gate ** state -- return state from response. 42300Sstevel@tonic-gate ** 42310Sstevel@tonic-gate ** Returns: 42320Sstevel@tonic-gate ** response string (may be NULL) 42330Sstevel@tonic-gate */ 42340Sstevel@tonic-gate 42350Sstevel@tonic-gate char * 42360Sstevel@tonic-gate milter_data_cmd(e, state) 42370Sstevel@tonic-gate ENVELOPE *e; 42380Sstevel@tonic-gate char *state; 42390Sstevel@tonic-gate { 42400Sstevel@tonic-gate if (tTd(64, 10)) 42410Sstevel@tonic-gate sm_dprintf("milter_data_cmd\n"); 42420Sstevel@tonic-gate 42430Sstevel@tonic-gate /* send it over */ 4244*3544Sjbeck return milter_command(SMFIC_DATA, NULL, 0, MilterDataMacros, e, state, 4245*3544Sjbeck "data", false); 42460Sstevel@tonic-gate } 42470Sstevel@tonic-gate 42480Sstevel@tonic-gate /* 42490Sstevel@tonic-gate ** MILTER_DATA -- send message headers/body and gather final message results 42500Sstevel@tonic-gate ** 42510Sstevel@tonic-gate ** Parameters: 42520Sstevel@tonic-gate ** e -- current envelope. 42530Sstevel@tonic-gate ** state -- return state from response. 42540Sstevel@tonic-gate ** 42550Sstevel@tonic-gate ** Returns: 42560Sstevel@tonic-gate ** response string (may be NULL) 42570Sstevel@tonic-gate ** 42580Sstevel@tonic-gate ** Side effects: 42590Sstevel@tonic-gate ** - Uses e->e_dfp for access to the body 42600Sstevel@tonic-gate ** - Can call the various milter action routines to 42610Sstevel@tonic-gate ** modify the envelope or message. 42620Sstevel@tonic-gate */ 42630Sstevel@tonic-gate 42640Sstevel@tonic-gate # define MILTER_CHECK_RESULTS() \ 42650Sstevel@tonic-gate if (*state == SMFIR_ACCEPT || \ 42660Sstevel@tonic-gate m->mf_state == SMFS_DONE || \ 42670Sstevel@tonic-gate m->mf_state == SMFS_ERROR) \ 42680Sstevel@tonic-gate { \ 42690Sstevel@tonic-gate if (m->mf_state != SMFS_ERROR) \ 42700Sstevel@tonic-gate m->mf_state = SMFS_DONE; \ 42710Sstevel@tonic-gate continue; /* to next filter */ \ 42720Sstevel@tonic-gate } \ 42730Sstevel@tonic-gate if (*state != SMFIR_CONTINUE) \ 42740Sstevel@tonic-gate { \ 42750Sstevel@tonic-gate m->mf_state = SMFS_DONE; \ 42760Sstevel@tonic-gate goto finishup; \ 42770Sstevel@tonic-gate } 42780Sstevel@tonic-gate 42790Sstevel@tonic-gate char * 42800Sstevel@tonic-gate milter_data(e, state) 42810Sstevel@tonic-gate ENVELOPE *e; 42820Sstevel@tonic-gate char *state; 42830Sstevel@tonic-gate { 42840Sstevel@tonic-gate bool replbody = false; /* milter_replbody() called? */ 42850Sstevel@tonic-gate bool replfailed = false; /* milter_replbody() failed? */ 42860Sstevel@tonic-gate bool rewind = false; /* rewind data file? */ 42870Sstevel@tonic-gate bool dfopen = false; /* data file open for writing? */ 42880Sstevel@tonic-gate bool newfilter; /* reset on each new filter */ 42890Sstevel@tonic-gate char rcmd; 42900Sstevel@tonic-gate int i; 42910Sstevel@tonic-gate int save_errno; 42920Sstevel@tonic-gate char *response = NULL; 42930Sstevel@tonic-gate time_t eomsent; 42940Sstevel@tonic-gate ssize_t rlen; 42950Sstevel@tonic-gate 42960Sstevel@tonic-gate if (tTd(64, 10)) 42970Sstevel@tonic-gate sm_dprintf("milter_data\n"); 42980Sstevel@tonic-gate 42990Sstevel@tonic-gate *state = SMFIR_CONTINUE; 43000Sstevel@tonic-gate 43010Sstevel@tonic-gate /* 43020Sstevel@tonic-gate ** XXX: Should actually send body chunks to each filter 43030Sstevel@tonic-gate ** a chunk at a time instead of sending the whole body to 43040Sstevel@tonic-gate ** each filter in turn. However, only if the filters don't 43050Sstevel@tonic-gate ** change the body. 43060Sstevel@tonic-gate */ 43070Sstevel@tonic-gate 43080Sstevel@tonic-gate for (i = 0; InputFilters[i] != NULL; i++) 43090Sstevel@tonic-gate { 43100Sstevel@tonic-gate struct milter *m = InputFilters[i]; 43110Sstevel@tonic-gate 43120Sstevel@tonic-gate if (*state != SMFIR_CONTINUE && 43130Sstevel@tonic-gate *state != SMFIR_ACCEPT) 43140Sstevel@tonic-gate { 43150Sstevel@tonic-gate /* 43160Sstevel@tonic-gate ** A previous filter has dealt with the message, 43170Sstevel@tonic-gate ** safe to stop processing the filters. 43180Sstevel@tonic-gate */ 43190Sstevel@tonic-gate 43200Sstevel@tonic-gate break; 43210Sstevel@tonic-gate } 43220Sstevel@tonic-gate 43230Sstevel@tonic-gate /* Now reset state for later evaluation */ 43240Sstevel@tonic-gate *state = SMFIR_CONTINUE; 43250Sstevel@tonic-gate newfilter = true; 43260Sstevel@tonic-gate 43270Sstevel@tonic-gate /* previous problem? */ 43280Sstevel@tonic-gate if (m->mf_state == SMFS_ERROR) 43290Sstevel@tonic-gate { 43300Sstevel@tonic-gate MILTER_CHECK_ERROR(false, continue); 43310Sstevel@tonic-gate break; 43320Sstevel@tonic-gate } 43330Sstevel@tonic-gate 43340Sstevel@tonic-gate /* sanity checks */ 43350Sstevel@tonic-gate if (m->mf_sock < 0 || 43360Sstevel@tonic-gate (m->mf_state != SMFS_OPEN && m->mf_state != SMFS_INMSG)) 43370Sstevel@tonic-gate continue; 43380Sstevel@tonic-gate 43390Sstevel@tonic-gate m->mf_state = SMFS_INMSG; 43400Sstevel@tonic-gate 43410Sstevel@tonic-gate /* check if filter wants the headers */ 43420Sstevel@tonic-gate if (!bitset(SMFIP_NOHDRS, m->mf_pflags)) 43430Sstevel@tonic-gate { 43440Sstevel@tonic-gate response = milter_headers(m, e, state); 43450Sstevel@tonic-gate MILTER_CHECK_RESULTS(); 43460Sstevel@tonic-gate } 43470Sstevel@tonic-gate 43480Sstevel@tonic-gate /* check if filter wants EOH */ 43490Sstevel@tonic-gate if (!bitset(SMFIP_NOEOH, m->mf_pflags)) 43500Sstevel@tonic-gate { 43510Sstevel@tonic-gate if (tTd(64, 10)) 43520Sstevel@tonic-gate sm_dprintf("milter_data: eoh\n"); 43530Sstevel@tonic-gate 4354*3544Sjbeck if (MilterEOHMacros[0] != NULL) 4355*3544Sjbeck { 4356*3544Sjbeck milter_send_macros(m, MilterEOHMacros, 4357*3544Sjbeck SMFIC_EOH, e); 4358*3544Sjbeck MILTER_CHECK_RESULTS(); 4359*3544Sjbeck } 4360*3544Sjbeck 43610Sstevel@tonic-gate /* send it over */ 43620Sstevel@tonic-gate response = milter_send_command(m, SMFIC_EOH, NULL, 0, 4363*3544Sjbeck e, state, "eoh"); 43640Sstevel@tonic-gate MILTER_CHECK_RESULTS(); 43650Sstevel@tonic-gate } 43660Sstevel@tonic-gate 43670Sstevel@tonic-gate /* check if filter wants the body */ 43680Sstevel@tonic-gate if (!bitset(SMFIP_NOBODY, m->mf_pflags) && 43690Sstevel@tonic-gate e->e_dfp != NULL) 43700Sstevel@tonic-gate { 43710Sstevel@tonic-gate rewind = true; 43720Sstevel@tonic-gate response = milter_body(m, e, state); 43730Sstevel@tonic-gate MILTER_CHECK_RESULTS(); 43740Sstevel@tonic-gate } 43750Sstevel@tonic-gate 43760Sstevel@tonic-gate if (MilterEOMMacros[0] != NULL) 43770Sstevel@tonic-gate { 43780Sstevel@tonic-gate milter_send_macros(m, MilterEOMMacros, 43790Sstevel@tonic-gate SMFIC_BODYEOB, e); 43800Sstevel@tonic-gate MILTER_CHECK_RESULTS(); 43810Sstevel@tonic-gate } 43820Sstevel@tonic-gate 43830Sstevel@tonic-gate /* send the final body chunk */ 43840Sstevel@tonic-gate (void) milter_write(m, SMFIC_BODYEOB, NULL, 0, 4385*3544Sjbeck m->mf_timeout[SMFTO_WRITE], e, "eom"); 43860Sstevel@tonic-gate 43870Sstevel@tonic-gate /* Get time EOM sent for timeout */ 43880Sstevel@tonic-gate eomsent = curtime(); 43890Sstevel@tonic-gate 43900Sstevel@tonic-gate /* deal with the possibility of multiple responses */ 43910Sstevel@tonic-gate while (*state == SMFIR_CONTINUE) 43920Sstevel@tonic-gate { 43930Sstevel@tonic-gate /* Check total timeout from EOM to final ACK/NAK */ 43940Sstevel@tonic-gate if (m->mf_timeout[SMFTO_EOM] > 0 && 43950Sstevel@tonic-gate curtime() - eomsent >= m->mf_timeout[SMFTO_EOM]) 43960Sstevel@tonic-gate { 43970Sstevel@tonic-gate if (tTd(64, 5)) 43980Sstevel@tonic-gate sm_dprintf("milter_data(%s): EOM ACK/NAK timeout\n", 43990Sstevel@tonic-gate m->mf_name); 44000Sstevel@tonic-gate if (MilterLogLevel > 0) 44010Sstevel@tonic-gate sm_syslog(LOG_ERR, e->e_id, 44020Sstevel@tonic-gate "milter_data(%s): EOM ACK/NAK timeout", 44030Sstevel@tonic-gate m->mf_name); 44040Sstevel@tonic-gate milter_error(m, e); 44050Sstevel@tonic-gate MILTER_CHECK_ERROR(false, break); 44060Sstevel@tonic-gate break; 44070Sstevel@tonic-gate } 44080Sstevel@tonic-gate 44090Sstevel@tonic-gate response = milter_read(m, &rcmd, &rlen, 4410*3544Sjbeck m->mf_timeout[SMFTO_READ], e, 4411*3544Sjbeck "body"); 44120Sstevel@tonic-gate if (m->mf_state == SMFS_ERROR) 44130Sstevel@tonic-gate break; 44140Sstevel@tonic-gate 44150Sstevel@tonic-gate if (tTd(64, 10)) 44160Sstevel@tonic-gate sm_dprintf("milter_data(%s): state %c\n", 44170Sstevel@tonic-gate m->mf_name, (char) rcmd); 44180Sstevel@tonic-gate 44190Sstevel@tonic-gate switch (rcmd) 44200Sstevel@tonic-gate { 44210Sstevel@tonic-gate case SMFIR_REPLYCODE: 44220Sstevel@tonic-gate MILTER_CHECK_REPLYCODE("554 5.7.1 Command rejected"); 44230Sstevel@tonic-gate if (MilterLogLevel > 12) 44240Sstevel@tonic-gate sm_syslog(LOG_INFO, e->e_id, "milter=%s, reject=%s", 44250Sstevel@tonic-gate m->mf_name, response); 44260Sstevel@tonic-gate *state = rcmd; 44270Sstevel@tonic-gate m->mf_state = SMFS_DONE; 44280Sstevel@tonic-gate break; 44290Sstevel@tonic-gate 44300Sstevel@tonic-gate case SMFIR_REJECT: /* log msg at end of function */ 44310Sstevel@tonic-gate if (MilterLogLevel > 12) 44320Sstevel@tonic-gate sm_syslog(LOG_INFO, e->e_id, "milter=%s, reject", 44330Sstevel@tonic-gate m->mf_name); 44340Sstevel@tonic-gate *state = rcmd; 44350Sstevel@tonic-gate m->mf_state = SMFS_DONE; 44360Sstevel@tonic-gate break; 44370Sstevel@tonic-gate 44380Sstevel@tonic-gate case SMFIR_DISCARD: 44390Sstevel@tonic-gate if (MilterLogLevel > 12) 44400Sstevel@tonic-gate sm_syslog(LOG_INFO, e->e_id, "milter=%s, discard", 44410Sstevel@tonic-gate m->mf_name); 44420Sstevel@tonic-gate *state = rcmd; 44430Sstevel@tonic-gate m->mf_state = SMFS_DONE; 44440Sstevel@tonic-gate break; 44450Sstevel@tonic-gate 44460Sstevel@tonic-gate case SMFIR_TEMPFAIL: 44470Sstevel@tonic-gate if (MilterLogLevel > 12) 44480Sstevel@tonic-gate sm_syslog(LOG_INFO, e->e_id, "milter=%s, tempfail", 44490Sstevel@tonic-gate m->mf_name); 44500Sstevel@tonic-gate *state = rcmd; 44510Sstevel@tonic-gate m->mf_state = SMFS_DONE; 44520Sstevel@tonic-gate break; 44530Sstevel@tonic-gate 44540Sstevel@tonic-gate case SMFIR_CONTINUE: 44550Sstevel@tonic-gate case SMFIR_ACCEPT: 44560Sstevel@tonic-gate /* this filter is done with message */ 44570Sstevel@tonic-gate if (replfailed) 44580Sstevel@tonic-gate *state = SMFIR_TEMPFAIL; 44590Sstevel@tonic-gate else 44600Sstevel@tonic-gate *state = SMFIR_ACCEPT; 44610Sstevel@tonic-gate m->mf_state = SMFS_DONE; 44620Sstevel@tonic-gate break; 44630Sstevel@tonic-gate 44640Sstevel@tonic-gate case SMFIR_PROGRESS: 44650Sstevel@tonic-gate break; 44660Sstevel@tonic-gate 44670Sstevel@tonic-gate case SMFIR_QUARANTINE: 44680Sstevel@tonic-gate if (!bitset(SMFIF_QUARANTINE, m->mf_fflags)) 44690Sstevel@tonic-gate { 44700Sstevel@tonic-gate if (MilterLogLevel > 9) 44710Sstevel@tonic-gate sm_syslog(LOG_WARNING, e->e_id, 44720Sstevel@tonic-gate "milter_data(%s): lied about quarantining, honoring request anyway", 44730Sstevel@tonic-gate m->mf_name); 44740Sstevel@tonic-gate } 44750Sstevel@tonic-gate if (response == NULL) 44760Sstevel@tonic-gate response = newstr(""); 44770Sstevel@tonic-gate if (MilterLogLevel > 3) 44780Sstevel@tonic-gate sm_syslog(LOG_INFO, e->e_id, 44790Sstevel@tonic-gate "milter=%s, quarantine=%s", 44800Sstevel@tonic-gate m->mf_name, response); 44810Sstevel@tonic-gate e->e_quarmsg = sm_rpool_strdup_x(e->e_rpool, 44820Sstevel@tonic-gate response); 44830Sstevel@tonic-gate macdefine(&e->e_macro, A_PERM, 44840Sstevel@tonic-gate macid("{quarantine}"), e->e_quarmsg); 44850Sstevel@tonic-gate break; 44860Sstevel@tonic-gate 44870Sstevel@tonic-gate case SMFIR_ADDHEADER: 44880Sstevel@tonic-gate if (!bitset(SMFIF_ADDHDRS, m->mf_fflags)) 44890Sstevel@tonic-gate { 44900Sstevel@tonic-gate if (MilterLogLevel > 9) 44910Sstevel@tonic-gate sm_syslog(LOG_WARNING, e->e_id, 44920Sstevel@tonic-gate "milter_data(%s): lied about adding headers, honoring request anyway", 44930Sstevel@tonic-gate m->mf_name); 44940Sstevel@tonic-gate } 4495*3544Sjbeck milter_addheader(m, response, rlen, e); 44960Sstevel@tonic-gate break; 44970Sstevel@tonic-gate 44980Sstevel@tonic-gate case SMFIR_INSHEADER: 44990Sstevel@tonic-gate if (!bitset(SMFIF_ADDHDRS, m->mf_fflags)) 45000Sstevel@tonic-gate { 45010Sstevel@tonic-gate if (MilterLogLevel > 9) 45020Sstevel@tonic-gate sm_syslog(LOG_WARNING, e->e_id, 45030Sstevel@tonic-gate "milter_data(%s): lied about adding headers, honoring request anyway", 45040Sstevel@tonic-gate m->mf_name); 45050Sstevel@tonic-gate } 4506*3544Sjbeck milter_insheader(m, response, rlen, e); 45070Sstevel@tonic-gate break; 45080Sstevel@tonic-gate 45090Sstevel@tonic-gate case SMFIR_CHGHEADER: 45100Sstevel@tonic-gate if (!bitset(SMFIF_CHGHDRS, m->mf_fflags)) 45110Sstevel@tonic-gate { 45120Sstevel@tonic-gate if (MilterLogLevel > 9) 45130Sstevel@tonic-gate sm_syslog(LOG_WARNING, e->e_id, 45140Sstevel@tonic-gate "milter_data(%s): lied about changing headers, honoring request anyway", 45150Sstevel@tonic-gate m->mf_name); 45160Sstevel@tonic-gate } 4517*3544Sjbeck milter_changeheader(m, response, rlen, e); 4518*3544Sjbeck break; 4519*3544Sjbeck 4520*3544Sjbeck case SMFIR_CHGFROM: 4521*3544Sjbeck if (!bitset(SMFIF_CHGFROM, m->mf_fflags)) 4522*3544Sjbeck { 4523*3544Sjbeck if (MilterLogLevel > 9) 4524*3544Sjbeck sm_syslog(LOG_WARNING, e->e_id, 4525*3544Sjbeck "milter_data(%s) lied about changing sender, honoring request anyway", 4526*3544Sjbeck m->mf_name); 4527*3544Sjbeck } 4528*3544Sjbeck milter_chgfrom(response, rlen, e); 45290Sstevel@tonic-gate break; 45300Sstevel@tonic-gate 45310Sstevel@tonic-gate case SMFIR_ADDRCPT: 45320Sstevel@tonic-gate if (!bitset(SMFIF_ADDRCPT, m->mf_fflags)) 45330Sstevel@tonic-gate { 45340Sstevel@tonic-gate if (MilterLogLevel > 9) 45350Sstevel@tonic-gate sm_syslog(LOG_WARNING, e->e_id, 45360Sstevel@tonic-gate "milter_data(%s) lied about adding recipients, honoring request anyway", 45370Sstevel@tonic-gate m->mf_name); 45380Sstevel@tonic-gate } 45390Sstevel@tonic-gate milter_addrcpt(response, rlen, e); 45400Sstevel@tonic-gate break; 45410Sstevel@tonic-gate 4542*3544Sjbeck case SMFIR_ADDRCPT_PAR: 4543*3544Sjbeck if (!bitset(SMFIF_ADDRCPT_PAR, m->mf_fflags)) 4544*3544Sjbeck { 4545*3544Sjbeck if (MilterLogLevel > 9) 4546*3544Sjbeck sm_syslog(LOG_WARNING, e->e_id, 4547*3544Sjbeck "milter_data(%s) lied about adding recipients with parameters, honoring request anyway", 4548*3544Sjbeck m->mf_name); 4549*3544Sjbeck } 4550*3544Sjbeck milter_addrcpt_par(response, rlen, e); 4551*3544Sjbeck break; 4552*3544Sjbeck 45530Sstevel@tonic-gate case SMFIR_DELRCPT: 45540Sstevel@tonic-gate if (!bitset(SMFIF_DELRCPT, m->mf_fflags)) 45550Sstevel@tonic-gate { 45560Sstevel@tonic-gate if (MilterLogLevel > 9) 45570Sstevel@tonic-gate sm_syslog(LOG_WARNING, e->e_id, 45580Sstevel@tonic-gate "milter_data(%s): lied about removing recipients, honoring request anyway", 45590Sstevel@tonic-gate m->mf_name); 45600Sstevel@tonic-gate } 45610Sstevel@tonic-gate milter_delrcpt(response, rlen, e); 45620Sstevel@tonic-gate break; 45630Sstevel@tonic-gate 45640Sstevel@tonic-gate case SMFIR_REPLBODY: 45650Sstevel@tonic-gate if (!bitset(SMFIF_MODBODY, m->mf_fflags)) 45660Sstevel@tonic-gate { 45670Sstevel@tonic-gate if (MilterLogLevel > 0) 45680Sstevel@tonic-gate sm_syslog(LOG_ERR, e->e_id, 45690Sstevel@tonic-gate "milter_data(%s): lied about replacing body, rejecting request and tempfailing message", 45700Sstevel@tonic-gate m->mf_name); 45710Sstevel@tonic-gate replfailed = true; 45720Sstevel@tonic-gate break; 45730Sstevel@tonic-gate } 45740Sstevel@tonic-gate 45750Sstevel@tonic-gate /* already failed in attempt */ 45760Sstevel@tonic-gate if (replfailed) 45770Sstevel@tonic-gate break; 45780Sstevel@tonic-gate 45790Sstevel@tonic-gate if (!dfopen) 45800Sstevel@tonic-gate { 45810Sstevel@tonic-gate if (milter_reopen_df(e) < 0) 45820Sstevel@tonic-gate { 45830Sstevel@tonic-gate replfailed = true; 45840Sstevel@tonic-gate break; 45850Sstevel@tonic-gate } 45860Sstevel@tonic-gate dfopen = true; 45870Sstevel@tonic-gate rewind = true; 45880Sstevel@tonic-gate } 45890Sstevel@tonic-gate 45900Sstevel@tonic-gate if (milter_replbody(response, rlen, 45910Sstevel@tonic-gate newfilter, e) < 0) 45920Sstevel@tonic-gate replfailed = true; 45930Sstevel@tonic-gate newfilter = false; 45940Sstevel@tonic-gate replbody = true; 45950Sstevel@tonic-gate break; 45960Sstevel@tonic-gate 45970Sstevel@tonic-gate default: 45980Sstevel@tonic-gate /* Invalid response to command */ 45990Sstevel@tonic-gate if (MilterLogLevel > 0) 46000Sstevel@tonic-gate sm_syslog(LOG_ERR, e->e_id, 46010Sstevel@tonic-gate "milter_data(%s): returned bogus response %c", 46020Sstevel@tonic-gate m->mf_name, rcmd); 46030Sstevel@tonic-gate milter_error(m, e); 46040Sstevel@tonic-gate break; 46050Sstevel@tonic-gate } 46060Sstevel@tonic-gate if (rcmd != SMFIR_REPLYCODE && response != NULL) 46070Sstevel@tonic-gate { 46080Sstevel@tonic-gate sm_free(response); /* XXX */ 46090Sstevel@tonic-gate response = NULL; 46100Sstevel@tonic-gate } 46110Sstevel@tonic-gate 46120Sstevel@tonic-gate if (m->mf_state == SMFS_ERROR) 46130Sstevel@tonic-gate break; 46140Sstevel@tonic-gate } 46150Sstevel@tonic-gate 46160Sstevel@tonic-gate if (replbody && !replfailed) 46170Sstevel@tonic-gate { 46180Sstevel@tonic-gate /* flush possible buffered character */ 46190Sstevel@tonic-gate milter_replbody(NULL, 0, !replbody, e); 46200Sstevel@tonic-gate replbody = false; 46210Sstevel@tonic-gate } 46220Sstevel@tonic-gate 46230Sstevel@tonic-gate if (m->mf_state == SMFS_ERROR) 46240Sstevel@tonic-gate { 46250Sstevel@tonic-gate MILTER_CHECK_ERROR(false, continue); 46260Sstevel@tonic-gate goto finishup; 46270Sstevel@tonic-gate } 46280Sstevel@tonic-gate } 46290Sstevel@tonic-gate 46300Sstevel@tonic-gate finishup: 46310Sstevel@tonic-gate /* leave things in the expected state if we touched it */ 46320Sstevel@tonic-gate if (replfailed) 46330Sstevel@tonic-gate { 46340Sstevel@tonic-gate if (*state == SMFIR_CONTINUE || 46350Sstevel@tonic-gate *state == SMFIR_ACCEPT) 46360Sstevel@tonic-gate { 46370Sstevel@tonic-gate *state = SMFIR_TEMPFAIL; 46380Sstevel@tonic-gate SM_FREE_CLR(response); 46390Sstevel@tonic-gate } 46400Sstevel@tonic-gate 46410Sstevel@tonic-gate if (dfopen) 46420Sstevel@tonic-gate { 46430Sstevel@tonic-gate (void) sm_io_close(e->e_dfp, SM_TIME_DEFAULT); 46440Sstevel@tonic-gate e->e_dfp = NULL; 46450Sstevel@tonic-gate e->e_flags &= ~EF_HAS_DF; 46460Sstevel@tonic-gate dfopen = false; 46470Sstevel@tonic-gate } 46480Sstevel@tonic-gate rewind = false; 46490Sstevel@tonic-gate } 46500Sstevel@tonic-gate 46510Sstevel@tonic-gate if ((dfopen && milter_reset_df(e) < 0) || 46520Sstevel@tonic-gate (rewind && bfrewind(e->e_dfp) < 0)) 46530Sstevel@tonic-gate { 46540Sstevel@tonic-gate save_errno = errno; 46550Sstevel@tonic-gate ExitStat = EX_IOERR; 46560Sstevel@tonic-gate 46570Sstevel@tonic-gate /* 46580Sstevel@tonic-gate ** If filter told us to keep message but we had 46590Sstevel@tonic-gate ** an error, we can't really keep it, tempfail it. 46600Sstevel@tonic-gate */ 46610Sstevel@tonic-gate 46620Sstevel@tonic-gate if (*state == SMFIR_CONTINUE || 46630Sstevel@tonic-gate *state == SMFIR_ACCEPT) 46640Sstevel@tonic-gate { 46650Sstevel@tonic-gate *state = SMFIR_TEMPFAIL; 46660Sstevel@tonic-gate SM_FREE_CLR(response); 46670Sstevel@tonic-gate } 46680Sstevel@tonic-gate 46690Sstevel@tonic-gate errno = save_errno; 46700Sstevel@tonic-gate syserr("milter_data: %s/%cf%s: read error", 46710Sstevel@tonic-gate qid_printqueue(e->e_qgrp, e->e_qdir), 46720Sstevel@tonic-gate DATAFL_LETTER, e->e_id); 46730Sstevel@tonic-gate } 46740Sstevel@tonic-gate 46750Sstevel@tonic-gate MILTER_CHECK_DONE_MSG(); 46760Sstevel@tonic-gate if (MilterLogLevel > 10 && *state == SMFIR_REJECT) 46770Sstevel@tonic-gate sm_syslog(LOG_INFO, e->e_id, "Milter: reject, data"); 46780Sstevel@tonic-gate return response; 46790Sstevel@tonic-gate } 46800Sstevel@tonic-gate 46810Sstevel@tonic-gate /* 46820Sstevel@tonic-gate ** MILTER_UNKNOWN -- send any unrecognized or unimplemented command 46830Sstevel@tonic-gate ** string to milter filters 46840Sstevel@tonic-gate ** 46850Sstevel@tonic-gate ** Parameters: 4686*3544Sjbeck ** smtpcmd -- the string itself. 46870Sstevel@tonic-gate ** e -- current envelope. 46880Sstevel@tonic-gate ** state -- return state from response. 46890Sstevel@tonic-gate ** 46900Sstevel@tonic-gate ** 46910Sstevel@tonic-gate ** Returns: 46920Sstevel@tonic-gate ** response string (may be NULL) 46930Sstevel@tonic-gate */ 46940Sstevel@tonic-gate 46950Sstevel@tonic-gate char * 4696*3544Sjbeck milter_unknown(smtpcmd, e, state) 4697*3544Sjbeck char *smtpcmd; 46980Sstevel@tonic-gate ENVELOPE *e; 46990Sstevel@tonic-gate char *state; 47000Sstevel@tonic-gate { 47010Sstevel@tonic-gate if (tTd(64, 10)) 4702*3544Sjbeck sm_dprintf("milter_unknown(%s)\n", smtpcmd); 4703*3544Sjbeck 4704*3544Sjbeck return milter_command(SMFIC_UNKNOWN, smtpcmd, strlen(smtpcmd) + 1, 4705*3544Sjbeck NULL, e, state, "unknown", false); 47060Sstevel@tonic-gate } 47070Sstevel@tonic-gate 47080Sstevel@tonic-gate /* 47090Sstevel@tonic-gate ** MILTER_QUIT -- informs the filter(s) we are done and closes connection(s) 47100Sstevel@tonic-gate ** 47110Sstevel@tonic-gate ** Parameters: 47120Sstevel@tonic-gate ** e -- current envelope. 47130Sstevel@tonic-gate ** 47140Sstevel@tonic-gate ** Returns: 47150Sstevel@tonic-gate ** none 47160Sstevel@tonic-gate */ 47170Sstevel@tonic-gate 47180Sstevel@tonic-gate void 47190Sstevel@tonic-gate milter_quit(e) 47200Sstevel@tonic-gate ENVELOPE *e; 47210Sstevel@tonic-gate { 47220Sstevel@tonic-gate int i; 47230Sstevel@tonic-gate 47240Sstevel@tonic-gate if (tTd(64, 10)) 47250Sstevel@tonic-gate sm_dprintf("milter_quit(%s)\n", e->e_id); 47260Sstevel@tonic-gate 47270Sstevel@tonic-gate for (i = 0; InputFilters[i] != NULL; i++) 47280Sstevel@tonic-gate milter_quit_filter(InputFilters[i], e); 47290Sstevel@tonic-gate } 4730*3544Sjbeck 47310Sstevel@tonic-gate /* 47320Sstevel@tonic-gate ** MILTER_ABORT -- informs the filter(s) that we are aborting current message 47330Sstevel@tonic-gate ** 47340Sstevel@tonic-gate ** Parameters: 47350Sstevel@tonic-gate ** e -- current envelope. 47360Sstevel@tonic-gate ** 47370Sstevel@tonic-gate ** Returns: 47380Sstevel@tonic-gate ** none 47390Sstevel@tonic-gate */ 47400Sstevel@tonic-gate 47410Sstevel@tonic-gate void 47420Sstevel@tonic-gate milter_abort(e) 47430Sstevel@tonic-gate ENVELOPE *e; 47440Sstevel@tonic-gate { 47450Sstevel@tonic-gate int i; 47460Sstevel@tonic-gate 47470Sstevel@tonic-gate if (tTd(64, 10)) 47480Sstevel@tonic-gate sm_dprintf("milter_abort\n"); 47490Sstevel@tonic-gate 47500Sstevel@tonic-gate for (i = 0; InputFilters[i] != NULL; i++) 47510Sstevel@tonic-gate { 47520Sstevel@tonic-gate struct milter *m = InputFilters[i]; 47530Sstevel@tonic-gate 47540Sstevel@tonic-gate /* sanity checks */ 47550Sstevel@tonic-gate if (m->mf_sock < 0 || m->mf_state != SMFS_INMSG) 47560Sstevel@tonic-gate continue; 47570Sstevel@tonic-gate 47580Sstevel@tonic-gate milter_abort_filter(m, e); 47590Sstevel@tonic-gate } 47600Sstevel@tonic-gate } 47610Sstevel@tonic-gate #endif /* MILTER */ 4762