1*0Sstevel@tonic-gate /* 2*0Sstevel@tonic-gate * Copyright (c) 1999-2005 Sendmail, Inc. and its suppliers. 3*0Sstevel@tonic-gate * All rights reserved. 4*0Sstevel@tonic-gate * 5*0Sstevel@tonic-gate * By using this file, you agree to the terms and conditions set 6*0Sstevel@tonic-gate * forth in the LICENSE file which can be found at the top level of 7*0Sstevel@tonic-gate * the sendmail distribution. 8*0Sstevel@tonic-gate * 9*0Sstevel@tonic-gate */ 10*0Sstevel@tonic-gate 11*0Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 12*0Sstevel@tonic-gate 13*0Sstevel@tonic-gate #include <sendmail.h> 14*0Sstevel@tonic-gate 15*0Sstevel@tonic-gate SM_RCSID("@(#)$Id: milter.c,v 8.229 2005/03/02 02:32:34 ca Exp $") 16*0Sstevel@tonic-gate 17*0Sstevel@tonic-gate #if MILTER 18*0Sstevel@tonic-gate # include <libmilter/mfapi.h> 19*0Sstevel@tonic-gate # include <libmilter/mfdef.h> 20*0Sstevel@tonic-gate 21*0Sstevel@tonic-gate # include <errno.h> 22*0Sstevel@tonic-gate # include <sys/time.h> 23*0Sstevel@tonic-gate # include <sys/uio.h> 24*0Sstevel@tonic-gate 25*0Sstevel@tonic-gate # if NETINET || NETINET6 26*0Sstevel@tonic-gate # include <arpa/inet.h> 27*0Sstevel@tonic-gate # if _FFR_MILTER_NAGLE 28*0Sstevel@tonic-gate # include <netinet/tcp.h> 29*0Sstevel@tonic-gate # endif /* _FFR_MILTER_NAGLE */ 30*0Sstevel@tonic-gate # endif /* NETINET || NETINET6 */ 31*0Sstevel@tonic-gate 32*0Sstevel@tonic-gate # include <sm/fdset.h> 33*0Sstevel@tonic-gate 34*0Sstevel@tonic-gate static void milter_connect_timeout __P((int)); 35*0Sstevel@tonic-gate static void milter_error __P((struct milter *, ENVELOPE *)); 36*0Sstevel@tonic-gate static int milter_open __P((struct milter *, bool, ENVELOPE *)); 37*0Sstevel@tonic-gate static void milter_parse_timeouts __P((char *, struct milter *)); 38*0Sstevel@tonic-gate 39*0Sstevel@tonic-gate static char *MilterConnectMacros[MAXFILTERMACROS + 1]; 40*0Sstevel@tonic-gate static char *MilterHeloMacros[MAXFILTERMACROS + 1]; 41*0Sstevel@tonic-gate static char *MilterEnvFromMacros[MAXFILTERMACROS + 1]; 42*0Sstevel@tonic-gate static char *MilterEnvRcptMacros[MAXFILTERMACROS + 1]; 43*0Sstevel@tonic-gate static char *MilterDataMacros[MAXFILTERMACROS + 1]; 44*0Sstevel@tonic-gate static char *MilterEOMMacros[MAXFILTERMACROS + 1]; 45*0Sstevel@tonic-gate static size_t MilterMaxDataSize = MILTER_MAX_DATA_SIZE; 46*0Sstevel@tonic-gate 47*0Sstevel@tonic-gate # define MILTER_CHECK_DONE_MSG() \ 48*0Sstevel@tonic-gate if (*state == SMFIR_REPLYCODE || \ 49*0Sstevel@tonic-gate *state == SMFIR_REJECT || \ 50*0Sstevel@tonic-gate *state == SMFIR_DISCARD || \ 51*0Sstevel@tonic-gate *state == SMFIR_TEMPFAIL) \ 52*0Sstevel@tonic-gate { \ 53*0Sstevel@tonic-gate /* Abort the filters to let them know we are done with msg */ \ 54*0Sstevel@tonic-gate milter_abort(e); \ 55*0Sstevel@tonic-gate } 56*0Sstevel@tonic-gate 57*0Sstevel@tonic-gate # define MILTER_CHECK_ERROR(initial, action) \ 58*0Sstevel@tonic-gate if (!initial && tTd(71, 100)) \ 59*0Sstevel@tonic-gate { \ 60*0Sstevel@tonic-gate if (e->e_quarmsg == NULL) \ 61*0Sstevel@tonic-gate { \ 62*0Sstevel@tonic-gate e->e_quarmsg = sm_rpool_strdup_x(e->e_rpool, \ 63*0Sstevel@tonic-gate "filter failure"); \ 64*0Sstevel@tonic-gate macdefine(&e->e_macro, A_PERM, macid("{quarantine}"), \ 65*0Sstevel@tonic-gate e->e_quarmsg); \ 66*0Sstevel@tonic-gate } \ 67*0Sstevel@tonic-gate } \ 68*0Sstevel@tonic-gate else if (tTd(71, 101)) \ 69*0Sstevel@tonic-gate { \ 70*0Sstevel@tonic-gate if (e->e_quarmsg == NULL) \ 71*0Sstevel@tonic-gate { \ 72*0Sstevel@tonic-gate e->e_quarmsg = sm_rpool_strdup_x(e->e_rpool, \ 73*0Sstevel@tonic-gate "filter failure"); \ 74*0Sstevel@tonic-gate macdefine(&e->e_macro, A_PERM, macid("{quarantine}"), \ 75*0Sstevel@tonic-gate e->e_quarmsg); \ 76*0Sstevel@tonic-gate } \ 77*0Sstevel@tonic-gate } \ 78*0Sstevel@tonic-gate else if (bitnset(SMF_TEMPFAIL, m->mf_flags)) \ 79*0Sstevel@tonic-gate *state = SMFIR_TEMPFAIL; \ 80*0Sstevel@tonic-gate else if (bitnset(SMF_TEMPDROP, m->mf_flags)) \ 81*0Sstevel@tonic-gate *state = SMFIR_SHUTDOWN; \ 82*0Sstevel@tonic-gate else if (bitnset(SMF_REJECT, m->mf_flags)) \ 83*0Sstevel@tonic-gate *state = SMFIR_REJECT; \ 84*0Sstevel@tonic-gate else \ 85*0Sstevel@tonic-gate action; 86*0Sstevel@tonic-gate 87*0Sstevel@tonic-gate # define MILTER_CHECK_REPLYCODE(default) \ 88*0Sstevel@tonic-gate if (response == NULL || \ 89*0Sstevel@tonic-gate strlen(response) + 1 != (size_t) rlen || \ 90*0Sstevel@tonic-gate rlen < 3 || \ 91*0Sstevel@tonic-gate (response[0] != '4' && response[0] != '5') || \ 92*0Sstevel@tonic-gate !isascii(response[1]) || !isdigit(response[1]) || \ 93*0Sstevel@tonic-gate !isascii(response[2]) || !isdigit(response[2])) \ 94*0Sstevel@tonic-gate { \ 95*0Sstevel@tonic-gate if (response != NULL) \ 96*0Sstevel@tonic-gate sm_free(response); /* XXX */ \ 97*0Sstevel@tonic-gate response = newstr(default); \ 98*0Sstevel@tonic-gate } \ 99*0Sstevel@tonic-gate else \ 100*0Sstevel@tonic-gate { \ 101*0Sstevel@tonic-gate char *ptr = response; \ 102*0Sstevel@tonic-gate \ 103*0Sstevel@tonic-gate /* Check for unprotected %'s in the string */ \ 104*0Sstevel@tonic-gate while (*ptr != '\0') \ 105*0Sstevel@tonic-gate { \ 106*0Sstevel@tonic-gate if (*ptr == '%' && *++ptr != '%') \ 107*0Sstevel@tonic-gate { \ 108*0Sstevel@tonic-gate sm_free(response); /* XXX */ \ 109*0Sstevel@tonic-gate response = newstr(default); \ 110*0Sstevel@tonic-gate break; \ 111*0Sstevel@tonic-gate } \ 112*0Sstevel@tonic-gate ptr++; \ 113*0Sstevel@tonic-gate } \ 114*0Sstevel@tonic-gate } 115*0Sstevel@tonic-gate 116*0Sstevel@tonic-gate # define MILTER_DF_ERROR(msg) \ 117*0Sstevel@tonic-gate { \ 118*0Sstevel@tonic-gate int save_errno = errno; \ 119*0Sstevel@tonic-gate \ 120*0Sstevel@tonic-gate if (tTd(64, 5)) \ 121*0Sstevel@tonic-gate { \ 122*0Sstevel@tonic-gate sm_dprintf(msg, dfname, sm_errstring(save_errno)); \ 123*0Sstevel@tonic-gate sm_dprintf("\n"); \ 124*0Sstevel@tonic-gate } \ 125*0Sstevel@tonic-gate if (MilterLogLevel > 0) \ 126*0Sstevel@tonic-gate sm_syslog(LOG_ERR, e->e_id, msg, dfname, sm_errstring(save_errno)); \ 127*0Sstevel@tonic-gate if (SuperSafe == SAFE_REALLY) \ 128*0Sstevel@tonic-gate { \ 129*0Sstevel@tonic-gate if (e->e_dfp != NULL) \ 130*0Sstevel@tonic-gate { \ 131*0Sstevel@tonic-gate (void) sm_io_close(e->e_dfp, SM_TIME_DEFAULT); \ 132*0Sstevel@tonic-gate e->e_dfp = NULL; \ 133*0Sstevel@tonic-gate } \ 134*0Sstevel@tonic-gate e->e_flags &= ~EF_HAS_DF; \ 135*0Sstevel@tonic-gate } \ 136*0Sstevel@tonic-gate errno = save_errno; \ 137*0Sstevel@tonic-gate } 138*0Sstevel@tonic-gate 139*0Sstevel@tonic-gate /* 140*0Sstevel@tonic-gate ** MILTER_TIMEOUT -- make sure socket is ready in time 141*0Sstevel@tonic-gate ** 142*0Sstevel@tonic-gate ** Parameters: 143*0Sstevel@tonic-gate ** routine -- routine name for debug/logging 144*0Sstevel@tonic-gate ** secs -- number of seconds in timeout 145*0Sstevel@tonic-gate ** write -- waiting to read or write? 146*0Sstevel@tonic-gate ** started -- whether this is part of a previous sequence 147*0Sstevel@tonic-gate ** 148*0Sstevel@tonic-gate ** Assumes 'm' is a milter structure for the current socket. 149*0Sstevel@tonic-gate */ 150*0Sstevel@tonic-gate 151*0Sstevel@tonic-gate # define MILTER_TIMEOUT(routine, secs, write, started) \ 152*0Sstevel@tonic-gate { \ 153*0Sstevel@tonic-gate int ret; \ 154*0Sstevel@tonic-gate int save_errno; \ 155*0Sstevel@tonic-gate fd_set fds; \ 156*0Sstevel@tonic-gate struct timeval tv; \ 157*0Sstevel@tonic-gate \ 158*0Sstevel@tonic-gate if (SM_FD_SETSIZE > 0 && m->mf_sock >= SM_FD_SETSIZE) \ 159*0Sstevel@tonic-gate { \ 160*0Sstevel@tonic-gate if (tTd(64, 5)) \ 161*0Sstevel@tonic-gate sm_dprintf("milter_%s(%s): socket %d is larger than FD_SETSIZE %d\n", \ 162*0Sstevel@tonic-gate (routine), m->mf_name, m->mf_sock, \ 163*0Sstevel@tonic-gate SM_FD_SETSIZE); \ 164*0Sstevel@tonic-gate if (MilterLogLevel > 0) \ 165*0Sstevel@tonic-gate sm_syslog(LOG_ERR, e->e_id, \ 166*0Sstevel@tonic-gate "Milter (%s): socket(%s) %d is larger than FD_SETSIZE %d", \ 167*0Sstevel@tonic-gate m->mf_name, (routine), m->mf_sock, \ 168*0Sstevel@tonic-gate SM_FD_SETSIZE); \ 169*0Sstevel@tonic-gate milter_error(m, e); \ 170*0Sstevel@tonic-gate return NULL; \ 171*0Sstevel@tonic-gate } \ 172*0Sstevel@tonic-gate \ 173*0Sstevel@tonic-gate do \ 174*0Sstevel@tonic-gate { \ 175*0Sstevel@tonic-gate FD_ZERO(&fds); \ 176*0Sstevel@tonic-gate SM_FD_SET(m->mf_sock, &fds); \ 177*0Sstevel@tonic-gate tv.tv_sec = (secs); \ 178*0Sstevel@tonic-gate tv.tv_usec = 0; \ 179*0Sstevel@tonic-gate ret = select(m->mf_sock + 1, \ 180*0Sstevel@tonic-gate (write) ? NULL : &fds, \ 181*0Sstevel@tonic-gate (write) ? &fds : NULL, \ 182*0Sstevel@tonic-gate NULL, &tv); \ 183*0Sstevel@tonic-gate } while (ret < 0 && errno == EINTR); \ 184*0Sstevel@tonic-gate \ 185*0Sstevel@tonic-gate switch (ret) \ 186*0Sstevel@tonic-gate { \ 187*0Sstevel@tonic-gate case 0: \ 188*0Sstevel@tonic-gate if (tTd(64, 5)) \ 189*0Sstevel@tonic-gate sm_dprintf("milter_%s(%s): timeout\n", (routine), \ 190*0Sstevel@tonic-gate m->mf_name); \ 191*0Sstevel@tonic-gate if (MilterLogLevel > 0) \ 192*0Sstevel@tonic-gate sm_syslog(LOG_ERR, e->e_id, \ 193*0Sstevel@tonic-gate "Milter (%s): %s %s %s %s", \ 194*0Sstevel@tonic-gate m->mf_name, "timeout", \ 195*0Sstevel@tonic-gate started ? "during" : "before", \ 196*0Sstevel@tonic-gate "data", (routine)); \ 197*0Sstevel@tonic-gate milter_error(m, e); \ 198*0Sstevel@tonic-gate return NULL; \ 199*0Sstevel@tonic-gate \ 200*0Sstevel@tonic-gate case -1: \ 201*0Sstevel@tonic-gate save_errno = errno; \ 202*0Sstevel@tonic-gate if (tTd(64, 5)) \ 203*0Sstevel@tonic-gate sm_dprintf("milter_%s(%s): select: %s\n", (routine), \ 204*0Sstevel@tonic-gate m->mf_name, sm_errstring(save_errno)); \ 205*0Sstevel@tonic-gate if (MilterLogLevel > 0) \ 206*0Sstevel@tonic-gate { \ 207*0Sstevel@tonic-gate sm_syslog(LOG_ERR, e->e_id, \ 208*0Sstevel@tonic-gate "Milter (%s): select(%s): %s", \ 209*0Sstevel@tonic-gate m->mf_name, (routine), \ 210*0Sstevel@tonic-gate sm_errstring(save_errno)); \ 211*0Sstevel@tonic-gate } \ 212*0Sstevel@tonic-gate milter_error(m, e); \ 213*0Sstevel@tonic-gate return NULL; \ 214*0Sstevel@tonic-gate \ 215*0Sstevel@tonic-gate default: \ 216*0Sstevel@tonic-gate if (SM_FD_ISSET(m->mf_sock, &fds)) \ 217*0Sstevel@tonic-gate break; \ 218*0Sstevel@tonic-gate if (tTd(64, 5)) \ 219*0Sstevel@tonic-gate sm_dprintf("milter_%s(%s): socket not ready\n", \ 220*0Sstevel@tonic-gate (routine), m->mf_name); \ 221*0Sstevel@tonic-gate if (MilterLogLevel > 0) \ 222*0Sstevel@tonic-gate { \ 223*0Sstevel@tonic-gate sm_syslog(LOG_ERR, e->e_id, \ 224*0Sstevel@tonic-gate "Milter (%s): socket(%s) not ready", \ 225*0Sstevel@tonic-gate m->mf_name, (routine)); \ 226*0Sstevel@tonic-gate } \ 227*0Sstevel@tonic-gate milter_error(m, e); \ 228*0Sstevel@tonic-gate return NULL; \ 229*0Sstevel@tonic-gate } \ 230*0Sstevel@tonic-gate } 231*0Sstevel@tonic-gate 232*0Sstevel@tonic-gate /* 233*0Sstevel@tonic-gate ** Low level functions 234*0Sstevel@tonic-gate */ 235*0Sstevel@tonic-gate 236*0Sstevel@tonic-gate /* 237*0Sstevel@tonic-gate ** MILTER_READ -- read from a remote milter filter 238*0Sstevel@tonic-gate ** 239*0Sstevel@tonic-gate ** Parameters: 240*0Sstevel@tonic-gate ** m -- milter to read from. 241*0Sstevel@tonic-gate ** cmd -- return param for command read. 242*0Sstevel@tonic-gate ** rlen -- return length of response string. 243*0Sstevel@tonic-gate ** to -- timeout in seconds. 244*0Sstevel@tonic-gate ** e -- current envelope. 245*0Sstevel@tonic-gate ** 246*0Sstevel@tonic-gate ** Returns: 247*0Sstevel@tonic-gate ** response string (may be NULL) 248*0Sstevel@tonic-gate */ 249*0Sstevel@tonic-gate 250*0Sstevel@tonic-gate static char * 251*0Sstevel@tonic-gate milter_sysread(m, buf, sz, to, e) 252*0Sstevel@tonic-gate struct milter *m; 253*0Sstevel@tonic-gate char *buf; 254*0Sstevel@tonic-gate ssize_t sz; 255*0Sstevel@tonic-gate time_t to; 256*0Sstevel@tonic-gate ENVELOPE *e; 257*0Sstevel@tonic-gate { 258*0Sstevel@tonic-gate time_t readstart = 0; 259*0Sstevel@tonic-gate ssize_t len, curl; 260*0Sstevel@tonic-gate bool started = false; 261*0Sstevel@tonic-gate 262*0Sstevel@tonic-gate curl = 0; 263*0Sstevel@tonic-gate 264*0Sstevel@tonic-gate if (to > 0) 265*0Sstevel@tonic-gate readstart = curtime(); 266*0Sstevel@tonic-gate 267*0Sstevel@tonic-gate for (;;) 268*0Sstevel@tonic-gate { 269*0Sstevel@tonic-gate if (to > 0) 270*0Sstevel@tonic-gate { 271*0Sstevel@tonic-gate time_t now; 272*0Sstevel@tonic-gate 273*0Sstevel@tonic-gate now = curtime(); 274*0Sstevel@tonic-gate if (now - readstart >= to) 275*0Sstevel@tonic-gate { 276*0Sstevel@tonic-gate if (tTd(64, 5)) 277*0Sstevel@tonic-gate sm_dprintf("milter_read (%s): %s %s %s", 278*0Sstevel@tonic-gate m->mf_name, "timeout", 279*0Sstevel@tonic-gate started ? "during" : "before", 280*0Sstevel@tonic-gate "data read"); 281*0Sstevel@tonic-gate if (MilterLogLevel > 0) 282*0Sstevel@tonic-gate sm_syslog(LOG_ERR, e->e_id, 283*0Sstevel@tonic-gate "Milter (%s): %s %s %s", 284*0Sstevel@tonic-gate m->mf_name, "timeout", 285*0Sstevel@tonic-gate started ? "during" : "before", 286*0Sstevel@tonic-gate "data read"); 287*0Sstevel@tonic-gate milter_error(m, e); 288*0Sstevel@tonic-gate return NULL; 289*0Sstevel@tonic-gate } 290*0Sstevel@tonic-gate to -= now - readstart; 291*0Sstevel@tonic-gate readstart = now; 292*0Sstevel@tonic-gate MILTER_TIMEOUT("read", to, false, started); 293*0Sstevel@tonic-gate } 294*0Sstevel@tonic-gate 295*0Sstevel@tonic-gate len = read(m->mf_sock, buf + curl, sz - curl); 296*0Sstevel@tonic-gate 297*0Sstevel@tonic-gate if (len < 0) 298*0Sstevel@tonic-gate { 299*0Sstevel@tonic-gate int save_errno = errno; 300*0Sstevel@tonic-gate 301*0Sstevel@tonic-gate if (tTd(64, 5)) 302*0Sstevel@tonic-gate sm_dprintf("milter_read(%s): read returned %ld: %s\n", 303*0Sstevel@tonic-gate m->mf_name, (long) len, 304*0Sstevel@tonic-gate sm_errstring(save_errno)); 305*0Sstevel@tonic-gate if (MilterLogLevel > 0) 306*0Sstevel@tonic-gate sm_syslog(LOG_ERR, e->e_id, 307*0Sstevel@tonic-gate "Milter (%s): read returned %ld: %s", 308*0Sstevel@tonic-gate m->mf_name, (long) len, 309*0Sstevel@tonic-gate sm_errstring(save_errno)); 310*0Sstevel@tonic-gate milter_error(m, e); 311*0Sstevel@tonic-gate return NULL; 312*0Sstevel@tonic-gate } 313*0Sstevel@tonic-gate 314*0Sstevel@tonic-gate started = true; 315*0Sstevel@tonic-gate curl += len; 316*0Sstevel@tonic-gate if (len == 0 || curl >= sz) 317*0Sstevel@tonic-gate break; 318*0Sstevel@tonic-gate 319*0Sstevel@tonic-gate } 320*0Sstevel@tonic-gate 321*0Sstevel@tonic-gate if (curl != sz) 322*0Sstevel@tonic-gate { 323*0Sstevel@tonic-gate if (tTd(64, 5)) 324*0Sstevel@tonic-gate sm_dprintf("milter_read(%s): cmd read returned %ld, expecting %ld\n", 325*0Sstevel@tonic-gate m->mf_name, (long) curl, (long) sz); 326*0Sstevel@tonic-gate if (MilterLogLevel > 0) 327*0Sstevel@tonic-gate sm_syslog(LOG_ERR, e->e_id, 328*0Sstevel@tonic-gate "milter_read(%s): cmd read returned %ld, expecting %ld", 329*0Sstevel@tonic-gate m->mf_name, (long) curl, (long) sz); 330*0Sstevel@tonic-gate milter_error(m, e); 331*0Sstevel@tonic-gate return NULL; 332*0Sstevel@tonic-gate } 333*0Sstevel@tonic-gate return buf; 334*0Sstevel@tonic-gate } 335*0Sstevel@tonic-gate 336*0Sstevel@tonic-gate static char * 337*0Sstevel@tonic-gate milter_read(m, cmd, rlen, to, e) 338*0Sstevel@tonic-gate struct milter *m; 339*0Sstevel@tonic-gate char *cmd; 340*0Sstevel@tonic-gate ssize_t *rlen; 341*0Sstevel@tonic-gate time_t to; 342*0Sstevel@tonic-gate ENVELOPE *e; 343*0Sstevel@tonic-gate { 344*0Sstevel@tonic-gate time_t readstart = 0; 345*0Sstevel@tonic-gate ssize_t expl; 346*0Sstevel@tonic-gate mi_int32 i; 347*0Sstevel@tonic-gate # if _FFR_MILTER_NAGLE 348*0Sstevel@tonic-gate # ifdef TCP_CORK 349*0Sstevel@tonic-gate int cork = 0; 350*0Sstevel@tonic-gate # endif 351*0Sstevel@tonic-gate # endif /* _FFR_MILTER_NAGLE */ 352*0Sstevel@tonic-gate char *buf; 353*0Sstevel@tonic-gate char data[MILTER_LEN_BYTES + 1]; 354*0Sstevel@tonic-gate 355*0Sstevel@tonic-gate if (m->mf_sock < 0) 356*0Sstevel@tonic-gate { 357*0Sstevel@tonic-gate if (MilterLogLevel > 0) 358*0Sstevel@tonic-gate sm_syslog(LOG_ERR, e->e_id, 359*0Sstevel@tonic-gate "milter_read(%s): socket closed", 360*0Sstevel@tonic-gate m->mf_name); 361*0Sstevel@tonic-gate milter_error(m, e); 362*0Sstevel@tonic-gate return NULL; 363*0Sstevel@tonic-gate } 364*0Sstevel@tonic-gate 365*0Sstevel@tonic-gate *rlen = 0; 366*0Sstevel@tonic-gate *cmd = '\0'; 367*0Sstevel@tonic-gate 368*0Sstevel@tonic-gate if (to > 0) 369*0Sstevel@tonic-gate readstart = curtime(); 370*0Sstevel@tonic-gate 371*0Sstevel@tonic-gate # if _FFR_MILTER_NAGLE 372*0Sstevel@tonic-gate # ifdef TCP_CORK 373*0Sstevel@tonic-gate setsockopt(m->mf_sock, IPPROTO_TCP, TCP_CORK, (char *)&cork, 374*0Sstevel@tonic-gate sizeof(cork)); 375*0Sstevel@tonic-gate # endif 376*0Sstevel@tonic-gate # endif /* _FFR_MILTER_NAGLE */ 377*0Sstevel@tonic-gate 378*0Sstevel@tonic-gate if (milter_sysread(m, data, sizeof data, to, e) == NULL) 379*0Sstevel@tonic-gate return NULL; 380*0Sstevel@tonic-gate 381*0Sstevel@tonic-gate # if _FFR_MILTER_NAGLE 382*0Sstevel@tonic-gate # ifdef TCP_CORK 383*0Sstevel@tonic-gate cork = 1; 384*0Sstevel@tonic-gate setsockopt(m->mf_sock, IPPROTO_TCP, TCP_CORK, (char *)&cork, 385*0Sstevel@tonic-gate sizeof(cork)); 386*0Sstevel@tonic-gate # endif 387*0Sstevel@tonic-gate # endif /* _FFR_MILTER_NAGLE */ 388*0Sstevel@tonic-gate 389*0Sstevel@tonic-gate /* reset timeout */ 390*0Sstevel@tonic-gate if (to > 0) 391*0Sstevel@tonic-gate { 392*0Sstevel@tonic-gate time_t now; 393*0Sstevel@tonic-gate 394*0Sstevel@tonic-gate now = curtime(); 395*0Sstevel@tonic-gate if (now - readstart >= to) 396*0Sstevel@tonic-gate { 397*0Sstevel@tonic-gate if (tTd(64, 5)) 398*0Sstevel@tonic-gate sm_dprintf("milter_read(%s): timeout before data read\n", 399*0Sstevel@tonic-gate m->mf_name); 400*0Sstevel@tonic-gate if (MilterLogLevel > 0) 401*0Sstevel@tonic-gate sm_syslog(LOG_ERR, e->e_id, 402*0Sstevel@tonic-gate "Milter read(%s): timeout before data read", 403*0Sstevel@tonic-gate m->mf_name); 404*0Sstevel@tonic-gate milter_error(m, e); 405*0Sstevel@tonic-gate return NULL; 406*0Sstevel@tonic-gate } 407*0Sstevel@tonic-gate to -= now - readstart; 408*0Sstevel@tonic-gate } 409*0Sstevel@tonic-gate 410*0Sstevel@tonic-gate *cmd = data[MILTER_LEN_BYTES]; 411*0Sstevel@tonic-gate data[MILTER_LEN_BYTES] = '\0'; 412*0Sstevel@tonic-gate (void) memcpy(&i, data, MILTER_LEN_BYTES); 413*0Sstevel@tonic-gate expl = ntohl(i) - 1; 414*0Sstevel@tonic-gate 415*0Sstevel@tonic-gate if (tTd(64, 25)) 416*0Sstevel@tonic-gate sm_dprintf("milter_read(%s): expecting %ld bytes\n", 417*0Sstevel@tonic-gate m->mf_name, (long) expl); 418*0Sstevel@tonic-gate 419*0Sstevel@tonic-gate if (expl < 0) 420*0Sstevel@tonic-gate { 421*0Sstevel@tonic-gate if (tTd(64, 5)) 422*0Sstevel@tonic-gate sm_dprintf("milter_read(%s): read size %ld out of range\n", 423*0Sstevel@tonic-gate m->mf_name, (long) expl); 424*0Sstevel@tonic-gate if (MilterLogLevel > 0) 425*0Sstevel@tonic-gate sm_syslog(LOG_ERR, e->e_id, 426*0Sstevel@tonic-gate "milter_read(%s): read size %ld out of range", 427*0Sstevel@tonic-gate m->mf_name, (long) expl); 428*0Sstevel@tonic-gate milter_error(m, e); 429*0Sstevel@tonic-gate return NULL; 430*0Sstevel@tonic-gate } 431*0Sstevel@tonic-gate 432*0Sstevel@tonic-gate if (expl == 0) 433*0Sstevel@tonic-gate return NULL; 434*0Sstevel@tonic-gate 435*0Sstevel@tonic-gate buf = (char *) xalloc(expl); 436*0Sstevel@tonic-gate 437*0Sstevel@tonic-gate if (milter_sysread(m, buf, expl, to, e) == NULL) 438*0Sstevel@tonic-gate { 439*0Sstevel@tonic-gate sm_free(buf); /* XXX */ 440*0Sstevel@tonic-gate return NULL; 441*0Sstevel@tonic-gate } 442*0Sstevel@tonic-gate 443*0Sstevel@tonic-gate if (tTd(64, 50)) 444*0Sstevel@tonic-gate sm_dprintf("milter_read(%s): Returning %*s\n", 445*0Sstevel@tonic-gate m->mf_name, (int) expl, buf); 446*0Sstevel@tonic-gate *rlen = expl; 447*0Sstevel@tonic-gate return buf; 448*0Sstevel@tonic-gate } 449*0Sstevel@tonic-gate 450*0Sstevel@tonic-gate /* 451*0Sstevel@tonic-gate ** MILTER_WRITE -- write to a remote milter filter 452*0Sstevel@tonic-gate ** 453*0Sstevel@tonic-gate ** Parameters: 454*0Sstevel@tonic-gate ** m -- milter to read from. 455*0Sstevel@tonic-gate ** cmd -- command to send. 456*0Sstevel@tonic-gate ** buf -- optional command data. 457*0Sstevel@tonic-gate ** len -- length of buf. 458*0Sstevel@tonic-gate ** to -- timeout in seconds. 459*0Sstevel@tonic-gate ** e -- current envelope. 460*0Sstevel@tonic-gate ** 461*0Sstevel@tonic-gate ** Returns: 462*0Sstevel@tonic-gate ** buf if successful, NULL otherwise 463*0Sstevel@tonic-gate ** Not actually used anywhere but function prototype 464*0Sstevel@tonic-gate ** must match milter_read() 465*0Sstevel@tonic-gate */ 466*0Sstevel@tonic-gate 467*0Sstevel@tonic-gate static char * 468*0Sstevel@tonic-gate milter_write(m, cmd, buf, len, to, e) 469*0Sstevel@tonic-gate struct milter *m; 470*0Sstevel@tonic-gate char cmd; 471*0Sstevel@tonic-gate char *buf; 472*0Sstevel@tonic-gate ssize_t len; 473*0Sstevel@tonic-gate time_t to; 474*0Sstevel@tonic-gate ENVELOPE *e; 475*0Sstevel@tonic-gate { 476*0Sstevel@tonic-gate time_t writestart = (time_t) 0; 477*0Sstevel@tonic-gate ssize_t sl, i; 478*0Sstevel@tonic-gate int num_vectors; 479*0Sstevel@tonic-gate mi_int32 nl; 480*0Sstevel@tonic-gate char data[MILTER_LEN_BYTES + 1]; 481*0Sstevel@tonic-gate bool started = false; 482*0Sstevel@tonic-gate struct iovec vector[2]; 483*0Sstevel@tonic-gate 484*0Sstevel@tonic-gate /* 485*0Sstevel@tonic-gate ** At most two buffers will be written, though 486*0Sstevel@tonic-gate ** only one may actually be used (see num_vectors). 487*0Sstevel@tonic-gate ** The first is the size/command and the second is the command data. 488*0Sstevel@tonic-gate */ 489*0Sstevel@tonic-gate 490*0Sstevel@tonic-gate if (len < 0 || len > MilterMaxDataSize) 491*0Sstevel@tonic-gate { 492*0Sstevel@tonic-gate if (tTd(64, 5)) 493*0Sstevel@tonic-gate sm_dprintf("milter_write(%s): length %ld out of range\n", 494*0Sstevel@tonic-gate m->mf_name, (long) len); 495*0Sstevel@tonic-gate if (MilterLogLevel > 0) 496*0Sstevel@tonic-gate sm_syslog(LOG_ERR, e->e_id, 497*0Sstevel@tonic-gate "milter_write(%s): length %ld out of range", 498*0Sstevel@tonic-gate m->mf_name, (long) len); 499*0Sstevel@tonic-gate milter_error(m, e); 500*0Sstevel@tonic-gate return NULL; 501*0Sstevel@tonic-gate } 502*0Sstevel@tonic-gate if (m->mf_sock < 0) 503*0Sstevel@tonic-gate { 504*0Sstevel@tonic-gate if (MilterLogLevel > 0) 505*0Sstevel@tonic-gate sm_syslog(LOG_ERR, e->e_id, 506*0Sstevel@tonic-gate "milter_write(%s): socket closed", 507*0Sstevel@tonic-gate m->mf_name); 508*0Sstevel@tonic-gate milter_error(m, e); 509*0Sstevel@tonic-gate return NULL; 510*0Sstevel@tonic-gate } 511*0Sstevel@tonic-gate 512*0Sstevel@tonic-gate if (tTd(64, 20)) 513*0Sstevel@tonic-gate sm_dprintf("milter_write(%s): cmd %c, len %ld\n", 514*0Sstevel@tonic-gate m->mf_name, cmd, (long) len); 515*0Sstevel@tonic-gate 516*0Sstevel@tonic-gate nl = htonl(len + 1); /* add 1 for the cmd char */ 517*0Sstevel@tonic-gate (void) memcpy(data, (char *) &nl, MILTER_LEN_BYTES); 518*0Sstevel@tonic-gate data[MILTER_LEN_BYTES] = cmd; 519*0Sstevel@tonic-gate sl = MILTER_LEN_BYTES + 1; 520*0Sstevel@tonic-gate 521*0Sstevel@tonic-gate /* set up the vector for the size / command */ 522*0Sstevel@tonic-gate vector[0].iov_base = (void *) data; 523*0Sstevel@tonic-gate vector[0].iov_len = sl; 524*0Sstevel@tonic-gate 525*0Sstevel@tonic-gate /* 526*0Sstevel@tonic-gate ** Determine if there is command data. If so, there will be two 527*0Sstevel@tonic-gate ** vectors. If not, there will be only one. The vectors are set 528*0Sstevel@tonic-gate ** up here and 'num_vectors' and 'sl' are set appropriately. 529*0Sstevel@tonic-gate */ 530*0Sstevel@tonic-gate 531*0Sstevel@tonic-gate /* NOTE: len<0 has already been checked for. Pedantic */ 532*0Sstevel@tonic-gate if (len <= 0 || buf == NULL) 533*0Sstevel@tonic-gate { 534*0Sstevel@tonic-gate /* There is no command data -- only a size / command data */ 535*0Sstevel@tonic-gate num_vectors = 1; 536*0Sstevel@tonic-gate } 537*0Sstevel@tonic-gate else 538*0Sstevel@tonic-gate { 539*0Sstevel@tonic-gate /* 540*0Sstevel@tonic-gate ** There is both size / command and command data. 541*0Sstevel@tonic-gate ** Set up the vector for the command data. 542*0Sstevel@tonic-gate */ 543*0Sstevel@tonic-gate 544*0Sstevel@tonic-gate num_vectors = 2; 545*0Sstevel@tonic-gate sl += len; 546*0Sstevel@tonic-gate vector[1].iov_base = (void *) buf; 547*0Sstevel@tonic-gate vector[1].iov_len = len; 548*0Sstevel@tonic-gate 549*0Sstevel@tonic-gate if (tTd(64, 50)) 550*0Sstevel@tonic-gate sm_dprintf("milter_write(%s): Sending %*s\n", 551*0Sstevel@tonic-gate m->mf_name, (int) len, buf); 552*0Sstevel@tonic-gate } 553*0Sstevel@tonic-gate 554*0Sstevel@tonic-gate if (to > 0) 555*0Sstevel@tonic-gate { 556*0Sstevel@tonic-gate writestart = curtime(); 557*0Sstevel@tonic-gate MILTER_TIMEOUT("write", to, true, started); 558*0Sstevel@tonic-gate } 559*0Sstevel@tonic-gate 560*0Sstevel@tonic-gate /* write the vector(s) */ 561*0Sstevel@tonic-gate i = writev(m->mf_sock, vector, num_vectors); 562*0Sstevel@tonic-gate if (i != sl) 563*0Sstevel@tonic-gate { 564*0Sstevel@tonic-gate int save_errno = errno; 565*0Sstevel@tonic-gate 566*0Sstevel@tonic-gate if (tTd(64, 5)) 567*0Sstevel@tonic-gate sm_dprintf("milter_write(%s): write(%c) returned %ld, expected %ld: %s\n", 568*0Sstevel@tonic-gate m->mf_name, cmd, (long) i, (long) sl, 569*0Sstevel@tonic-gate sm_errstring(save_errno)); 570*0Sstevel@tonic-gate if (MilterLogLevel > 0) 571*0Sstevel@tonic-gate sm_syslog(LOG_ERR, e->e_id, 572*0Sstevel@tonic-gate "Milter (%s): write(%c) returned %ld, expected %ld: %s", 573*0Sstevel@tonic-gate m->mf_name, cmd, (long) i, (long) sl, 574*0Sstevel@tonic-gate sm_errstring(save_errno)); 575*0Sstevel@tonic-gate milter_error(m, e); 576*0Sstevel@tonic-gate return NULL; 577*0Sstevel@tonic-gate } 578*0Sstevel@tonic-gate return buf; 579*0Sstevel@tonic-gate } 580*0Sstevel@tonic-gate 581*0Sstevel@tonic-gate /* 582*0Sstevel@tonic-gate ** Utility functions 583*0Sstevel@tonic-gate */ 584*0Sstevel@tonic-gate 585*0Sstevel@tonic-gate /* 586*0Sstevel@tonic-gate ** MILTER_OPEN -- connect to remote milter filter 587*0Sstevel@tonic-gate ** 588*0Sstevel@tonic-gate ** Parameters: 589*0Sstevel@tonic-gate ** m -- milter to connect to. 590*0Sstevel@tonic-gate ** parseonly -- parse but don't connect. 591*0Sstevel@tonic-gate ** e -- current envelope. 592*0Sstevel@tonic-gate ** 593*0Sstevel@tonic-gate ** Returns: 594*0Sstevel@tonic-gate ** connected socket if successful && !parseonly, 595*0Sstevel@tonic-gate ** 0 upon parse success if parseonly, 596*0Sstevel@tonic-gate ** -1 otherwise. 597*0Sstevel@tonic-gate */ 598*0Sstevel@tonic-gate 599*0Sstevel@tonic-gate static jmp_buf MilterConnectTimeout; 600*0Sstevel@tonic-gate 601*0Sstevel@tonic-gate static int 602*0Sstevel@tonic-gate milter_open(m, parseonly, e) 603*0Sstevel@tonic-gate struct milter *m; 604*0Sstevel@tonic-gate bool parseonly; 605*0Sstevel@tonic-gate ENVELOPE *e; 606*0Sstevel@tonic-gate { 607*0Sstevel@tonic-gate int sock = 0; 608*0Sstevel@tonic-gate SOCKADDR_LEN_T addrlen = 0; 609*0Sstevel@tonic-gate int addrno = 0; 610*0Sstevel@tonic-gate int save_errno; 611*0Sstevel@tonic-gate char *p; 612*0Sstevel@tonic-gate char *colon; 613*0Sstevel@tonic-gate char *at; 614*0Sstevel@tonic-gate struct hostent *hp = NULL; 615*0Sstevel@tonic-gate SOCKADDR addr; 616*0Sstevel@tonic-gate 617*0Sstevel@tonic-gate if (m->mf_conn == NULL || m->mf_conn[0] == '\0') 618*0Sstevel@tonic-gate { 619*0Sstevel@tonic-gate if (tTd(64, 5)) 620*0Sstevel@tonic-gate sm_dprintf("X%s: empty or missing socket information\n", 621*0Sstevel@tonic-gate m->mf_name); 622*0Sstevel@tonic-gate if (parseonly) 623*0Sstevel@tonic-gate syserr("X%s: empty or missing socket information", 624*0Sstevel@tonic-gate m->mf_name); 625*0Sstevel@tonic-gate else if (MilterLogLevel > 0) 626*0Sstevel@tonic-gate sm_syslog(LOG_ERR, e->e_id, 627*0Sstevel@tonic-gate "Milter (%s): empty or missing socket information", 628*0Sstevel@tonic-gate m->mf_name); 629*0Sstevel@tonic-gate milter_error(m, e); 630*0Sstevel@tonic-gate return -1; 631*0Sstevel@tonic-gate } 632*0Sstevel@tonic-gate 633*0Sstevel@tonic-gate /* protocol:filename or protocol:port@host */ 634*0Sstevel@tonic-gate memset(&addr, '\0', sizeof addr); 635*0Sstevel@tonic-gate p = m->mf_conn; 636*0Sstevel@tonic-gate colon = strchr(p, ':'); 637*0Sstevel@tonic-gate if (colon != NULL) 638*0Sstevel@tonic-gate { 639*0Sstevel@tonic-gate *colon = '\0'; 640*0Sstevel@tonic-gate 641*0Sstevel@tonic-gate if (*p == '\0') 642*0Sstevel@tonic-gate { 643*0Sstevel@tonic-gate # if NETUNIX 644*0Sstevel@tonic-gate /* default to AF_UNIX */ 645*0Sstevel@tonic-gate addr.sa.sa_family = AF_UNIX; 646*0Sstevel@tonic-gate # else /* NETUNIX */ 647*0Sstevel@tonic-gate # if NETINET 648*0Sstevel@tonic-gate /* default to AF_INET */ 649*0Sstevel@tonic-gate addr.sa.sa_family = AF_INET; 650*0Sstevel@tonic-gate # else /* NETINET */ 651*0Sstevel@tonic-gate # if NETINET6 652*0Sstevel@tonic-gate /* default to AF_INET6 */ 653*0Sstevel@tonic-gate addr.sa.sa_family = AF_INET6; 654*0Sstevel@tonic-gate # else /* NETINET6 */ 655*0Sstevel@tonic-gate /* no protocols available */ 656*0Sstevel@tonic-gate if (MilterLogLevel > 0) 657*0Sstevel@tonic-gate sm_syslog(LOG_ERR, e->e_id, 658*0Sstevel@tonic-gate "Milter (%s): no valid socket protocols available", 659*0Sstevel@tonic-gate m->mf_name); 660*0Sstevel@tonic-gate milter_error(m, e); 661*0Sstevel@tonic-gate return -1; 662*0Sstevel@tonic-gate # endif /* NETINET6 */ 663*0Sstevel@tonic-gate # endif /* NETINET */ 664*0Sstevel@tonic-gate # endif /* NETUNIX */ 665*0Sstevel@tonic-gate } 666*0Sstevel@tonic-gate # if NETUNIX 667*0Sstevel@tonic-gate else if (sm_strcasecmp(p, "unix") == 0 || 668*0Sstevel@tonic-gate sm_strcasecmp(p, "local") == 0) 669*0Sstevel@tonic-gate addr.sa.sa_family = AF_UNIX; 670*0Sstevel@tonic-gate # endif /* NETUNIX */ 671*0Sstevel@tonic-gate # if NETINET 672*0Sstevel@tonic-gate else if (sm_strcasecmp(p, "inet") == 0) 673*0Sstevel@tonic-gate addr.sa.sa_family = AF_INET; 674*0Sstevel@tonic-gate # endif /* NETINET */ 675*0Sstevel@tonic-gate # if NETINET6 676*0Sstevel@tonic-gate else if (sm_strcasecmp(p, "inet6") == 0) 677*0Sstevel@tonic-gate addr.sa.sa_family = AF_INET6; 678*0Sstevel@tonic-gate # endif /* NETINET6 */ 679*0Sstevel@tonic-gate else 680*0Sstevel@tonic-gate { 681*0Sstevel@tonic-gate # ifdef EPROTONOSUPPORT 682*0Sstevel@tonic-gate errno = EPROTONOSUPPORT; 683*0Sstevel@tonic-gate # else /* EPROTONOSUPPORT */ 684*0Sstevel@tonic-gate errno = EINVAL; 685*0Sstevel@tonic-gate # endif /* EPROTONOSUPPORT */ 686*0Sstevel@tonic-gate if (tTd(64, 5)) 687*0Sstevel@tonic-gate sm_dprintf("X%s: unknown socket type %s\n", 688*0Sstevel@tonic-gate m->mf_name, p); 689*0Sstevel@tonic-gate if (parseonly) 690*0Sstevel@tonic-gate syserr("X%s: unknown socket type %s", 691*0Sstevel@tonic-gate m->mf_name, p); 692*0Sstevel@tonic-gate else if (MilterLogLevel > 0) 693*0Sstevel@tonic-gate sm_syslog(LOG_ERR, e->e_id, 694*0Sstevel@tonic-gate "Milter (%s): unknown socket type %s", 695*0Sstevel@tonic-gate m->mf_name, p); 696*0Sstevel@tonic-gate milter_error(m, e); 697*0Sstevel@tonic-gate return -1; 698*0Sstevel@tonic-gate } 699*0Sstevel@tonic-gate *colon++ = ':'; 700*0Sstevel@tonic-gate } 701*0Sstevel@tonic-gate else 702*0Sstevel@tonic-gate { 703*0Sstevel@tonic-gate /* default to AF_UNIX */ 704*0Sstevel@tonic-gate addr.sa.sa_family = AF_UNIX; 705*0Sstevel@tonic-gate colon = p; 706*0Sstevel@tonic-gate } 707*0Sstevel@tonic-gate 708*0Sstevel@tonic-gate # if NETUNIX 709*0Sstevel@tonic-gate if (addr.sa.sa_family == AF_UNIX) 710*0Sstevel@tonic-gate { 711*0Sstevel@tonic-gate long sff = SFF_SAFEDIRPATH|SFF_OPENASROOT|SFF_NOLINK|SFF_EXECOK; 712*0Sstevel@tonic-gate 713*0Sstevel@tonic-gate at = colon; 714*0Sstevel@tonic-gate if (strlen(colon) >= sizeof addr.sunix.sun_path) 715*0Sstevel@tonic-gate { 716*0Sstevel@tonic-gate if (tTd(64, 5)) 717*0Sstevel@tonic-gate sm_dprintf("X%s: local socket name %s too long\n", 718*0Sstevel@tonic-gate m->mf_name, colon); 719*0Sstevel@tonic-gate errno = EINVAL; 720*0Sstevel@tonic-gate if (parseonly) 721*0Sstevel@tonic-gate syserr("X%s: local socket name %s too long", 722*0Sstevel@tonic-gate m->mf_name, colon); 723*0Sstevel@tonic-gate else if (MilterLogLevel > 0) 724*0Sstevel@tonic-gate sm_syslog(LOG_ERR, e->e_id, 725*0Sstevel@tonic-gate "Milter (%s): local socket name %s too long", 726*0Sstevel@tonic-gate m->mf_name, colon); 727*0Sstevel@tonic-gate milter_error(m, e); 728*0Sstevel@tonic-gate return -1; 729*0Sstevel@tonic-gate } 730*0Sstevel@tonic-gate errno = safefile(colon, RunAsUid, RunAsGid, RunAsUserName, sff, 731*0Sstevel@tonic-gate S_IRUSR|S_IWUSR, NULL); 732*0Sstevel@tonic-gate 733*0Sstevel@tonic-gate /* if just parsing .cf file, socket doesn't need to exist */ 734*0Sstevel@tonic-gate if (parseonly && errno == ENOENT) 735*0Sstevel@tonic-gate { 736*0Sstevel@tonic-gate if (OpMode == MD_DAEMON || 737*0Sstevel@tonic-gate OpMode == MD_FGDAEMON) 738*0Sstevel@tonic-gate (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, 739*0Sstevel@tonic-gate "WARNING: X%s: local socket name %s missing\n", 740*0Sstevel@tonic-gate m->mf_name, colon); 741*0Sstevel@tonic-gate } 742*0Sstevel@tonic-gate else if (errno != 0) 743*0Sstevel@tonic-gate { 744*0Sstevel@tonic-gate /* if not safe, don't create */ 745*0Sstevel@tonic-gate save_errno = errno; 746*0Sstevel@tonic-gate if (tTd(64, 5)) 747*0Sstevel@tonic-gate sm_dprintf("X%s: local socket name %s unsafe\n", 748*0Sstevel@tonic-gate m->mf_name, colon); 749*0Sstevel@tonic-gate errno = save_errno; 750*0Sstevel@tonic-gate if (parseonly) 751*0Sstevel@tonic-gate { 752*0Sstevel@tonic-gate if (OpMode == MD_DAEMON || 753*0Sstevel@tonic-gate OpMode == MD_FGDAEMON || 754*0Sstevel@tonic-gate OpMode == MD_SMTP) 755*0Sstevel@tonic-gate syserr("X%s: local socket name %s unsafe", 756*0Sstevel@tonic-gate m->mf_name, colon); 757*0Sstevel@tonic-gate } 758*0Sstevel@tonic-gate else if (MilterLogLevel > 0) 759*0Sstevel@tonic-gate sm_syslog(LOG_ERR, e->e_id, 760*0Sstevel@tonic-gate "Milter (%s): local socket name %s unsafe", 761*0Sstevel@tonic-gate m->mf_name, colon); 762*0Sstevel@tonic-gate milter_error(m, e); 763*0Sstevel@tonic-gate return -1; 764*0Sstevel@tonic-gate } 765*0Sstevel@tonic-gate 766*0Sstevel@tonic-gate (void) sm_strlcpy(addr.sunix.sun_path, colon, 767*0Sstevel@tonic-gate sizeof addr.sunix.sun_path); 768*0Sstevel@tonic-gate addrlen = sizeof (struct sockaddr_un); 769*0Sstevel@tonic-gate } 770*0Sstevel@tonic-gate else 771*0Sstevel@tonic-gate # endif /* NETUNIX */ 772*0Sstevel@tonic-gate # if NETINET || NETINET6 773*0Sstevel@tonic-gate if (false 774*0Sstevel@tonic-gate # if NETINET 775*0Sstevel@tonic-gate || addr.sa.sa_family == AF_INET 776*0Sstevel@tonic-gate # endif /* NETINET */ 777*0Sstevel@tonic-gate # if NETINET6 778*0Sstevel@tonic-gate || addr.sa.sa_family == AF_INET6 779*0Sstevel@tonic-gate # endif /* NETINET6 */ 780*0Sstevel@tonic-gate ) 781*0Sstevel@tonic-gate { 782*0Sstevel@tonic-gate unsigned short port; 783*0Sstevel@tonic-gate 784*0Sstevel@tonic-gate /* Parse port@host */ 785*0Sstevel@tonic-gate at = strchr(colon, '@'); 786*0Sstevel@tonic-gate if (at == NULL) 787*0Sstevel@tonic-gate { 788*0Sstevel@tonic-gate if (tTd(64, 5)) 789*0Sstevel@tonic-gate sm_dprintf("X%s: bad address %s (expected port@host)\n", 790*0Sstevel@tonic-gate m->mf_name, colon); 791*0Sstevel@tonic-gate if (parseonly) 792*0Sstevel@tonic-gate syserr("X%s: bad address %s (expected port@host)", 793*0Sstevel@tonic-gate m->mf_name, colon); 794*0Sstevel@tonic-gate else if (MilterLogLevel > 0) 795*0Sstevel@tonic-gate sm_syslog(LOG_ERR, e->e_id, 796*0Sstevel@tonic-gate "Milter (%s): bad address %s (expected port@host)", 797*0Sstevel@tonic-gate m->mf_name, colon); 798*0Sstevel@tonic-gate milter_error(m, e); 799*0Sstevel@tonic-gate return -1; 800*0Sstevel@tonic-gate } 801*0Sstevel@tonic-gate *at = '\0'; 802*0Sstevel@tonic-gate if (isascii(*colon) && isdigit(*colon)) 803*0Sstevel@tonic-gate port = htons((unsigned short) atoi(colon)); 804*0Sstevel@tonic-gate else 805*0Sstevel@tonic-gate { 806*0Sstevel@tonic-gate # ifdef NO_GETSERVBYNAME 807*0Sstevel@tonic-gate if (tTd(64, 5)) 808*0Sstevel@tonic-gate sm_dprintf("X%s: invalid port number %s\n", 809*0Sstevel@tonic-gate m->mf_name, colon); 810*0Sstevel@tonic-gate if (parseonly) 811*0Sstevel@tonic-gate syserr("X%s: invalid port number %s", 812*0Sstevel@tonic-gate m->mf_name, colon); 813*0Sstevel@tonic-gate else if (MilterLogLevel > 0) 814*0Sstevel@tonic-gate sm_syslog(LOG_ERR, e->e_id, 815*0Sstevel@tonic-gate "Milter (%s): invalid port number %s", 816*0Sstevel@tonic-gate m->mf_name, colon); 817*0Sstevel@tonic-gate milter_error(m, e); 818*0Sstevel@tonic-gate return -1; 819*0Sstevel@tonic-gate # else /* NO_GETSERVBYNAME */ 820*0Sstevel@tonic-gate register struct servent *sp; 821*0Sstevel@tonic-gate 822*0Sstevel@tonic-gate sp = getservbyname(colon, "tcp"); 823*0Sstevel@tonic-gate if (sp == NULL) 824*0Sstevel@tonic-gate { 825*0Sstevel@tonic-gate save_errno = errno; 826*0Sstevel@tonic-gate if (tTd(64, 5)) 827*0Sstevel@tonic-gate sm_dprintf("X%s: unknown port name %s\n", 828*0Sstevel@tonic-gate m->mf_name, colon); 829*0Sstevel@tonic-gate errno = save_errno; 830*0Sstevel@tonic-gate if (parseonly) 831*0Sstevel@tonic-gate syserr("X%s: unknown port name %s", 832*0Sstevel@tonic-gate m->mf_name, colon); 833*0Sstevel@tonic-gate else if (MilterLogLevel > 0) 834*0Sstevel@tonic-gate sm_syslog(LOG_ERR, e->e_id, 835*0Sstevel@tonic-gate "Milter (%s): unknown port name %s", 836*0Sstevel@tonic-gate m->mf_name, colon); 837*0Sstevel@tonic-gate milter_error(m, e); 838*0Sstevel@tonic-gate return -1; 839*0Sstevel@tonic-gate } 840*0Sstevel@tonic-gate port = sp->s_port; 841*0Sstevel@tonic-gate # endif /* NO_GETSERVBYNAME */ 842*0Sstevel@tonic-gate } 843*0Sstevel@tonic-gate *at++ = '@'; 844*0Sstevel@tonic-gate if (*at == '[') 845*0Sstevel@tonic-gate { 846*0Sstevel@tonic-gate char *end; 847*0Sstevel@tonic-gate 848*0Sstevel@tonic-gate end = strchr(at, ']'); 849*0Sstevel@tonic-gate if (end != NULL) 850*0Sstevel@tonic-gate { 851*0Sstevel@tonic-gate bool found = false; 852*0Sstevel@tonic-gate # if NETINET 853*0Sstevel@tonic-gate unsigned long hid = INADDR_NONE; 854*0Sstevel@tonic-gate # endif /* NETINET */ 855*0Sstevel@tonic-gate # if NETINET6 856*0Sstevel@tonic-gate struct sockaddr_in6 hid6; 857*0Sstevel@tonic-gate # endif /* NETINET6 */ 858*0Sstevel@tonic-gate 859*0Sstevel@tonic-gate *end = '\0'; 860*0Sstevel@tonic-gate # if NETINET 861*0Sstevel@tonic-gate if (addr.sa.sa_family == AF_INET && 862*0Sstevel@tonic-gate (hid = inet_addr(&at[1])) != INADDR_NONE) 863*0Sstevel@tonic-gate { 864*0Sstevel@tonic-gate addr.sin.sin_addr.s_addr = hid; 865*0Sstevel@tonic-gate addr.sin.sin_port = port; 866*0Sstevel@tonic-gate found = true; 867*0Sstevel@tonic-gate } 868*0Sstevel@tonic-gate # endif /* NETINET */ 869*0Sstevel@tonic-gate # if NETINET6 870*0Sstevel@tonic-gate (void) memset(&hid6, '\0', sizeof hid6); 871*0Sstevel@tonic-gate if (addr.sa.sa_family == AF_INET6 && 872*0Sstevel@tonic-gate anynet_pton(AF_INET6, &at[1], 873*0Sstevel@tonic-gate &hid6.sin6_addr) == 1) 874*0Sstevel@tonic-gate { 875*0Sstevel@tonic-gate addr.sin6.sin6_addr = hid6.sin6_addr; 876*0Sstevel@tonic-gate addr.sin6.sin6_port = port; 877*0Sstevel@tonic-gate found = true; 878*0Sstevel@tonic-gate } 879*0Sstevel@tonic-gate # endif /* NETINET6 */ 880*0Sstevel@tonic-gate *end = ']'; 881*0Sstevel@tonic-gate if (!found) 882*0Sstevel@tonic-gate { 883*0Sstevel@tonic-gate if (tTd(64, 5)) 884*0Sstevel@tonic-gate sm_dprintf("X%s: Invalid numeric domain spec \"%s\"\n", 885*0Sstevel@tonic-gate m->mf_name, at); 886*0Sstevel@tonic-gate if (parseonly) 887*0Sstevel@tonic-gate syserr("X%s: Invalid numeric domain spec \"%s\"", 888*0Sstevel@tonic-gate m->mf_name, at); 889*0Sstevel@tonic-gate else if (MilterLogLevel > 0) 890*0Sstevel@tonic-gate sm_syslog(LOG_ERR, e->e_id, 891*0Sstevel@tonic-gate "Milter (%s): Invalid numeric domain spec \"%s\"", 892*0Sstevel@tonic-gate m->mf_name, at); 893*0Sstevel@tonic-gate milter_error(m, e); 894*0Sstevel@tonic-gate return -1; 895*0Sstevel@tonic-gate } 896*0Sstevel@tonic-gate } 897*0Sstevel@tonic-gate else 898*0Sstevel@tonic-gate { 899*0Sstevel@tonic-gate if (tTd(64, 5)) 900*0Sstevel@tonic-gate sm_dprintf("X%s: Invalid numeric domain spec \"%s\"\n", 901*0Sstevel@tonic-gate m->mf_name, at); 902*0Sstevel@tonic-gate if (parseonly) 903*0Sstevel@tonic-gate syserr("X%s: Invalid numeric domain spec \"%s\"", 904*0Sstevel@tonic-gate m->mf_name, at); 905*0Sstevel@tonic-gate else if (MilterLogLevel > 0) 906*0Sstevel@tonic-gate sm_syslog(LOG_ERR, e->e_id, 907*0Sstevel@tonic-gate "Milter (%s): Invalid numeric domain spec \"%s\"", 908*0Sstevel@tonic-gate m->mf_name, at); 909*0Sstevel@tonic-gate milter_error(m, e); 910*0Sstevel@tonic-gate return -1; 911*0Sstevel@tonic-gate } 912*0Sstevel@tonic-gate } 913*0Sstevel@tonic-gate else 914*0Sstevel@tonic-gate { 915*0Sstevel@tonic-gate hp = sm_gethostbyname(at, addr.sa.sa_family); 916*0Sstevel@tonic-gate if (hp == NULL) 917*0Sstevel@tonic-gate { 918*0Sstevel@tonic-gate save_errno = errno; 919*0Sstevel@tonic-gate if (tTd(64, 5)) 920*0Sstevel@tonic-gate sm_dprintf("X%s: Unknown host name %s\n", 921*0Sstevel@tonic-gate m->mf_name, at); 922*0Sstevel@tonic-gate errno = save_errno; 923*0Sstevel@tonic-gate if (parseonly) 924*0Sstevel@tonic-gate syserr("X%s: Unknown host name %s", 925*0Sstevel@tonic-gate m->mf_name, at); 926*0Sstevel@tonic-gate else if (MilterLogLevel > 0) 927*0Sstevel@tonic-gate sm_syslog(LOG_ERR, e->e_id, 928*0Sstevel@tonic-gate "Milter (%s): Unknown host name %s", 929*0Sstevel@tonic-gate m->mf_name, at); 930*0Sstevel@tonic-gate milter_error(m, e); 931*0Sstevel@tonic-gate return -1; 932*0Sstevel@tonic-gate } 933*0Sstevel@tonic-gate addr.sa.sa_family = hp->h_addrtype; 934*0Sstevel@tonic-gate switch (hp->h_addrtype) 935*0Sstevel@tonic-gate { 936*0Sstevel@tonic-gate # if NETINET 937*0Sstevel@tonic-gate case AF_INET: 938*0Sstevel@tonic-gate memmove(&addr.sin.sin_addr, 939*0Sstevel@tonic-gate hp->h_addr, INADDRSZ); 940*0Sstevel@tonic-gate addr.sin.sin_port = port; 941*0Sstevel@tonic-gate addrlen = sizeof (struct sockaddr_in); 942*0Sstevel@tonic-gate addrno = 1; 943*0Sstevel@tonic-gate break; 944*0Sstevel@tonic-gate # endif /* NETINET */ 945*0Sstevel@tonic-gate 946*0Sstevel@tonic-gate # if NETINET6 947*0Sstevel@tonic-gate case AF_INET6: 948*0Sstevel@tonic-gate memmove(&addr.sin6.sin6_addr, 949*0Sstevel@tonic-gate hp->h_addr, IN6ADDRSZ); 950*0Sstevel@tonic-gate addr.sin6.sin6_port = port; 951*0Sstevel@tonic-gate addrlen = sizeof (struct sockaddr_in6); 952*0Sstevel@tonic-gate addrno = 1; 953*0Sstevel@tonic-gate break; 954*0Sstevel@tonic-gate # endif /* NETINET6 */ 955*0Sstevel@tonic-gate 956*0Sstevel@tonic-gate default: 957*0Sstevel@tonic-gate if (tTd(64, 5)) 958*0Sstevel@tonic-gate sm_dprintf("X%s: Unknown protocol for %s (%d)\n", 959*0Sstevel@tonic-gate m->mf_name, at, 960*0Sstevel@tonic-gate hp->h_addrtype); 961*0Sstevel@tonic-gate if (parseonly) 962*0Sstevel@tonic-gate syserr("X%s: Unknown protocol for %s (%d)", 963*0Sstevel@tonic-gate m->mf_name, at, hp->h_addrtype); 964*0Sstevel@tonic-gate else if (MilterLogLevel > 0) 965*0Sstevel@tonic-gate sm_syslog(LOG_ERR, e->e_id, 966*0Sstevel@tonic-gate "Milter (%s): Unknown protocol for %s (%d)", 967*0Sstevel@tonic-gate m->mf_name, at, 968*0Sstevel@tonic-gate hp->h_addrtype); 969*0Sstevel@tonic-gate milter_error(m, e); 970*0Sstevel@tonic-gate # if NETINET6 971*0Sstevel@tonic-gate freehostent(hp); 972*0Sstevel@tonic-gate # endif /* NETINET6 */ 973*0Sstevel@tonic-gate return -1; 974*0Sstevel@tonic-gate } 975*0Sstevel@tonic-gate } 976*0Sstevel@tonic-gate } 977*0Sstevel@tonic-gate else 978*0Sstevel@tonic-gate # endif /* NETINET || NETINET6 */ 979*0Sstevel@tonic-gate { 980*0Sstevel@tonic-gate if (tTd(64, 5)) 981*0Sstevel@tonic-gate sm_dprintf("X%s: unknown socket protocol\n", 982*0Sstevel@tonic-gate m->mf_name); 983*0Sstevel@tonic-gate if (parseonly) 984*0Sstevel@tonic-gate syserr("X%s: unknown socket protocol", m->mf_name); 985*0Sstevel@tonic-gate else if (MilterLogLevel > 0) 986*0Sstevel@tonic-gate sm_syslog(LOG_ERR, e->e_id, 987*0Sstevel@tonic-gate "Milter (%s): unknown socket protocol", 988*0Sstevel@tonic-gate m->mf_name); 989*0Sstevel@tonic-gate milter_error(m, e); 990*0Sstevel@tonic-gate return -1; 991*0Sstevel@tonic-gate } 992*0Sstevel@tonic-gate 993*0Sstevel@tonic-gate /* just parsing through? */ 994*0Sstevel@tonic-gate if (parseonly) 995*0Sstevel@tonic-gate { 996*0Sstevel@tonic-gate m->mf_state = SMFS_READY; 997*0Sstevel@tonic-gate # if NETINET6 998*0Sstevel@tonic-gate if (hp != NULL) 999*0Sstevel@tonic-gate freehostent(hp); 1000*0Sstevel@tonic-gate # endif /* NETINET6 */ 1001*0Sstevel@tonic-gate return 0; 1002*0Sstevel@tonic-gate } 1003*0Sstevel@tonic-gate 1004*0Sstevel@tonic-gate /* sanity check */ 1005*0Sstevel@tonic-gate if (m->mf_state != SMFS_READY && 1006*0Sstevel@tonic-gate m->mf_state != SMFS_CLOSED) 1007*0Sstevel@tonic-gate { 1008*0Sstevel@tonic-gate /* shouldn't happen */ 1009*0Sstevel@tonic-gate if (tTd(64, 1)) 1010*0Sstevel@tonic-gate sm_dprintf("Milter (%s): Trying to open filter in state %c\n", 1011*0Sstevel@tonic-gate m->mf_name, (char) m->mf_state); 1012*0Sstevel@tonic-gate milter_error(m, e); 1013*0Sstevel@tonic-gate # if NETINET6 1014*0Sstevel@tonic-gate if (hp != NULL) 1015*0Sstevel@tonic-gate freehostent(hp); 1016*0Sstevel@tonic-gate # endif /* NETINET6 */ 1017*0Sstevel@tonic-gate return -1; 1018*0Sstevel@tonic-gate } 1019*0Sstevel@tonic-gate 1020*0Sstevel@tonic-gate /* nope, actually connecting */ 1021*0Sstevel@tonic-gate for (;;) 1022*0Sstevel@tonic-gate { 1023*0Sstevel@tonic-gate sock = socket(addr.sa.sa_family, SOCK_STREAM, 0); 1024*0Sstevel@tonic-gate if (sock < 0) 1025*0Sstevel@tonic-gate { 1026*0Sstevel@tonic-gate save_errno = errno; 1027*0Sstevel@tonic-gate if (tTd(64, 5)) 1028*0Sstevel@tonic-gate sm_dprintf("Milter (%s): error creating socket: %s\n", 1029*0Sstevel@tonic-gate m->mf_name, 1030*0Sstevel@tonic-gate sm_errstring(save_errno)); 1031*0Sstevel@tonic-gate if (MilterLogLevel > 0) 1032*0Sstevel@tonic-gate sm_syslog(LOG_ERR, e->e_id, 1033*0Sstevel@tonic-gate "Milter (%s): error creating socket: %s", 1034*0Sstevel@tonic-gate m->mf_name, sm_errstring(save_errno)); 1035*0Sstevel@tonic-gate milter_error(m, e); 1036*0Sstevel@tonic-gate # if NETINET6 1037*0Sstevel@tonic-gate if (hp != NULL) 1038*0Sstevel@tonic-gate freehostent(hp); 1039*0Sstevel@tonic-gate # endif /* NETINET6 */ 1040*0Sstevel@tonic-gate return -1; 1041*0Sstevel@tonic-gate } 1042*0Sstevel@tonic-gate 1043*0Sstevel@tonic-gate if (setjmp(MilterConnectTimeout) == 0) 1044*0Sstevel@tonic-gate { 1045*0Sstevel@tonic-gate SM_EVENT *ev = NULL; 1046*0Sstevel@tonic-gate int i; 1047*0Sstevel@tonic-gate 1048*0Sstevel@tonic-gate if (m->mf_timeout[SMFTO_CONNECT] > 0) 1049*0Sstevel@tonic-gate ev = sm_setevent(m->mf_timeout[SMFTO_CONNECT], 1050*0Sstevel@tonic-gate milter_connect_timeout, 0); 1051*0Sstevel@tonic-gate 1052*0Sstevel@tonic-gate i = connect(sock, (struct sockaddr *) &addr, addrlen); 1053*0Sstevel@tonic-gate save_errno = errno; 1054*0Sstevel@tonic-gate if (ev != NULL) 1055*0Sstevel@tonic-gate sm_clrevent(ev); 1056*0Sstevel@tonic-gate errno = save_errno; 1057*0Sstevel@tonic-gate if (i >= 0) 1058*0Sstevel@tonic-gate break; 1059*0Sstevel@tonic-gate } 1060*0Sstevel@tonic-gate 1061*0Sstevel@tonic-gate /* couldn't connect.... try next address */ 1062*0Sstevel@tonic-gate save_errno = errno; 1063*0Sstevel@tonic-gate p = CurHostName; 1064*0Sstevel@tonic-gate CurHostName = at; 1065*0Sstevel@tonic-gate if (tTd(64, 5)) 1066*0Sstevel@tonic-gate sm_dprintf("milter_open (%s): open %s failed: %s\n", 1067*0Sstevel@tonic-gate m->mf_name, at, sm_errstring(save_errno)); 1068*0Sstevel@tonic-gate if (MilterLogLevel > 13) 1069*0Sstevel@tonic-gate sm_syslog(LOG_INFO, e->e_id, 1070*0Sstevel@tonic-gate "Milter (%s): open %s failed: %s", 1071*0Sstevel@tonic-gate m->mf_name, at, sm_errstring(save_errno)); 1072*0Sstevel@tonic-gate CurHostName = p; 1073*0Sstevel@tonic-gate (void) close(sock); 1074*0Sstevel@tonic-gate 1075*0Sstevel@tonic-gate /* try next address */ 1076*0Sstevel@tonic-gate if (hp != NULL && hp->h_addr_list[addrno] != NULL) 1077*0Sstevel@tonic-gate { 1078*0Sstevel@tonic-gate switch (addr.sa.sa_family) 1079*0Sstevel@tonic-gate { 1080*0Sstevel@tonic-gate # if NETINET 1081*0Sstevel@tonic-gate case AF_INET: 1082*0Sstevel@tonic-gate memmove(&addr.sin.sin_addr, 1083*0Sstevel@tonic-gate hp->h_addr_list[addrno++], 1084*0Sstevel@tonic-gate INADDRSZ); 1085*0Sstevel@tonic-gate break; 1086*0Sstevel@tonic-gate # endif /* NETINET */ 1087*0Sstevel@tonic-gate 1088*0Sstevel@tonic-gate # if NETINET6 1089*0Sstevel@tonic-gate case AF_INET6: 1090*0Sstevel@tonic-gate memmove(&addr.sin6.sin6_addr, 1091*0Sstevel@tonic-gate hp->h_addr_list[addrno++], 1092*0Sstevel@tonic-gate IN6ADDRSZ); 1093*0Sstevel@tonic-gate break; 1094*0Sstevel@tonic-gate # endif /* NETINET6 */ 1095*0Sstevel@tonic-gate 1096*0Sstevel@tonic-gate default: 1097*0Sstevel@tonic-gate if (tTd(64, 5)) 1098*0Sstevel@tonic-gate sm_dprintf("X%s: Unknown protocol for %s (%d)\n", 1099*0Sstevel@tonic-gate m->mf_name, at, 1100*0Sstevel@tonic-gate hp->h_addrtype); 1101*0Sstevel@tonic-gate if (MilterLogLevel > 0) 1102*0Sstevel@tonic-gate sm_syslog(LOG_ERR, e->e_id, 1103*0Sstevel@tonic-gate "Milter (%s): Unknown protocol for %s (%d)", 1104*0Sstevel@tonic-gate m->mf_name, at, 1105*0Sstevel@tonic-gate hp->h_addrtype); 1106*0Sstevel@tonic-gate milter_error(m, e); 1107*0Sstevel@tonic-gate # if NETINET6 1108*0Sstevel@tonic-gate freehostent(hp); 1109*0Sstevel@tonic-gate # endif /* NETINET6 */ 1110*0Sstevel@tonic-gate return -1; 1111*0Sstevel@tonic-gate } 1112*0Sstevel@tonic-gate continue; 1113*0Sstevel@tonic-gate } 1114*0Sstevel@tonic-gate p = CurHostName; 1115*0Sstevel@tonic-gate CurHostName = at; 1116*0Sstevel@tonic-gate if (tTd(64, 5)) 1117*0Sstevel@tonic-gate sm_dprintf("X%s: error connecting to filter: %s\n", 1118*0Sstevel@tonic-gate m->mf_name, sm_errstring(save_errno)); 1119*0Sstevel@tonic-gate if (MilterLogLevel > 0) 1120*0Sstevel@tonic-gate sm_syslog(LOG_ERR, e->e_id, 1121*0Sstevel@tonic-gate "Milter (%s): error connecting to filter: %s", 1122*0Sstevel@tonic-gate m->mf_name, sm_errstring(save_errno)); 1123*0Sstevel@tonic-gate CurHostName = p; 1124*0Sstevel@tonic-gate milter_error(m, e); 1125*0Sstevel@tonic-gate # if NETINET6 1126*0Sstevel@tonic-gate if (hp != NULL) 1127*0Sstevel@tonic-gate freehostent(hp); 1128*0Sstevel@tonic-gate # endif /* NETINET6 */ 1129*0Sstevel@tonic-gate return -1; 1130*0Sstevel@tonic-gate } 1131*0Sstevel@tonic-gate m->mf_state = SMFS_OPEN; 1132*0Sstevel@tonic-gate # if NETINET6 1133*0Sstevel@tonic-gate if (hp != NULL) 1134*0Sstevel@tonic-gate { 1135*0Sstevel@tonic-gate freehostent(hp); 1136*0Sstevel@tonic-gate hp = NULL; 1137*0Sstevel@tonic-gate } 1138*0Sstevel@tonic-gate # endif /* NETINET6 */ 1139*0Sstevel@tonic-gate # if _FFR_MILTER_NAGLE 1140*0Sstevel@tonic-gate # ifndef TCP_CORK 1141*0Sstevel@tonic-gate { 1142*0Sstevel@tonic-gate int nodelay = 1; 1143*0Sstevel@tonic-gate 1144*0Sstevel@tonic-gate setsockopt(m->mf_sock, IPPROTO_TCP, TCP_NODELAY, 1145*0Sstevel@tonic-gate (char *)&nodelay, sizeof(nodelay)); 1146*0Sstevel@tonic-gate } 1147*0Sstevel@tonic-gate # endif /* TCP_CORK */ 1148*0Sstevel@tonic-gate # endif /* _FFR_MILTER_NAGLE */ 1149*0Sstevel@tonic-gate return sock; 1150*0Sstevel@tonic-gate } 1151*0Sstevel@tonic-gate 1152*0Sstevel@tonic-gate static void 1153*0Sstevel@tonic-gate milter_connect_timeout(ignore) 1154*0Sstevel@tonic-gate int ignore; 1155*0Sstevel@tonic-gate { 1156*0Sstevel@tonic-gate /* 1157*0Sstevel@tonic-gate ** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD 1158*0Sstevel@tonic-gate ** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE 1159*0Sstevel@tonic-gate ** DOING. 1160*0Sstevel@tonic-gate */ 1161*0Sstevel@tonic-gate 1162*0Sstevel@tonic-gate errno = ETIMEDOUT; 1163*0Sstevel@tonic-gate longjmp(MilterConnectTimeout, 1); 1164*0Sstevel@tonic-gate } 1165*0Sstevel@tonic-gate /* 1166*0Sstevel@tonic-gate ** MILTER_SETUP -- setup structure for a mail filter 1167*0Sstevel@tonic-gate ** 1168*0Sstevel@tonic-gate ** Parameters: 1169*0Sstevel@tonic-gate ** line -- the options line. 1170*0Sstevel@tonic-gate ** 1171*0Sstevel@tonic-gate ** Returns: 1172*0Sstevel@tonic-gate ** none 1173*0Sstevel@tonic-gate */ 1174*0Sstevel@tonic-gate 1175*0Sstevel@tonic-gate void 1176*0Sstevel@tonic-gate milter_setup(line) 1177*0Sstevel@tonic-gate char *line; 1178*0Sstevel@tonic-gate { 1179*0Sstevel@tonic-gate char fcode; 1180*0Sstevel@tonic-gate register char *p; 1181*0Sstevel@tonic-gate register struct milter *m; 1182*0Sstevel@tonic-gate STAB *s; 1183*0Sstevel@tonic-gate 1184*0Sstevel@tonic-gate /* collect the filter name */ 1185*0Sstevel@tonic-gate for (p = line; 1186*0Sstevel@tonic-gate *p != '\0' && *p != ',' && !(isascii(*p) && isspace(*p)); 1187*0Sstevel@tonic-gate p++) 1188*0Sstevel@tonic-gate continue; 1189*0Sstevel@tonic-gate if (*p != '\0') 1190*0Sstevel@tonic-gate *p++ = '\0'; 1191*0Sstevel@tonic-gate if (line[0] == '\0') 1192*0Sstevel@tonic-gate { 1193*0Sstevel@tonic-gate syserr("name required for mail filter"); 1194*0Sstevel@tonic-gate return; 1195*0Sstevel@tonic-gate } 1196*0Sstevel@tonic-gate m = (struct milter *) xalloc(sizeof *m); 1197*0Sstevel@tonic-gate memset((char *) m, '\0', sizeof *m); 1198*0Sstevel@tonic-gate m->mf_name = newstr(line); 1199*0Sstevel@tonic-gate m->mf_state = SMFS_READY; 1200*0Sstevel@tonic-gate m->mf_sock = -1; 1201*0Sstevel@tonic-gate m->mf_timeout[SMFTO_CONNECT] = (time_t) 300; 1202*0Sstevel@tonic-gate m->mf_timeout[SMFTO_WRITE] = (time_t) 10; 1203*0Sstevel@tonic-gate m->mf_timeout[SMFTO_READ] = (time_t) 10; 1204*0Sstevel@tonic-gate m->mf_timeout[SMFTO_EOM] = (time_t) 300; 1205*0Sstevel@tonic-gate 1206*0Sstevel@tonic-gate /* now scan through and assign info from the fields */ 1207*0Sstevel@tonic-gate while (*p != '\0') 1208*0Sstevel@tonic-gate { 1209*0Sstevel@tonic-gate char *delimptr; 1210*0Sstevel@tonic-gate 1211*0Sstevel@tonic-gate while (*p != '\0' && 1212*0Sstevel@tonic-gate (*p == ',' || (isascii(*p) && isspace(*p)))) 1213*0Sstevel@tonic-gate p++; 1214*0Sstevel@tonic-gate 1215*0Sstevel@tonic-gate /* p now points to field code */ 1216*0Sstevel@tonic-gate fcode = *p; 1217*0Sstevel@tonic-gate while (*p != '\0' && *p != '=' && *p != ',') 1218*0Sstevel@tonic-gate p++; 1219*0Sstevel@tonic-gate if (*p++ != '=') 1220*0Sstevel@tonic-gate { 1221*0Sstevel@tonic-gate syserr("X%s: `=' expected", m->mf_name); 1222*0Sstevel@tonic-gate return; 1223*0Sstevel@tonic-gate } 1224*0Sstevel@tonic-gate while (isascii(*p) && isspace(*p)) 1225*0Sstevel@tonic-gate p++; 1226*0Sstevel@tonic-gate 1227*0Sstevel@tonic-gate /* p now points to the field body */ 1228*0Sstevel@tonic-gate p = munchstring(p, &delimptr, ','); 1229*0Sstevel@tonic-gate 1230*0Sstevel@tonic-gate /* install the field into the filter struct */ 1231*0Sstevel@tonic-gate switch (fcode) 1232*0Sstevel@tonic-gate { 1233*0Sstevel@tonic-gate case 'S': /* socket */ 1234*0Sstevel@tonic-gate if (p == NULL) 1235*0Sstevel@tonic-gate m->mf_conn = NULL; 1236*0Sstevel@tonic-gate else 1237*0Sstevel@tonic-gate m->mf_conn = newstr(p); 1238*0Sstevel@tonic-gate break; 1239*0Sstevel@tonic-gate 1240*0Sstevel@tonic-gate case 'F': /* Milter flags configured on MTA */ 1241*0Sstevel@tonic-gate for (; *p != '\0'; p++) 1242*0Sstevel@tonic-gate { 1243*0Sstevel@tonic-gate if (!(isascii(*p) && isspace(*p))) 1244*0Sstevel@tonic-gate setbitn(bitidx(*p), m->mf_flags); 1245*0Sstevel@tonic-gate } 1246*0Sstevel@tonic-gate break; 1247*0Sstevel@tonic-gate 1248*0Sstevel@tonic-gate case 'T': /* timeouts */ 1249*0Sstevel@tonic-gate milter_parse_timeouts(p, m); 1250*0Sstevel@tonic-gate break; 1251*0Sstevel@tonic-gate 1252*0Sstevel@tonic-gate default: 1253*0Sstevel@tonic-gate syserr("X%s: unknown filter equate %c=", 1254*0Sstevel@tonic-gate m->mf_name, fcode); 1255*0Sstevel@tonic-gate break; 1256*0Sstevel@tonic-gate } 1257*0Sstevel@tonic-gate p = delimptr; 1258*0Sstevel@tonic-gate } 1259*0Sstevel@tonic-gate 1260*0Sstevel@tonic-gate /* early check for errors */ 1261*0Sstevel@tonic-gate (void) milter_open(m, true, CurEnv); 1262*0Sstevel@tonic-gate 1263*0Sstevel@tonic-gate /* enter the filter into the symbol table */ 1264*0Sstevel@tonic-gate s = stab(m->mf_name, ST_MILTER, ST_ENTER); 1265*0Sstevel@tonic-gate if (s->s_milter != NULL) 1266*0Sstevel@tonic-gate syserr("X%s: duplicate filter definition", m->mf_name); 1267*0Sstevel@tonic-gate else 1268*0Sstevel@tonic-gate s->s_milter = m; 1269*0Sstevel@tonic-gate } 1270*0Sstevel@tonic-gate /* 1271*0Sstevel@tonic-gate ** MILTER_CONFIG -- parse option list into an array and check config 1272*0Sstevel@tonic-gate ** 1273*0Sstevel@tonic-gate ** Called when reading configuration file. 1274*0Sstevel@tonic-gate ** 1275*0Sstevel@tonic-gate ** Parameters: 1276*0Sstevel@tonic-gate ** spec -- the filter list. 1277*0Sstevel@tonic-gate ** list -- the array to fill in. 1278*0Sstevel@tonic-gate ** max -- the maximum number of entries in list. 1279*0Sstevel@tonic-gate ** 1280*0Sstevel@tonic-gate ** Returns: 1281*0Sstevel@tonic-gate ** none 1282*0Sstevel@tonic-gate */ 1283*0Sstevel@tonic-gate 1284*0Sstevel@tonic-gate void 1285*0Sstevel@tonic-gate milter_config(spec, list, max) 1286*0Sstevel@tonic-gate char *spec; 1287*0Sstevel@tonic-gate struct milter **list; 1288*0Sstevel@tonic-gate int max; 1289*0Sstevel@tonic-gate { 1290*0Sstevel@tonic-gate int numitems = 0; 1291*0Sstevel@tonic-gate register char *p; 1292*0Sstevel@tonic-gate 1293*0Sstevel@tonic-gate /* leave one for the NULL signifying the end of the list */ 1294*0Sstevel@tonic-gate max--; 1295*0Sstevel@tonic-gate 1296*0Sstevel@tonic-gate for (p = spec; p != NULL; ) 1297*0Sstevel@tonic-gate { 1298*0Sstevel@tonic-gate STAB *s; 1299*0Sstevel@tonic-gate 1300*0Sstevel@tonic-gate while (isascii(*p) && isspace(*p)) 1301*0Sstevel@tonic-gate p++; 1302*0Sstevel@tonic-gate if (*p == '\0') 1303*0Sstevel@tonic-gate break; 1304*0Sstevel@tonic-gate spec = p; 1305*0Sstevel@tonic-gate 1306*0Sstevel@tonic-gate if (numitems >= max) 1307*0Sstevel@tonic-gate { 1308*0Sstevel@tonic-gate syserr("Too many filters defined, %d max", max); 1309*0Sstevel@tonic-gate if (max > 0) 1310*0Sstevel@tonic-gate list[0] = NULL; 1311*0Sstevel@tonic-gate return; 1312*0Sstevel@tonic-gate } 1313*0Sstevel@tonic-gate p = strpbrk(p, ";,"); 1314*0Sstevel@tonic-gate if (p != NULL) 1315*0Sstevel@tonic-gate *p++ = '\0'; 1316*0Sstevel@tonic-gate 1317*0Sstevel@tonic-gate s = stab(spec, ST_MILTER, ST_FIND); 1318*0Sstevel@tonic-gate if (s == NULL) 1319*0Sstevel@tonic-gate { 1320*0Sstevel@tonic-gate syserr("InputFilter %s not defined", spec); 1321*0Sstevel@tonic-gate ExitStat = EX_CONFIG; 1322*0Sstevel@tonic-gate return; 1323*0Sstevel@tonic-gate } 1324*0Sstevel@tonic-gate list[numitems++] = s->s_milter; 1325*0Sstevel@tonic-gate } 1326*0Sstevel@tonic-gate list[numitems] = NULL; 1327*0Sstevel@tonic-gate 1328*0Sstevel@tonic-gate /* if not set, set to LogLevel */ 1329*0Sstevel@tonic-gate if (MilterLogLevel == -1) 1330*0Sstevel@tonic-gate MilterLogLevel = LogLevel; 1331*0Sstevel@tonic-gate } 1332*0Sstevel@tonic-gate /* 1333*0Sstevel@tonic-gate ** MILTER_PARSE_TIMEOUTS -- parse timeout list 1334*0Sstevel@tonic-gate ** 1335*0Sstevel@tonic-gate ** Called when reading configuration file. 1336*0Sstevel@tonic-gate ** 1337*0Sstevel@tonic-gate ** Parameters: 1338*0Sstevel@tonic-gate ** spec -- the timeout list. 1339*0Sstevel@tonic-gate ** m -- milter to set. 1340*0Sstevel@tonic-gate ** 1341*0Sstevel@tonic-gate ** Returns: 1342*0Sstevel@tonic-gate ** none 1343*0Sstevel@tonic-gate */ 1344*0Sstevel@tonic-gate 1345*0Sstevel@tonic-gate static void 1346*0Sstevel@tonic-gate milter_parse_timeouts(spec, m) 1347*0Sstevel@tonic-gate char *spec; 1348*0Sstevel@tonic-gate struct milter *m; 1349*0Sstevel@tonic-gate { 1350*0Sstevel@tonic-gate char fcode; 1351*0Sstevel@tonic-gate int tcode; 1352*0Sstevel@tonic-gate register char *p; 1353*0Sstevel@tonic-gate 1354*0Sstevel@tonic-gate p = spec; 1355*0Sstevel@tonic-gate 1356*0Sstevel@tonic-gate /* now scan through and assign info from the fields */ 1357*0Sstevel@tonic-gate while (*p != '\0') 1358*0Sstevel@tonic-gate { 1359*0Sstevel@tonic-gate char *delimptr; 1360*0Sstevel@tonic-gate 1361*0Sstevel@tonic-gate while (*p != '\0' && 1362*0Sstevel@tonic-gate (*p == ';' || (isascii(*p) && isspace(*p)))) 1363*0Sstevel@tonic-gate p++; 1364*0Sstevel@tonic-gate 1365*0Sstevel@tonic-gate /* p now points to field code */ 1366*0Sstevel@tonic-gate fcode = *p; 1367*0Sstevel@tonic-gate while (*p != '\0' && *p != ':') 1368*0Sstevel@tonic-gate p++; 1369*0Sstevel@tonic-gate if (*p++ != ':') 1370*0Sstevel@tonic-gate { 1371*0Sstevel@tonic-gate syserr("X%s, T=: `:' expected", m->mf_name); 1372*0Sstevel@tonic-gate return; 1373*0Sstevel@tonic-gate } 1374*0Sstevel@tonic-gate while (isascii(*p) && isspace(*p)) 1375*0Sstevel@tonic-gate p++; 1376*0Sstevel@tonic-gate 1377*0Sstevel@tonic-gate /* p now points to the field body */ 1378*0Sstevel@tonic-gate p = munchstring(p, &delimptr, ';'); 1379*0Sstevel@tonic-gate tcode = -1; 1380*0Sstevel@tonic-gate 1381*0Sstevel@tonic-gate /* install the field into the filter struct */ 1382*0Sstevel@tonic-gate switch (fcode) 1383*0Sstevel@tonic-gate { 1384*0Sstevel@tonic-gate case 'C': 1385*0Sstevel@tonic-gate tcode = SMFTO_CONNECT; 1386*0Sstevel@tonic-gate break; 1387*0Sstevel@tonic-gate 1388*0Sstevel@tonic-gate case 'S': 1389*0Sstevel@tonic-gate tcode = SMFTO_WRITE; 1390*0Sstevel@tonic-gate break; 1391*0Sstevel@tonic-gate 1392*0Sstevel@tonic-gate case 'R': 1393*0Sstevel@tonic-gate tcode = SMFTO_READ; 1394*0Sstevel@tonic-gate break; 1395*0Sstevel@tonic-gate 1396*0Sstevel@tonic-gate case 'E': 1397*0Sstevel@tonic-gate tcode = SMFTO_EOM; 1398*0Sstevel@tonic-gate break; 1399*0Sstevel@tonic-gate 1400*0Sstevel@tonic-gate default: 1401*0Sstevel@tonic-gate if (tTd(64, 5)) 1402*0Sstevel@tonic-gate sm_dprintf("X%s: %c unknown\n", 1403*0Sstevel@tonic-gate m->mf_name, fcode); 1404*0Sstevel@tonic-gate syserr("X%s: unknown filter timeout %c", 1405*0Sstevel@tonic-gate m->mf_name, fcode); 1406*0Sstevel@tonic-gate break; 1407*0Sstevel@tonic-gate } 1408*0Sstevel@tonic-gate if (tcode >= 0) 1409*0Sstevel@tonic-gate { 1410*0Sstevel@tonic-gate m->mf_timeout[tcode] = convtime(p, 's'); 1411*0Sstevel@tonic-gate if (tTd(64, 5)) 1412*0Sstevel@tonic-gate sm_dprintf("X%s: %c=%ld\n", 1413*0Sstevel@tonic-gate m->mf_name, fcode, 1414*0Sstevel@tonic-gate (u_long) m->mf_timeout[tcode]); 1415*0Sstevel@tonic-gate } 1416*0Sstevel@tonic-gate p = delimptr; 1417*0Sstevel@tonic-gate } 1418*0Sstevel@tonic-gate } 1419*0Sstevel@tonic-gate /* 1420*0Sstevel@tonic-gate ** MILTER_SET_OPTION -- set an individual milter option 1421*0Sstevel@tonic-gate ** 1422*0Sstevel@tonic-gate ** Parameters: 1423*0Sstevel@tonic-gate ** name -- the name of the option. 1424*0Sstevel@tonic-gate ** val -- the value of the option. 1425*0Sstevel@tonic-gate ** sticky -- if set, don't let other setoptions override 1426*0Sstevel@tonic-gate ** this value. 1427*0Sstevel@tonic-gate ** 1428*0Sstevel@tonic-gate ** Returns: 1429*0Sstevel@tonic-gate ** none. 1430*0Sstevel@tonic-gate */ 1431*0Sstevel@tonic-gate 1432*0Sstevel@tonic-gate /* set if Milter sub-option is stuck */ 1433*0Sstevel@tonic-gate static BITMAP256 StickyMilterOpt; 1434*0Sstevel@tonic-gate 1435*0Sstevel@tonic-gate static struct milteropt 1436*0Sstevel@tonic-gate { 1437*0Sstevel@tonic-gate char *mo_name; /* long name of milter option */ 1438*0Sstevel@tonic-gate unsigned char mo_code; /* code for option */ 1439*0Sstevel@tonic-gate } MilterOptTab[] = 1440*0Sstevel@tonic-gate { 1441*0Sstevel@tonic-gate # define MO_MACROS_CONNECT 0x01 1442*0Sstevel@tonic-gate { "macros.connect", MO_MACROS_CONNECT }, 1443*0Sstevel@tonic-gate # define MO_MACROS_HELO 0x02 1444*0Sstevel@tonic-gate { "macros.helo", MO_MACROS_HELO }, 1445*0Sstevel@tonic-gate # define MO_MACROS_ENVFROM 0x03 1446*0Sstevel@tonic-gate { "macros.envfrom", MO_MACROS_ENVFROM }, 1447*0Sstevel@tonic-gate # define MO_MACROS_ENVRCPT 0x04 1448*0Sstevel@tonic-gate { "macros.envrcpt", MO_MACROS_ENVRCPT }, 1449*0Sstevel@tonic-gate # define MO_MACROS_DATA 0x05 1450*0Sstevel@tonic-gate { "macros.data", MO_MACROS_DATA }, 1451*0Sstevel@tonic-gate # define MO_MACROS_EOM 0x06 1452*0Sstevel@tonic-gate { "macros.eom", MO_MACROS_EOM }, 1453*0Sstevel@tonic-gate # define MO_LOGLEVEL 0x07 1454*0Sstevel@tonic-gate { "loglevel", MO_LOGLEVEL }, 1455*0Sstevel@tonic-gate # if _FFR_MAXDATASIZE 1456*0Sstevel@tonic-gate # define MO_MAXDATASIZE 0x08 1457*0Sstevel@tonic-gate { "maxdatasize", MO_MAXDATASIZE }, 1458*0Sstevel@tonic-gate # endif /* _FFR_MAXDATASIZE */ 1459*0Sstevel@tonic-gate { NULL, 0 }, 1460*0Sstevel@tonic-gate }; 1461*0Sstevel@tonic-gate 1462*0Sstevel@tonic-gate void 1463*0Sstevel@tonic-gate milter_set_option(name, val, sticky) 1464*0Sstevel@tonic-gate char *name; 1465*0Sstevel@tonic-gate char *val; 1466*0Sstevel@tonic-gate bool sticky; 1467*0Sstevel@tonic-gate { 1468*0Sstevel@tonic-gate int nummac = 0; 1469*0Sstevel@tonic-gate register struct milteropt *mo; 1470*0Sstevel@tonic-gate char *p; 1471*0Sstevel@tonic-gate char **macros = NULL; 1472*0Sstevel@tonic-gate 1473*0Sstevel@tonic-gate if (tTd(37, 2) || tTd(64, 5)) 1474*0Sstevel@tonic-gate sm_dprintf("milter_set_option(%s = %s)", name, val); 1475*0Sstevel@tonic-gate 1476*0Sstevel@tonic-gate if (name == NULL) 1477*0Sstevel@tonic-gate { 1478*0Sstevel@tonic-gate syserr("milter_set_option: invalid Milter option, must specify suboption"); 1479*0Sstevel@tonic-gate return; 1480*0Sstevel@tonic-gate } 1481*0Sstevel@tonic-gate 1482*0Sstevel@tonic-gate for (mo = MilterOptTab; mo->mo_name != NULL; mo++) 1483*0Sstevel@tonic-gate { 1484*0Sstevel@tonic-gate if (sm_strcasecmp(mo->mo_name, name) == 0) 1485*0Sstevel@tonic-gate break; 1486*0Sstevel@tonic-gate } 1487*0Sstevel@tonic-gate 1488*0Sstevel@tonic-gate if (mo->mo_name == NULL) 1489*0Sstevel@tonic-gate { 1490*0Sstevel@tonic-gate syserr("milter_set_option: invalid Milter option %s", name); 1491*0Sstevel@tonic-gate return; 1492*0Sstevel@tonic-gate } 1493*0Sstevel@tonic-gate 1494*0Sstevel@tonic-gate /* 1495*0Sstevel@tonic-gate ** See if this option is preset for us. 1496*0Sstevel@tonic-gate */ 1497*0Sstevel@tonic-gate 1498*0Sstevel@tonic-gate if (!sticky && bitnset(mo->mo_code, StickyMilterOpt)) 1499*0Sstevel@tonic-gate { 1500*0Sstevel@tonic-gate if (tTd(37, 2) || tTd(64,5)) 1501*0Sstevel@tonic-gate sm_dprintf(" (ignored)\n"); 1502*0Sstevel@tonic-gate return; 1503*0Sstevel@tonic-gate } 1504*0Sstevel@tonic-gate 1505*0Sstevel@tonic-gate if (tTd(37, 2) || tTd(64,5)) 1506*0Sstevel@tonic-gate sm_dprintf("\n"); 1507*0Sstevel@tonic-gate 1508*0Sstevel@tonic-gate switch (mo->mo_code) 1509*0Sstevel@tonic-gate { 1510*0Sstevel@tonic-gate case MO_LOGLEVEL: 1511*0Sstevel@tonic-gate MilterLogLevel = atoi(val); 1512*0Sstevel@tonic-gate break; 1513*0Sstevel@tonic-gate 1514*0Sstevel@tonic-gate #if _FFR_MAXDATASIZE 1515*0Sstevel@tonic-gate case MO_MAXDATASIZE: 1516*0Sstevel@tonic-gate MilterMaxDataSize = (size_t)atol(val); 1517*0Sstevel@tonic-gate break; 1518*0Sstevel@tonic-gate #endif /* _FFR_MAXDATASIZE */ 1519*0Sstevel@tonic-gate 1520*0Sstevel@tonic-gate case MO_MACROS_CONNECT: 1521*0Sstevel@tonic-gate if (macros == NULL) 1522*0Sstevel@tonic-gate macros = MilterConnectMacros; 1523*0Sstevel@tonic-gate /* FALLTHROUGH */ 1524*0Sstevel@tonic-gate 1525*0Sstevel@tonic-gate case MO_MACROS_HELO: 1526*0Sstevel@tonic-gate if (macros == NULL) 1527*0Sstevel@tonic-gate macros = MilterHeloMacros; 1528*0Sstevel@tonic-gate /* FALLTHROUGH */ 1529*0Sstevel@tonic-gate 1530*0Sstevel@tonic-gate case MO_MACROS_ENVFROM: 1531*0Sstevel@tonic-gate if (macros == NULL) 1532*0Sstevel@tonic-gate macros = MilterEnvFromMacros; 1533*0Sstevel@tonic-gate /* FALLTHROUGH */ 1534*0Sstevel@tonic-gate 1535*0Sstevel@tonic-gate case MO_MACROS_ENVRCPT: 1536*0Sstevel@tonic-gate if (macros == NULL) 1537*0Sstevel@tonic-gate macros = MilterEnvRcptMacros; 1538*0Sstevel@tonic-gate /* FALLTHROUGH */ 1539*0Sstevel@tonic-gate 1540*0Sstevel@tonic-gate case MO_MACROS_EOM: 1541*0Sstevel@tonic-gate if (macros == NULL) 1542*0Sstevel@tonic-gate macros = MilterEOMMacros; 1543*0Sstevel@tonic-gate /* FALLTHROUGH */ 1544*0Sstevel@tonic-gate 1545*0Sstevel@tonic-gate case MO_MACROS_DATA: 1546*0Sstevel@tonic-gate if (macros == NULL) 1547*0Sstevel@tonic-gate macros = MilterDataMacros; 1548*0Sstevel@tonic-gate 1549*0Sstevel@tonic-gate p = newstr(val); 1550*0Sstevel@tonic-gate while (*p != '\0') 1551*0Sstevel@tonic-gate { 1552*0Sstevel@tonic-gate char *macro; 1553*0Sstevel@tonic-gate 1554*0Sstevel@tonic-gate /* Skip leading commas, spaces */ 1555*0Sstevel@tonic-gate while (*p != '\0' && 1556*0Sstevel@tonic-gate (*p == ',' || (isascii(*p) && isspace(*p)))) 1557*0Sstevel@tonic-gate p++; 1558*0Sstevel@tonic-gate 1559*0Sstevel@tonic-gate if (*p == '\0') 1560*0Sstevel@tonic-gate break; 1561*0Sstevel@tonic-gate 1562*0Sstevel@tonic-gate /* Find end of macro */ 1563*0Sstevel@tonic-gate macro = p; 1564*0Sstevel@tonic-gate while (*p != '\0' && *p != ',' && 1565*0Sstevel@tonic-gate isascii(*p) && !isspace(*p)) 1566*0Sstevel@tonic-gate p++; 1567*0Sstevel@tonic-gate if (*p != '\0') 1568*0Sstevel@tonic-gate *p++ = '\0'; 1569*0Sstevel@tonic-gate 1570*0Sstevel@tonic-gate if (nummac >= MAXFILTERMACROS) 1571*0Sstevel@tonic-gate { 1572*0Sstevel@tonic-gate syserr("milter_set_option: too many macros in Milter.%s (max %d)", 1573*0Sstevel@tonic-gate name, MAXFILTERMACROS); 1574*0Sstevel@tonic-gate macros[nummac] = NULL; 1575*0Sstevel@tonic-gate break; 1576*0Sstevel@tonic-gate } 1577*0Sstevel@tonic-gate macros[nummac++] = macro; 1578*0Sstevel@tonic-gate } 1579*0Sstevel@tonic-gate macros[nummac] = NULL; 1580*0Sstevel@tonic-gate break; 1581*0Sstevel@tonic-gate 1582*0Sstevel@tonic-gate default: 1583*0Sstevel@tonic-gate syserr("milter_set_option: invalid Milter option %s", name); 1584*0Sstevel@tonic-gate break; 1585*0Sstevel@tonic-gate } 1586*0Sstevel@tonic-gate if (sticky) 1587*0Sstevel@tonic-gate setbitn(mo->mo_code, StickyMilterOpt); 1588*0Sstevel@tonic-gate } 1589*0Sstevel@tonic-gate /* 1590*0Sstevel@tonic-gate ** MILTER_REOPEN_DF -- open & truncate the data file (for replbody) 1591*0Sstevel@tonic-gate ** 1592*0Sstevel@tonic-gate ** Parameters: 1593*0Sstevel@tonic-gate ** e -- current envelope. 1594*0Sstevel@tonic-gate ** 1595*0Sstevel@tonic-gate ** Returns: 1596*0Sstevel@tonic-gate ** 0 if succesful, -1 otherwise 1597*0Sstevel@tonic-gate */ 1598*0Sstevel@tonic-gate 1599*0Sstevel@tonic-gate static int 1600*0Sstevel@tonic-gate milter_reopen_df(e) 1601*0Sstevel@tonic-gate ENVELOPE *e; 1602*0Sstevel@tonic-gate { 1603*0Sstevel@tonic-gate char dfname[MAXPATHLEN]; 1604*0Sstevel@tonic-gate 1605*0Sstevel@tonic-gate (void) sm_strlcpy(dfname, queuename(e, DATAFL_LETTER), sizeof dfname); 1606*0Sstevel@tonic-gate 1607*0Sstevel@tonic-gate /* 1608*0Sstevel@tonic-gate ** In SuperSafe == SAFE_REALLY mode, e->e_dfp is a read-only FP so 1609*0Sstevel@tonic-gate ** close and reopen writable (later close and reopen 1610*0Sstevel@tonic-gate ** read only again). 1611*0Sstevel@tonic-gate ** 1612*0Sstevel@tonic-gate ** In SuperSafe != SAFE_REALLY mode, e->e_dfp still points at the 1613*0Sstevel@tonic-gate ** buffered file I/O descriptor, still open for writing so there 1614*0Sstevel@tonic-gate ** isn't any work to do here (except checking for consistency). 1615*0Sstevel@tonic-gate */ 1616*0Sstevel@tonic-gate 1617*0Sstevel@tonic-gate if (SuperSafe == SAFE_REALLY) 1618*0Sstevel@tonic-gate { 1619*0Sstevel@tonic-gate /* close read-only data file */ 1620*0Sstevel@tonic-gate if (bitset(EF_HAS_DF, e->e_flags) && e->e_dfp != NULL) 1621*0Sstevel@tonic-gate { 1622*0Sstevel@tonic-gate (void) sm_io_close(e->e_dfp, SM_TIME_DEFAULT); 1623*0Sstevel@tonic-gate e->e_flags &= ~EF_HAS_DF; 1624*0Sstevel@tonic-gate } 1625*0Sstevel@tonic-gate 1626*0Sstevel@tonic-gate /* open writable */ 1627*0Sstevel@tonic-gate if ((e->e_dfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, dfname, 1628*0Sstevel@tonic-gate SM_IO_RDWR_B, NULL)) == NULL) 1629*0Sstevel@tonic-gate { 1630*0Sstevel@tonic-gate MILTER_DF_ERROR("milter_reopen_df: sm_io_open %s: %s"); 1631*0Sstevel@tonic-gate return -1; 1632*0Sstevel@tonic-gate } 1633*0Sstevel@tonic-gate } 1634*0Sstevel@tonic-gate else if (e->e_dfp == NULL) 1635*0Sstevel@tonic-gate { 1636*0Sstevel@tonic-gate /* shouldn't happen */ 1637*0Sstevel@tonic-gate errno = ENOENT; 1638*0Sstevel@tonic-gate MILTER_DF_ERROR("milter_reopen_df: NULL e_dfp (%s: %s)"); 1639*0Sstevel@tonic-gate return -1; 1640*0Sstevel@tonic-gate } 1641*0Sstevel@tonic-gate return 0; 1642*0Sstevel@tonic-gate } 1643*0Sstevel@tonic-gate /* 1644*0Sstevel@tonic-gate ** MILTER_RESET_DF -- re-open read-only the data file (for replbody) 1645*0Sstevel@tonic-gate ** 1646*0Sstevel@tonic-gate ** Parameters: 1647*0Sstevel@tonic-gate ** e -- current envelope. 1648*0Sstevel@tonic-gate ** 1649*0Sstevel@tonic-gate ** Returns: 1650*0Sstevel@tonic-gate ** 0 if succesful, -1 otherwise 1651*0Sstevel@tonic-gate */ 1652*0Sstevel@tonic-gate 1653*0Sstevel@tonic-gate static int 1654*0Sstevel@tonic-gate milter_reset_df(e) 1655*0Sstevel@tonic-gate ENVELOPE *e; 1656*0Sstevel@tonic-gate { 1657*0Sstevel@tonic-gate int afd; 1658*0Sstevel@tonic-gate char dfname[MAXPATHLEN]; 1659*0Sstevel@tonic-gate 1660*0Sstevel@tonic-gate (void) sm_strlcpy(dfname, queuename(e, DATAFL_LETTER), sizeof dfname); 1661*0Sstevel@tonic-gate 1662*0Sstevel@tonic-gate if (sm_io_flush(e->e_dfp, SM_TIME_DEFAULT) != 0 || 1663*0Sstevel@tonic-gate sm_io_error(e->e_dfp)) 1664*0Sstevel@tonic-gate { 1665*0Sstevel@tonic-gate MILTER_DF_ERROR("milter_reset_df: error writing/flushing %s: %s"); 1666*0Sstevel@tonic-gate return -1; 1667*0Sstevel@tonic-gate } 1668*0Sstevel@tonic-gate else if (SuperSafe != SAFE_REALLY) 1669*0Sstevel@tonic-gate { 1670*0Sstevel@tonic-gate /* skip next few clauses */ 1671*0Sstevel@tonic-gate /* EMPTY */ 1672*0Sstevel@tonic-gate } 1673*0Sstevel@tonic-gate else if ((afd = sm_io_getinfo(e->e_dfp, SM_IO_WHAT_FD, NULL)) >= 0 1674*0Sstevel@tonic-gate && fsync(afd) < 0) 1675*0Sstevel@tonic-gate { 1676*0Sstevel@tonic-gate MILTER_DF_ERROR("milter_reset_df: error sync'ing %s: %s"); 1677*0Sstevel@tonic-gate return -1; 1678*0Sstevel@tonic-gate } 1679*0Sstevel@tonic-gate else if (sm_io_close(e->e_dfp, SM_TIME_DEFAULT) < 0) 1680*0Sstevel@tonic-gate { 1681*0Sstevel@tonic-gate MILTER_DF_ERROR("milter_reset_df: error closing %s: %s"); 1682*0Sstevel@tonic-gate return -1; 1683*0Sstevel@tonic-gate } 1684*0Sstevel@tonic-gate else if ((e->e_dfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, dfname, 1685*0Sstevel@tonic-gate SM_IO_RDONLY_B, NULL)) == NULL) 1686*0Sstevel@tonic-gate { 1687*0Sstevel@tonic-gate MILTER_DF_ERROR("milter_reset_df: error reopening %s: %s"); 1688*0Sstevel@tonic-gate return -1; 1689*0Sstevel@tonic-gate } 1690*0Sstevel@tonic-gate else 1691*0Sstevel@tonic-gate e->e_flags |= EF_HAS_DF; 1692*0Sstevel@tonic-gate return 0; 1693*0Sstevel@tonic-gate } 1694*0Sstevel@tonic-gate /* 1695*0Sstevel@tonic-gate ** MILTER_CAN_DELRCPTS -- can any milter filters delete recipients? 1696*0Sstevel@tonic-gate ** 1697*0Sstevel@tonic-gate ** Parameters: 1698*0Sstevel@tonic-gate ** none 1699*0Sstevel@tonic-gate ** 1700*0Sstevel@tonic-gate ** Returns: 1701*0Sstevel@tonic-gate ** true if any filter deletes recipients, false otherwise 1702*0Sstevel@tonic-gate */ 1703*0Sstevel@tonic-gate 1704*0Sstevel@tonic-gate bool 1705*0Sstevel@tonic-gate milter_can_delrcpts() 1706*0Sstevel@tonic-gate { 1707*0Sstevel@tonic-gate bool can = false; 1708*0Sstevel@tonic-gate int i; 1709*0Sstevel@tonic-gate 1710*0Sstevel@tonic-gate if (tTd(64, 10)) 1711*0Sstevel@tonic-gate sm_dprintf("milter_can_delrcpts:"); 1712*0Sstevel@tonic-gate 1713*0Sstevel@tonic-gate for (i = 0; InputFilters[i] != NULL; i++) 1714*0Sstevel@tonic-gate { 1715*0Sstevel@tonic-gate struct milter *m = InputFilters[i]; 1716*0Sstevel@tonic-gate 1717*0Sstevel@tonic-gate if (bitset(SMFIF_DELRCPT, m->mf_fflags)) 1718*0Sstevel@tonic-gate { 1719*0Sstevel@tonic-gate can = true; 1720*0Sstevel@tonic-gate break; 1721*0Sstevel@tonic-gate } 1722*0Sstevel@tonic-gate } 1723*0Sstevel@tonic-gate if (tTd(64, 10)) 1724*0Sstevel@tonic-gate sm_dprintf("%s\n", can ? "true" : "false"); 1725*0Sstevel@tonic-gate 1726*0Sstevel@tonic-gate return can; 1727*0Sstevel@tonic-gate } 1728*0Sstevel@tonic-gate /* 1729*0Sstevel@tonic-gate ** MILTER_QUIT_FILTER -- close down a single filter 1730*0Sstevel@tonic-gate ** 1731*0Sstevel@tonic-gate ** Parameters: 1732*0Sstevel@tonic-gate ** m -- milter structure of filter to close down. 1733*0Sstevel@tonic-gate ** e -- current envelope. 1734*0Sstevel@tonic-gate ** 1735*0Sstevel@tonic-gate ** Returns: 1736*0Sstevel@tonic-gate ** none 1737*0Sstevel@tonic-gate */ 1738*0Sstevel@tonic-gate 1739*0Sstevel@tonic-gate static void 1740*0Sstevel@tonic-gate milter_quit_filter(m, e) 1741*0Sstevel@tonic-gate struct milter *m; 1742*0Sstevel@tonic-gate ENVELOPE *e; 1743*0Sstevel@tonic-gate { 1744*0Sstevel@tonic-gate if (tTd(64, 10)) 1745*0Sstevel@tonic-gate sm_dprintf("milter_quit_filter(%s)\n", m->mf_name); 1746*0Sstevel@tonic-gate if (MilterLogLevel > 18) 1747*0Sstevel@tonic-gate sm_syslog(LOG_INFO, e->e_id, "Milter (%s): quit filter", 1748*0Sstevel@tonic-gate m->mf_name); 1749*0Sstevel@tonic-gate 1750*0Sstevel@tonic-gate /* Never replace error state */ 1751*0Sstevel@tonic-gate if (m->mf_state == SMFS_ERROR) 1752*0Sstevel@tonic-gate return; 1753*0Sstevel@tonic-gate 1754*0Sstevel@tonic-gate if (m->mf_sock < 0 || 1755*0Sstevel@tonic-gate m->mf_state == SMFS_CLOSED || 1756*0Sstevel@tonic-gate m->mf_state == SMFS_READY) 1757*0Sstevel@tonic-gate { 1758*0Sstevel@tonic-gate m->mf_sock = -1; 1759*0Sstevel@tonic-gate m->mf_state = SMFS_CLOSED; 1760*0Sstevel@tonic-gate return; 1761*0Sstevel@tonic-gate } 1762*0Sstevel@tonic-gate 1763*0Sstevel@tonic-gate (void) milter_write(m, SMFIC_QUIT, (char *) NULL, 0, 1764*0Sstevel@tonic-gate m->mf_timeout[SMFTO_WRITE], e); 1765*0Sstevel@tonic-gate if (m->mf_sock >= 0) 1766*0Sstevel@tonic-gate { 1767*0Sstevel@tonic-gate (void) close(m->mf_sock); 1768*0Sstevel@tonic-gate m->mf_sock = -1; 1769*0Sstevel@tonic-gate } 1770*0Sstevel@tonic-gate if (m->mf_state != SMFS_ERROR) 1771*0Sstevel@tonic-gate m->mf_state = SMFS_CLOSED; 1772*0Sstevel@tonic-gate } 1773*0Sstevel@tonic-gate /* 1774*0Sstevel@tonic-gate ** MILTER_ABORT_FILTER -- tell filter to abort current message 1775*0Sstevel@tonic-gate ** 1776*0Sstevel@tonic-gate ** Parameters: 1777*0Sstevel@tonic-gate ** m -- milter structure of filter to abort. 1778*0Sstevel@tonic-gate ** e -- current envelope. 1779*0Sstevel@tonic-gate ** 1780*0Sstevel@tonic-gate ** Returns: 1781*0Sstevel@tonic-gate ** none 1782*0Sstevel@tonic-gate */ 1783*0Sstevel@tonic-gate 1784*0Sstevel@tonic-gate static void 1785*0Sstevel@tonic-gate milter_abort_filter(m, e) 1786*0Sstevel@tonic-gate struct milter *m; 1787*0Sstevel@tonic-gate ENVELOPE *e; 1788*0Sstevel@tonic-gate { 1789*0Sstevel@tonic-gate if (tTd(64, 10)) 1790*0Sstevel@tonic-gate sm_dprintf("milter_abort_filter(%s)\n", m->mf_name); 1791*0Sstevel@tonic-gate if (MilterLogLevel > 10) 1792*0Sstevel@tonic-gate sm_syslog(LOG_INFO, e->e_id, "Milter (%s): abort filter", 1793*0Sstevel@tonic-gate m->mf_name); 1794*0Sstevel@tonic-gate 1795*0Sstevel@tonic-gate if (m->mf_sock < 0 || 1796*0Sstevel@tonic-gate m->mf_state != SMFS_INMSG) 1797*0Sstevel@tonic-gate return; 1798*0Sstevel@tonic-gate 1799*0Sstevel@tonic-gate (void) milter_write(m, SMFIC_ABORT, (char *) NULL, 0, 1800*0Sstevel@tonic-gate m->mf_timeout[SMFTO_WRITE], e); 1801*0Sstevel@tonic-gate if (m->mf_state != SMFS_ERROR) 1802*0Sstevel@tonic-gate m->mf_state = SMFS_DONE; 1803*0Sstevel@tonic-gate } 1804*0Sstevel@tonic-gate /* 1805*0Sstevel@tonic-gate ** MILTER_SEND_MACROS -- provide macros to the filters 1806*0Sstevel@tonic-gate ** 1807*0Sstevel@tonic-gate ** Parameters: 1808*0Sstevel@tonic-gate ** m -- milter to send macros to. 1809*0Sstevel@tonic-gate ** macros -- macros to send for filter smfi_getsymval(). 1810*0Sstevel@tonic-gate ** cmd -- which command the macros are associated with. 1811*0Sstevel@tonic-gate ** e -- current envelope (for macro access). 1812*0Sstevel@tonic-gate ** 1813*0Sstevel@tonic-gate ** Returns: 1814*0Sstevel@tonic-gate ** none 1815*0Sstevel@tonic-gate */ 1816*0Sstevel@tonic-gate 1817*0Sstevel@tonic-gate static void 1818*0Sstevel@tonic-gate milter_send_macros(m, macros, cmd, e) 1819*0Sstevel@tonic-gate struct milter *m; 1820*0Sstevel@tonic-gate char **macros; 1821*0Sstevel@tonic-gate char cmd; 1822*0Sstevel@tonic-gate ENVELOPE *e; 1823*0Sstevel@tonic-gate { 1824*0Sstevel@tonic-gate int i; 1825*0Sstevel@tonic-gate int mid; 1826*0Sstevel@tonic-gate char *v; 1827*0Sstevel@tonic-gate char *buf, *bp; 1828*0Sstevel@tonic-gate char exp[MAXLINE]; 1829*0Sstevel@tonic-gate ssize_t s; 1830*0Sstevel@tonic-gate 1831*0Sstevel@tonic-gate /* sanity check */ 1832*0Sstevel@tonic-gate if (macros == NULL || macros[0] == NULL) 1833*0Sstevel@tonic-gate return; 1834*0Sstevel@tonic-gate 1835*0Sstevel@tonic-gate /* put together data */ 1836*0Sstevel@tonic-gate s = 1; /* for the command character */ 1837*0Sstevel@tonic-gate for (i = 0; macros[i] != NULL; i++) 1838*0Sstevel@tonic-gate { 1839*0Sstevel@tonic-gate mid = macid(macros[i]); 1840*0Sstevel@tonic-gate if (mid == 0) 1841*0Sstevel@tonic-gate continue; 1842*0Sstevel@tonic-gate v = macvalue(mid, e); 1843*0Sstevel@tonic-gate if (v == NULL) 1844*0Sstevel@tonic-gate continue; 1845*0Sstevel@tonic-gate expand(v, exp, sizeof(exp), e); 1846*0Sstevel@tonic-gate s += strlen(macros[i]) + 1 + strlen(exp) + 1; 1847*0Sstevel@tonic-gate } 1848*0Sstevel@tonic-gate 1849*0Sstevel@tonic-gate if (s < 0) 1850*0Sstevel@tonic-gate return; 1851*0Sstevel@tonic-gate 1852*0Sstevel@tonic-gate buf = (char *) xalloc(s); 1853*0Sstevel@tonic-gate bp = buf; 1854*0Sstevel@tonic-gate *bp++ = cmd; 1855*0Sstevel@tonic-gate for (i = 0; macros[i] != NULL; i++) 1856*0Sstevel@tonic-gate { 1857*0Sstevel@tonic-gate mid = macid(macros[i]); 1858*0Sstevel@tonic-gate if (mid == 0) 1859*0Sstevel@tonic-gate continue; 1860*0Sstevel@tonic-gate v = macvalue(mid, e); 1861*0Sstevel@tonic-gate if (v == NULL) 1862*0Sstevel@tonic-gate continue; 1863*0Sstevel@tonic-gate expand(v, exp, sizeof(exp), e); 1864*0Sstevel@tonic-gate 1865*0Sstevel@tonic-gate if (tTd(64, 10)) 1866*0Sstevel@tonic-gate sm_dprintf("milter_send_macros(%s, %c): %s=%s\n", 1867*0Sstevel@tonic-gate m->mf_name, cmd, macros[i], exp); 1868*0Sstevel@tonic-gate 1869*0Sstevel@tonic-gate (void) sm_strlcpy(bp, macros[i], s - (bp - buf)); 1870*0Sstevel@tonic-gate bp += strlen(bp) + 1; 1871*0Sstevel@tonic-gate (void) sm_strlcpy(bp, exp, s - (bp - buf)); 1872*0Sstevel@tonic-gate bp += strlen(bp) + 1; 1873*0Sstevel@tonic-gate } 1874*0Sstevel@tonic-gate (void) milter_write(m, SMFIC_MACRO, buf, s, 1875*0Sstevel@tonic-gate m->mf_timeout[SMFTO_WRITE], e); 1876*0Sstevel@tonic-gate sm_free(buf); 1877*0Sstevel@tonic-gate } 1878*0Sstevel@tonic-gate 1879*0Sstevel@tonic-gate /* 1880*0Sstevel@tonic-gate ** MILTER_SEND_COMMAND -- send a command and return the response for a filter 1881*0Sstevel@tonic-gate ** 1882*0Sstevel@tonic-gate ** Parameters: 1883*0Sstevel@tonic-gate ** m -- current milter filter 1884*0Sstevel@tonic-gate ** command -- command to send. 1885*0Sstevel@tonic-gate ** data -- optional command data. 1886*0Sstevel@tonic-gate ** sz -- length of buf. 1887*0Sstevel@tonic-gate ** e -- current envelope (for e->e_id). 1888*0Sstevel@tonic-gate ** state -- return state word. 1889*0Sstevel@tonic-gate ** 1890*0Sstevel@tonic-gate ** Returns: 1891*0Sstevel@tonic-gate ** response string (may be NULL) 1892*0Sstevel@tonic-gate */ 1893*0Sstevel@tonic-gate 1894*0Sstevel@tonic-gate static char * 1895*0Sstevel@tonic-gate milter_send_command(m, command, data, sz, e, state) 1896*0Sstevel@tonic-gate struct milter *m; 1897*0Sstevel@tonic-gate char command; 1898*0Sstevel@tonic-gate void *data; 1899*0Sstevel@tonic-gate ssize_t sz; 1900*0Sstevel@tonic-gate ENVELOPE *e; 1901*0Sstevel@tonic-gate char *state; 1902*0Sstevel@tonic-gate { 1903*0Sstevel@tonic-gate char rcmd; 1904*0Sstevel@tonic-gate ssize_t rlen; 1905*0Sstevel@tonic-gate unsigned long skipflag; 1906*0Sstevel@tonic-gate #if _FFR_MILTER_NOHDR_RESP 1907*0Sstevel@tonic-gate unsigned long norespflag = 0; 1908*0Sstevel@tonic-gate #endif /* _FFR_MILTER_NOHDR_RESP */ 1909*0Sstevel@tonic-gate char *action; 1910*0Sstevel@tonic-gate char *defresponse; 1911*0Sstevel@tonic-gate char *response; 1912*0Sstevel@tonic-gate 1913*0Sstevel@tonic-gate if (tTd(64, 10)) 1914*0Sstevel@tonic-gate sm_dprintf("milter_send_command(%s): cmd %c len %ld\n", 1915*0Sstevel@tonic-gate m->mf_name, (char) command, (long) sz); 1916*0Sstevel@tonic-gate 1917*0Sstevel@tonic-gate /* find skip flag and default failure */ 1918*0Sstevel@tonic-gate switch (command) 1919*0Sstevel@tonic-gate { 1920*0Sstevel@tonic-gate case SMFIC_CONNECT: 1921*0Sstevel@tonic-gate skipflag = SMFIP_NOCONNECT; 1922*0Sstevel@tonic-gate action = "connect"; 1923*0Sstevel@tonic-gate defresponse = "554 Command rejected"; 1924*0Sstevel@tonic-gate break; 1925*0Sstevel@tonic-gate 1926*0Sstevel@tonic-gate case SMFIC_HELO: 1927*0Sstevel@tonic-gate skipflag = SMFIP_NOHELO; 1928*0Sstevel@tonic-gate action = "helo"; 1929*0Sstevel@tonic-gate defresponse = "550 Command rejected"; 1930*0Sstevel@tonic-gate break; 1931*0Sstevel@tonic-gate 1932*0Sstevel@tonic-gate case SMFIC_MAIL: 1933*0Sstevel@tonic-gate skipflag = SMFIP_NOMAIL; 1934*0Sstevel@tonic-gate action = "mail"; 1935*0Sstevel@tonic-gate defresponse = "550 5.7.1 Command rejected"; 1936*0Sstevel@tonic-gate break; 1937*0Sstevel@tonic-gate 1938*0Sstevel@tonic-gate case SMFIC_RCPT: 1939*0Sstevel@tonic-gate skipflag = SMFIP_NORCPT; 1940*0Sstevel@tonic-gate action = "rcpt"; 1941*0Sstevel@tonic-gate defresponse = "550 5.7.1 Command rejected"; 1942*0Sstevel@tonic-gate break; 1943*0Sstevel@tonic-gate 1944*0Sstevel@tonic-gate case SMFIC_HEADER: 1945*0Sstevel@tonic-gate skipflag = SMFIP_NOHDRS; 1946*0Sstevel@tonic-gate #if _FFR_MILTER_NOHDR_RESP 1947*0Sstevel@tonic-gate norespflag = SMFIP_NOHREPL; 1948*0Sstevel@tonic-gate #endif /* _FFR_MILTER_NOHDR_RESP */ 1949*0Sstevel@tonic-gate action = "header"; 1950*0Sstevel@tonic-gate defresponse = "550 5.7.1 Command rejected"; 1951*0Sstevel@tonic-gate break; 1952*0Sstevel@tonic-gate 1953*0Sstevel@tonic-gate case SMFIC_BODY: 1954*0Sstevel@tonic-gate skipflag = SMFIP_NOBODY; 1955*0Sstevel@tonic-gate action = "body"; 1956*0Sstevel@tonic-gate defresponse = "554 5.7.1 Command rejected"; 1957*0Sstevel@tonic-gate break; 1958*0Sstevel@tonic-gate 1959*0Sstevel@tonic-gate case SMFIC_EOH: 1960*0Sstevel@tonic-gate skipflag = SMFIP_NOEOH; 1961*0Sstevel@tonic-gate action = "eoh"; 1962*0Sstevel@tonic-gate defresponse = "550 5.7.1 Command rejected"; 1963*0Sstevel@tonic-gate break; 1964*0Sstevel@tonic-gate 1965*0Sstevel@tonic-gate #if SMFI_VERSION > 2 1966*0Sstevel@tonic-gate case SMFIC_UNKNOWN: 1967*0Sstevel@tonic-gate action = "unknown"; 1968*0Sstevel@tonic-gate defresponse = "550 5.7.1 Command rejected"; 1969*0Sstevel@tonic-gate break; 1970*0Sstevel@tonic-gate #endif /* SMFI_VERSION > 2 */ 1971*0Sstevel@tonic-gate 1972*0Sstevel@tonic-gate case SMFIC_BODYEOB: 1973*0Sstevel@tonic-gate case SMFIC_OPTNEG: 1974*0Sstevel@tonic-gate case SMFIC_MACRO: 1975*0Sstevel@tonic-gate case SMFIC_ABORT: 1976*0Sstevel@tonic-gate case SMFIC_QUIT: 1977*0Sstevel@tonic-gate /* NOTE: not handled by milter_send_command() */ 1978*0Sstevel@tonic-gate /* FALLTHROUGH */ 1979*0Sstevel@tonic-gate 1980*0Sstevel@tonic-gate default: 1981*0Sstevel@tonic-gate skipflag = 0; 1982*0Sstevel@tonic-gate action = "default"; 1983*0Sstevel@tonic-gate defresponse = "550 5.7.1 Command rejected"; 1984*0Sstevel@tonic-gate break; 1985*0Sstevel@tonic-gate } 1986*0Sstevel@tonic-gate 1987*0Sstevel@tonic-gate /* check if filter wants this command */ 1988*0Sstevel@tonic-gate if (skipflag != 0 && 1989*0Sstevel@tonic-gate bitset(skipflag, m->mf_pflags)) 1990*0Sstevel@tonic-gate return NULL; 1991*0Sstevel@tonic-gate 1992*0Sstevel@tonic-gate /* send the command to the filter */ 1993*0Sstevel@tonic-gate (void) milter_write(m, command, data, sz, 1994*0Sstevel@tonic-gate m->mf_timeout[SMFTO_WRITE], e); 1995*0Sstevel@tonic-gate if (m->mf_state == SMFS_ERROR) 1996*0Sstevel@tonic-gate { 1997*0Sstevel@tonic-gate MILTER_CHECK_ERROR(false, return NULL); 1998*0Sstevel@tonic-gate return NULL; 1999*0Sstevel@tonic-gate } 2000*0Sstevel@tonic-gate 2001*0Sstevel@tonic-gate #if _FFR_MILTER_NOHDR_RESP 2002*0Sstevel@tonic-gate /* check if filter sends response to this command */ 2003*0Sstevel@tonic-gate if (norespflag != 0 && bitset(norespflag, m->mf_pflags)) 2004*0Sstevel@tonic-gate return NULL; 2005*0Sstevel@tonic-gate #endif /* _FFR_MILTER_NOHDR_RESP */ 2006*0Sstevel@tonic-gate 2007*0Sstevel@tonic-gate /* get the response from the filter */ 2008*0Sstevel@tonic-gate response = milter_read(m, &rcmd, &rlen, 2009*0Sstevel@tonic-gate m->mf_timeout[SMFTO_READ], e); 2010*0Sstevel@tonic-gate if (m->mf_state == SMFS_ERROR) 2011*0Sstevel@tonic-gate { 2012*0Sstevel@tonic-gate MILTER_CHECK_ERROR(false, return NULL); 2013*0Sstevel@tonic-gate return NULL; 2014*0Sstevel@tonic-gate } 2015*0Sstevel@tonic-gate 2016*0Sstevel@tonic-gate if (tTd(64, 10)) 2017*0Sstevel@tonic-gate sm_dprintf("milter_send_command(%s): returned %c\n", 2018*0Sstevel@tonic-gate m->mf_name, (char) rcmd); 2019*0Sstevel@tonic-gate 2020*0Sstevel@tonic-gate switch (rcmd) 2021*0Sstevel@tonic-gate { 2022*0Sstevel@tonic-gate case SMFIR_REPLYCODE: 2023*0Sstevel@tonic-gate MILTER_CHECK_REPLYCODE(defresponse); 2024*0Sstevel@tonic-gate if (MilterLogLevel > 10) 2025*0Sstevel@tonic-gate sm_syslog(LOG_INFO, e->e_id, "milter=%s, action=%s, reject=%s", 2026*0Sstevel@tonic-gate m->mf_name, action, response); 2027*0Sstevel@tonic-gate *state = rcmd; 2028*0Sstevel@tonic-gate break; 2029*0Sstevel@tonic-gate 2030*0Sstevel@tonic-gate case SMFIR_REJECT: 2031*0Sstevel@tonic-gate if (MilterLogLevel > 10) 2032*0Sstevel@tonic-gate sm_syslog(LOG_INFO, e->e_id, "milter=%s, action=%s, reject", 2033*0Sstevel@tonic-gate m->mf_name, action); 2034*0Sstevel@tonic-gate *state = rcmd; 2035*0Sstevel@tonic-gate break; 2036*0Sstevel@tonic-gate 2037*0Sstevel@tonic-gate case SMFIR_DISCARD: 2038*0Sstevel@tonic-gate if (MilterLogLevel > 10) 2039*0Sstevel@tonic-gate sm_syslog(LOG_INFO, e->e_id, "milter=%s, action=%s, discard", 2040*0Sstevel@tonic-gate m->mf_name, action); 2041*0Sstevel@tonic-gate *state = rcmd; 2042*0Sstevel@tonic-gate break; 2043*0Sstevel@tonic-gate 2044*0Sstevel@tonic-gate case SMFIR_TEMPFAIL: 2045*0Sstevel@tonic-gate if (MilterLogLevel > 10) 2046*0Sstevel@tonic-gate sm_syslog(LOG_INFO, e->e_id, "milter=%s, action=%s, tempfail", 2047*0Sstevel@tonic-gate m->mf_name, action); 2048*0Sstevel@tonic-gate *state = rcmd; 2049*0Sstevel@tonic-gate break; 2050*0Sstevel@tonic-gate 2051*0Sstevel@tonic-gate case SMFIR_ACCEPT: 2052*0Sstevel@tonic-gate /* this filter is done with message/connection */ 2053*0Sstevel@tonic-gate if (command == SMFIC_HELO || 2054*0Sstevel@tonic-gate command == SMFIC_CONNECT) 2055*0Sstevel@tonic-gate m->mf_state = SMFS_CLOSABLE; 2056*0Sstevel@tonic-gate else 2057*0Sstevel@tonic-gate m->mf_state = SMFS_DONE; 2058*0Sstevel@tonic-gate if (MilterLogLevel > 10) 2059*0Sstevel@tonic-gate sm_syslog(LOG_INFO, e->e_id, "milter=%s, action=%s, accepted", 2060*0Sstevel@tonic-gate m->mf_name, action); 2061*0Sstevel@tonic-gate break; 2062*0Sstevel@tonic-gate 2063*0Sstevel@tonic-gate case SMFIR_CONTINUE: 2064*0Sstevel@tonic-gate /* if MAIL command is ok, filter is in message state */ 2065*0Sstevel@tonic-gate if (command == SMFIC_MAIL) 2066*0Sstevel@tonic-gate m->mf_state = SMFS_INMSG; 2067*0Sstevel@tonic-gate if (MilterLogLevel > 12) 2068*0Sstevel@tonic-gate sm_syslog(LOG_INFO, e->e_id, "milter=%s, action=%s, continue", 2069*0Sstevel@tonic-gate m->mf_name, action); 2070*0Sstevel@tonic-gate break; 2071*0Sstevel@tonic-gate 2072*0Sstevel@tonic-gate default: 2073*0Sstevel@tonic-gate /* Invalid response to command */ 2074*0Sstevel@tonic-gate if (MilterLogLevel > 0) 2075*0Sstevel@tonic-gate sm_syslog(LOG_ERR, e->e_id, 2076*0Sstevel@tonic-gate "milter_send_command(%s): action=%s returned bogus response %c", 2077*0Sstevel@tonic-gate m->mf_name, action, rcmd); 2078*0Sstevel@tonic-gate milter_error(m, e); 2079*0Sstevel@tonic-gate break; 2080*0Sstevel@tonic-gate } 2081*0Sstevel@tonic-gate 2082*0Sstevel@tonic-gate if (*state != SMFIR_REPLYCODE && 2083*0Sstevel@tonic-gate response != NULL) 2084*0Sstevel@tonic-gate { 2085*0Sstevel@tonic-gate sm_free(response); /* XXX */ 2086*0Sstevel@tonic-gate response = NULL; 2087*0Sstevel@tonic-gate } 2088*0Sstevel@tonic-gate return response; 2089*0Sstevel@tonic-gate } 2090*0Sstevel@tonic-gate 2091*0Sstevel@tonic-gate /* 2092*0Sstevel@tonic-gate ** MILTER_COMMAND -- send a command and return the response for each filter 2093*0Sstevel@tonic-gate ** 2094*0Sstevel@tonic-gate ** Parameters: 2095*0Sstevel@tonic-gate ** command -- command to send. 2096*0Sstevel@tonic-gate ** data -- optional command data. 2097*0Sstevel@tonic-gate ** sz -- length of buf. 2098*0Sstevel@tonic-gate ** macros -- macros to send for filter smfi_getsymval(). 2099*0Sstevel@tonic-gate ** e -- current envelope (for macro access). 2100*0Sstevel@tonic-gate ** state -- return state word. 2101*0Sstevel@tonic-gate ** 2102*0Sstevel@tonic-gate ** Returns: 2103*0Sstevel@tonic-gate ** response string (may be NULL) 2104*0Sstevel@tonic-gate */ 2105*0Sstevel@tonic-gate 2106*0Sstevel@tonic-gate static char * 2107*0Sstevel@tonic-gate milter_command(command, data, sz, macros, e, state) 2108*0Sstevel@tonic-gate char command; 2109*0Sstevel@tonic-gate void *data; 2110*0Sstevel@tonic-gate ssize_t sz; 2111*0Sstevel@tonic-gate char **macros; 2112*0Sstevel@tonic-gate ENVELOPE *e; 2113*0Sstevel@tonic-gate char *state; 2114*0Sstevel@tonic-gate { 2115*0Sstevel@tonic-gate int i; 2116*0Sstevel@tonic-gate char *response = NULL; 2117*0Sstevel@tonic-gate time_t tn = 0; 2118*0Sstevel@tonic-gate 2119*0Sstevel@tonic-gate if (tTd(64, 10)) 2120*0Sstevel@tonic-gate sm_dprintf("milter_command: cmd %c len %ld\n", 2121*0Sstevel@tonic-gate (char) command, (long) sz); 2122*0Sstevel@tonic-gate 2123*0Sstevel@tonic-gate *state = SMFIR_CONTINUE; 2124*0Sstevel@tonic-gate for (i = 0; InputFilters[i] != NULL; i++) 2125*0Sstevel@tonic-gate { 2126*0Sstevel@tonic-gate struct milter *m = InputFilters[i]; 2127*0Sstevel@tonic-gate 2128*0Sstevel@tonic-gate /* previous problem? */ 2129*0Sstevel@tonic-gate if (m->mf_state == SMFS_ERROR) 2130*0Sstevel@tonic-gate { 2131*0Sstevel@tonic-gate MILTER_CHECK_ERROR(false, continue); 2132*0Sstevel@tonic-gate break; 2133*0Sstevel@tonic-gate } 2134*0Sstevel@tonic-gate 2135*0Sstevel@tonic-gate /* sanity check */ 2136*0Sstevel@tonic-gate if (m->mf_sock < 0 || 2137*0Sstevel@tonic-gate (m->mf_state != SMFS_OPEN && m->mf_state != SMFS_INMSG)) 2138*0Sstevel@tonic-gate continue; 2139*0Sstevel@tonic-gate 2140*0Sstevel@tonic-gate /* send macros (regardless of whether we send command) */ 2141*0Sstevel@tonic-gate if (macros != NULL && macros[0] != NULL) 2142*0Sstevel@tonic-gate { 2143*0Sstevel@tonic-gate milter_send_macros(m, macros, command, e); 2144*0Sstevel@tonic-gate if (m->mf_state == SMFS_ERROR) 2145*0Sstevel@tonic-gate { 2146*0Sstevel@tonic-gate MILTER_CHECK_ERROR(false, continue); 2147*0Sstevel@tonic-gate break; 2148*0Sstevel@tonic-gate } 2149*0Sstevel@tonic-gate } 2150*0Sstevel@tonic-gate 2151*0Sstevel@tonic-gate if (MilterLogLevel > 21) 2152*0Sstevel@tonic-gate tn = curtime(); 2153*0Sstevel@tonic-gate 2154*0Sstevel@tonic-gate response = milter_send_command(m, command, data, sz, e, state); 2155*0Sstevel@tonic-gate 2156*0Sstevel@tonic-gate if (MilterLogLevel > 21) 2157*0Sstevel@tonic-gate { 2158*0Sstevel@tonic-gate /* log the time it took for the command per filter */ 2159*0Sstevel@tonic-gate sm_syslog(LOG_INFO, e->e_id, 2160*0Sstevel@tonic-gate "Milter (%s): time command (%c), %d", 2161*0Sstevel@tonic-gate m->mf_name, command, (int) (tn - curtime())); 2162*0Sstevel@tonic-gate } 2163*0Sstevel@tonic-gate 2164*0Sstevel@tonic-gate if (*state != SMFIR_CONTINUE) 2165*0Sstevel@tonic-gate break; 2166*0Sstevel@tonic-gate } 2167*0Sstevel@tonic-gate return response; 2168*0Sstevel@tonic-gate } 2169*0Sstevel@tonic-gate /* 2170*0Sstevel@tonic-gate ** MILTER_NEGOTIATE -- get version and flags from filter 2171*0Sstevel@tonic-gate ** 2172*0Sstevel@tonic-gate ** Parameters: 2173*0Sstevel@tonic-gate ** m -- milter filter structure. 2174*0Sstevel@tonic-gate ** e -- current envelope. 2175*0Sstevel@tonic-gate ** 2176*0Sstevel@tonic-gate ** Returns: 2177*0Sstevel@tonic-gate ** 0 on success, -1 otherwise 2178*0Sstevel@tonic-gate */ 2179*0Sstevel@tonic-gate 2180*0Sstevel@tonic-gate static int 2181*0Sstevel@tonic-gate milter_negotiate(m, e) 2182*0Sstevel@tonic-gate struct milter *m; 2183*0Sstevel@tonic-gate ENVELOPE *e; 2184*0Sstevel@tonic-gate { 2185*0Sstevel@tonic-gate char rcmd; 2186*0Sstevel@tonic-gate mi_int32 fvers; 2187*0Sstevel@tonic-gate mi_int32 fflags; 2188*0Sstevel@tonic-gate mi_int32 pflags; 2189*0Sstevel@tonic-gate char *response; 2190*0Sstevel@tonic-gate ssize_t rlen; 2191*0Sstevel@tonic-gate char data[MILTER_OPTLEN]; 2192*0Sstevel@tonic-gate 2193*0Sstevel@tonic-gate /* sanity check */ 2194*0Sstevel@tonic-gate if (m->mf_sock < 0 || m->mf_state != SMFS_OPEN) 2195*0Sstevel@tonic-gate { 2196*0Sstevel@tonic-gate if (MilterLogLevel > 0) 2197*0Sstevel@tonic-gate sm_syslog(LOG_ERR, e->e_id, 2198*0Sstevel@tonic-gate "Milter (%s): negotiate, impossible state", 2199*0Sstevel@tonic-gate m->mf_name); 2200*0Sstevel@tonic-gate milter_error(m, e); 2201*0Sstevel@tonic-gate return -1; 2202*0Sstevel@tonic-gate } 2203*0Sstevel@tonic-gate 2204*0Sstevel@tonic-gate fvers = htonl(SMFI_VERSION); 2205*0Sstevel@tonic-gate fflags = htonl(SMFI_CURR_ACTS); 2206*0Sstevel@tonic-gate pflags = htonl(SMFI_CURR_PROT); 2207*0Sstevel@tonic-gate (void) memcpy(data, (char *) &fvers, MILTER_LEN_BYTES); 2208*0Sstevel@tonic-gate (void) memcpy(data + MILTER_LEN_BYTES, 2209*0Sstevel@tonic-gate (char *) &fflags, MILTER_LEN_BYTES); 2210*0Sstevel@tonic-gate (void) memcpy(data + (MILTER_LEN_BYTES * 2), 2211*0Sstevel@tonic-gate (char *) &pflags, MILTER_LEN_BYTES); 2212*0Sstevel@tonic-gate (void) milter_write(m, SMFIC_OPTNEG, data, sizeof data, 2213*0Sstevel@tonic-gate m->mf_timeout[SMFTO_WRITE], e); 2214*0Sstevel@tonic-gate 2215*0Sstevel@tonic-gate if (m->mf_state == SMFS_ERROR) 2216*0Sstevel@tonic-gate return -1; 2217*0Sstevel@tonic-gate 2218*0Sstevel@tonic-gate response = milter_read(m, &rcmd, &rlen, m->mf_timeout[SMFTO_READ], e); 2219*0Sstevel@tonic-gate if (m->mf_state == SMFS_ERROR) 2220*0Sstevel@tonic-gate return -1; 2221*0Sstevel@tonic-gate 2222*0Sstevel@tonic-gate if (rcmd != SMFIC_OPTNEG) 2223*0Sstevel@tonic-gate { 2224*0Sstevel@tonic-gate if (tTd(64, 5)) 2225*0Sstevel@tonic-gate sm_dprintf("milter_negotiate(%s): returned %c instead of %c\n", 2226*0Sstevel@tonic-gate m->mf_name, rcmd, SMFIC_OPTNEG); 2227*0Sstevel@tonic-gate if (MilterLogLevel > 0) 2228*0Sstevel@tonic-gate sm_syslog(LOG_ERR, e->e_id, 2229*0Sstevel@tonic-gate "Milter (%s): negotiate: returned %c instead of %c", 2230*0Sstevel@tonic-gate m->mf_name, rcmd, SMFIC_OPTNEG); 2231*0Sstevel@tonic-gate if (response != NULL) 2232*0Sstevel@tonic-gate sm_free(response); /* XXX */ 2233*0Sstevel@tonic-gate milter_error(m, e); 2234*0Sstevel@tonic-gate return -1; 2235*0Sstevel@tonic-gate } 2236*0Sstevel@tonic-gate 2237*0Sstevel@tonic-gate /* Make sure we have enough bytes for the version */ 2238*0Sstevel@tonic-gate if (response == NULL || rlen < MILTER_LEN_BYTES) 2239*0Sstevel@tonic-gate { 2240*0Sstevel@tonic-gate if (tTd(64, 5)) 2241*0Sstevel@tonic-gate sm_dprintf("milter_negotiate(%s): did not return valid info\n", 2242*0Sstevel@tonic-gate m->mf_name); 2243*0Sstevel@tonic-gate if (MilterLogLevel > 0) 2244*0Sstevel@tonic-gate sm_syslog(LOG_ERR, e->e_id, 2245*0Sstevel@tonic-gate "Milter (%s): negotiate: did not return valid info", 2246*0Sstevel@tonic-gate m->mf_name); 2247*0Sstevel@tonic-gate if (response != NULL) 2248*0Sstevel@tonic-gate sm_free(response); /* XXX */ 2249*0Sstevel@tonic-gate milter_error(m, e); 2250*0Sstevel@tonic-gate return -1; 2251*0Sstevel@tonic-gate } 2252*0Sstevel@tonic-gate 2253*0Sstevel@tonic-gate /* extract information */ 2254*0Sstevel@tonic-gate (void) memcpy((char *) &fvers, response, MILTER_LEN_BYTES); 2255*0Sstevel@tonic-gate 2256*0Sstevel@tonic-gate /* Now make sure we have enough for the feature bitmap */ 2257*0Sstevel@tonic-gate if (rlen != MILTER_OPTLEN) 2258*0Sstevel@tonic-gate { 2259*0Sstevel@tonic-gate if (tTd(64, 5)) 2260*0Sstevel@tonic-gate sm_dprintf("milter_negotiate(%s): did not return enough info\n", 2261*0Sstevel@tonic-gate m->mf_name); 2262*0Sstevel@tonic-gate if (MilterLogLevel > 0) 2263*0Sstevel@tonic-gate sm_syslog(LOG_ERR, e->e_id, 2264*0Sstevel@tonic-gate "Milter (%s): negotiate: did not return enough info", 2265*0Sstevel@tonic-gate m->mf_name); 2266*0Sstevel@tonic-gate if (response != NULL) 2267*0Sstevel@tonic-gate sm_free(response); /* XXX */ 2268*0Sstevel@tonic-gate milter_error(m, e); 2269*0Sstevel@tonic-gate return -1; 2270*0Sstevel@tonic-gate } 2271*0Sstevel@tonic-gate 2272*0Sstevel@tonic-gate (void) memcpy((char *) &fflags, response + MILTER_LEN_BYTES, 2273*0Sstevel@tonic-gate MILTER_LEN_BYTES); 2274*0Sstevel@tonic-gate (void) memcpy((char *) &pflags, response + (MILTER_LEN_BYTES * 2), 2275*0Sstevel@tonic-gate MILTER_LEN_BYTES); 2276*0Sstevel@tonic-gate sm_free(response); /* XXX */ 2277*0Sstevel@tonic-gate response = NULL; 2278*0Sstevel@tonic-gate 2279*0Sstevel@tonic-gate m->mf_fvers = ntohl(fvers); 2280*0Sstevel@tonic-gate m->mf_fflags = ntohl(fflags); 2281*0Sstevel@tonic-gate m->mf_pflags = ntohl(pflags); 2282*0Sstevel@tonic-gate 2283*0Sstevel@tonic-gate /* check for version compatibility */ 2284*0Sstevel@tonic-gate if (m->mf_fvers == 1 || 2285*0Sstevel@tonic-gate m->mf_fvers > SMFI_VERSION) 2286*0Sstevel@tonic-gate { 2287*0Sstevel@tonic-gate if (tTd(64, 5)) 2288*0Sstevel@tonic-gate sm_dprintf("milter_negotiate(%s): version %d != MTA milter version %d\n", 2289*0Sstevel@tonic-gate m->mf_name, m->mf_fvers, SMFI_VERSION); 2290*0Sstevel@tonic-gate if (MilterLogLevel > 0) 2291*0Sstevel@tonic-gate sm_syslog(LOG_ERR, e->e_id, 2292*0Sstevel@tonic-gate "Milter (%s): negotiate: version %d != MTA milter version %d", 2293*0Sstevel@tonic-gate m->mf_name, m->mf_fvers, SMFI_VERSION); 2294*0Sstevel@tonic-gate milter_error(m, e); 2295*0Sstevel@tonic-gate return -1; 2296*0Sstevel@tonic-gate } 2297*0Sstevel@tonic-gate 2298*0Sstevel@tonic-gate /* check for filter feature mismatch */ 2299*0Sstevel@tonic-gate if ((m->mf_fflags & SMFI_CURR_ACTS) != m->mf_fflags) 2300*0Sstevel@tonic-gate { 2301*0Sstevel@tonic-gate if (tTd(64, 5)) 2302*0Sstevel@tonic-gate sm_dprintf("milter_negotiate(%s): filter abilities 0x%x != MTA milter abilities 0x%lx\n", 2303*0Sstevel@tonic-gate m->mf_name, m->mf_fflags, 2304*0Sstevel@tonic-gate SMFI_CURR_ACTS); 2305*0Sstevel@tonic-gate if (MilterLogLevel > 0) 2306*0Sstevel@tonic-gate sm_syslog(LOG_ERR, e->e_id, 2307*0Sstevel@tonic-gate "Milter (%s): negotiate: filter abilities 0x%x != MTA milter abilities 0x%lx", 2308*0Sstevel@tonic-gate m->mf_name, m->mf_fflags, 2309*0Sstevel@tonic-gate (unsigned long) SMFI_CURR_ACTS); 2310*0Sstevel@tonic-gate milter_error(m, e); 2311*0Sstevel@tonic-gate return -1; 2312*0Sstevel@tonic-gate } 2313*0Sstevel@tonic-gate 2314*0Sstevel@tonic-gate /* check for protocol feature mismatch */ 2315*0Sstevel@tonic-gate if ((m->mf_pflags & SMFI_CURR_PROT) != m->mf_pflags) 2316*0Sstevel@tonic-gate { 2317*0Sstevel@tonic-gate if (tTd(64, 5)) 2318*0Sstevel@tonic-gate sm_dprintf("milter_negotiate(%s): protocol abilities 0x%x != MTA milter abilities 0x%lx\n", 2319*0Sstevel@tonic-gate m->mf_name, m->mf_pflags, 2320*0Sstevel@tonic-gate (unsigned long) SMFI_CURR_PROT); 2321*0Sstevel@tonic-gate if (MilterLogLevel > 0) 2322*0Sstevel@tonic-gate sm_syslog(LOG_ERR, e->e_id, 2323*0Sstevel@tonic-gate "Milter (%s): negotiate: protocol abilities 0x%x != MTA milter abilities 0x%lx", 2324*0Sstevel@tonic-gate m->mf_name, m->mf_pflags, 2325*0Sstevel@tonic-gate (unsigned long) SMFI_CURR_PROT); 2326*0Sstevel@tonic-gate milter_error(m, e); 2327*0Sstevel@tonic-gate return -1; 2328*0Sstevel@tonic-gate } 2329*0Sstevel@tonic-gate 2330*0Sstevel@tonic-gate if (tTd(64, 5)) 2331*0Sstevel@tonic-gate sm_dprintf("milter_negotiate(%s): version %u, fflags 0x%x, pflags 0x%x\n", 2332*0Sstevel@tonic-gate m->mf_name, m->mf_fvers, m->mf_fflags, m->mf_pflags); 2333*0Sstevel@tonic-gate return 0; 2334*0Sstevel@tonic-gate } 2335*0Sstevel@tonic-gate /* 2336*0Sstevel@tonic-gate ** MILTER_PER_CONNECTION_CHECK -- checks on per-connection commands 2337*0Sstevel@tonic-gate ** 2338*0Sstevel@tonic-gate ** Reduce code duplication by putting these checks in one place 2339*0Sstevel@tonic-gate ** 2340*0Sstevel@tonic-gate ** Parameters: 2341*0Sstevel@tonic-gate ** e -- current envelope. 2342*0Sstevel@tonic-gate ** 2343*0Sstevel@tonic-gate ** Returns: 2344*0Sstevel@tonic-gate ** none 2345*0Sstevel@tonic-gate */ 2346*0Sstevel@tonic-gate 2347*0Sstevel@tonic-gate static void 2348*0Sstevel@tonic-gate milter_per_connection_check(e) 2349*0Sstevel@tonic-gate ENVELOPE *e; 2350*0Sstevel@tonic-gate { 2351*0Sstevel@tonic-gate int i; 2352*0Sstevel@tonic-gate 2353*0Sstevel@tonic-gate /* see if we are done with any of the filters */ 2354*0Sstevel@tonic-gate for (i = 0; InputFilters[i] != NULL; i++) 2355*0Sstevel@tonic-gate { 2356*0Sstevel@tonic-gate struct milter *m = InputFilters[i]; 2357*0Sstevel@tonic-gate 2358*0Sstevel@tonic-gate if (m->mf_state == SMFS_CLOSABLE) 2359*0Sstevel@tonic-gate milter_quit_filter(m, e); 2360*0Sstevel@tonic-gate } 2361*0Sstevel@tonic-gate } 2362*0Sstevel@tonic-gate /* 2363*0Sstevel@tonic-gate ** MILTER_ERROR -- Put a milter filter into error state 2364*0Sstevel@tonic-gate ** 2365*0Sstevel@tonic-gate ** Parameters: 2366*0Sstevel@tonic-gate ** m -- the broken filter. 2367*0Sstevel@tonic-gate ** e -- current envelope. 2368*0Sstevel@tonic-gate ** 2369*0Sstevel@tonic-gate ** Returns: 2370*0Sstevel@tonic-gate ** none 2371*0Sstevel@tonic-gate */ 2372*0Sstevel@tonic-gate 2373*0Sstevel@tonic-gate static void 2374*0Sstevel@tonic-gate milter_error(m, e) 2375*0Sstevel@tonic-gate struct milter *m; 2376*0Sstevel@tonic-gate ENVELOPE *e; 2377*0Sstevel@tonic-gate { 2378*0Sstevel@tonic-gate /* 2379*0Sstevel@tonic-gate ** We could send a quit here but we may have gotten here due to 2380*0Sstevel@tonic-gate ** an I/O error so we don't want to try to make things worse. 2381*0Sstevel@tonic-gate */ 2382*0Sstevel@tonic-gate 2383*0Sstevel@tonic-gate if (m->mf_sock >= 0) 2384*0Sstevel@tonic-gate { 2385*0Sstevel@tonic-gate (void) close(m->mf_sock); 2386*0Sstevel@tonic-gate m->mf_sock = -1; 2387*0Sstevel@tonic-gate } 2388*0Sstevel@tonic-gate m->mf_state = SMFS_ERROR; 2389*0Sstevel@tonic-gate 2390*0Sstevel@tonic-gate if (MilterLogLevel > 0) 2391*0Sstevel@tonic-gate sm_syslog(LOG_INFO, e->e_id, "Milter (%s): to error state", 2392*0Sstevel@tonic-gate m->mf_name); 2393*0Sstevel@tonic-gate } 2394*0Sstevel@tonic-gate /* 2395*0Sstevel@tonic-gate ** MILTER_HEADERS -- send headers to a single milter filter 2396*0Sstevel@tonic-gate ** 2397*0Sstevel@tonic-gate ** Parameters: 2398*0Sstevel@tonic-gate ** m -- current filter. 2399*0Sstevel@tonic-gate ** e -- current envelope. 2400*0Sstevel@tonic-gate ** state -- return state from response. 2401*0Sstevel@tonic-gate ** 2402*0Sstevel@tonic-gate ** Returns: 2403*0Sstevel@tonic-gate ** response string (may be NULL) 2404*0Sstevel@tonic-gate */ 2405*0Sstevel@tonic-gate 2406*0Sstevel@tonic-gate static char * 2407*0Sstevel@tonic-gate milter_headers(m, e, state) 2408*0Sstevel@tonic-gate struct milter *m; 2409*0Sstevel@tonic-gate ENVELOPE *e; 2410*0Sstevel@tonic-gate char *state; 2411*0Sstevel@tonic-gate { 2412*0Sstevel@tonic-gate char *response = NULL; 2413*0Sstevel@tonic-gate HDR *h; 2414*0Sstevel@tonic-gate 2415*0Sstevel@tonic-gate if (MilterLogLevel > 17) 2416*0Sstevel@tonic-gate sm_syslog(LOG_INFO, e->e_id, "Milter (%s): headers, send", 2417*0Sstevel@tonic-gate m->mf_name); 2418*0Sstevel@tonic-gate 2419*0Sstevel@tonic-gate for (h = e->e_header; h != NULL; h = h->h_link) 2420*0Sstevel@tonic-gate { 2421*0Sstevel@tonic-gate char *buf; 2422*0Sstevel@tonic-gate ssize_t s; 2423*0Sstevel@tonic-gate 2424*0Sstevel@tonic-gate /* don't send over deleted headers */ 2425*0Sstevel@tonic-gate if (h->h_value == NULL) 2426*0Sstevel@tonic-gate { 2427*0Sstevel@tonic-gate /* strip H_USER so not counted in milter_changeheader() */ 2428*0Sstevel@tonic-gate h->h_flags &= ~H_USER; 2429*0Sstevel@tonic-gate continue; 2430*0Sstevel@tonic-gate } 2431*0Sstevel@tonic-gate 2432*0Sstevel@tonic-gate /* skip auto-generated */ 2433*0Sstevel@tonic-gate if (!bitset(H_USER, h->h_flags)) 2434*0Sstevel@tonic-gate continue; 2435*0Sstevel@tonic-gate 2436*0Sstevel@tonic-gate if (tTd(64, 10)) 2437*0Sstevel@tonic-gate sm_dprintf("milter_headers: %s: %s\n", 2438*0Sstevel@tonic-gate h->h_field, h->h_value); 2439*0Sstevel@tonic-gate if (MilterLogLevel > 21) 2440*0Sstevel@tonic-gate sm_syslog(LOG_INFO, e->e_id, "Milter (%s): header, %s", 2441*0Sstevel@tonic-gate m->mf_name, h->h_field); 2442*0Sstevel@tonic-gate 2443*0Sstevel@tonic-gate s = strlen(h->h_field) + 1 + strlen(h->h_value) + 1; 2444*0Sstevel@tonic-gate if (s < 0) 2445*0Sstevel@tonic-gate continue; 2446*0Sstevel@tonic-gate buf = (char *) xalloc(s); 2447*0Sstevel@tonic-gate (void) sm_snprintf(buf, s, "%s%c%s", 2448*0Sstevel@tonic-gate h->h_field, '\0', h->h_value); 2449*0Sstevel@tonic-gate 2450*0Sstevel@tonic-gate /* send it over */ 2451*0Sstevel@tonic-gate response = milter_send_command(m, SMFIC_HEADER, buf, 2452*0Sstevel@tonic-gate s, e, state); 2453*0Sstevel@tonic-gate sm_free(buf); /* XXX */ 2454*0Sstevel@tonic-gate if (m->mf_state == SMFS_ERROR || 2455*0Sstevel@tonic-gate m->mf_state == SMFS_DONE || 2456*0Sstevel@tonic-gate *state != SMFIR_CONTINUE) 2457*0Sstevel@tonic-gate break; 2458*0Sstevel@tonic-gate } 2459*0Sstevel@tonic-gate if (MilterLogLevel > 17) 2460*0Sstevel@tonic-gate sm_syslog(LOG_INFO, e->e_id, "Milter (%s): headers, sent", 2461*0Sstevel@tonic-gate m->mf_name); 2462*0Sstevel@tonic-gate return response; 2463*0Sstevel@tonic-gate } 2464*0Sstevel@tonic-gate /* 2465*0Sstevel@tonic-gate ** MILTER_BODY -- send the body to a filter 2466*0Sstevel@tonic-gate ** 2467*0Sstevel@tonic-gate ** Parameters: 2468*0Sstevel@tonic-gate ** m -- current filter. 2469*0Sstevel@tonic-gate ** e -- current envelope. 2470*0Sstevel@tonic-gate ** state -- return state from response. 2471*0Sstevel@tonic-gate ** 2472*0Sstevel@tonic-gate ** Returns: 2473*0Sstevel@tonic-gate ** response string (may be NULL) 2474*0Sstevel@tonic-gate */ 2475*0Sstevel@tonic-gate 2476*0Sstevel@tonic-gate static char * 2477*0Sstevel@tonic-gate milter_body(m, e, state) 2478*0Sstevel@tonic-gate struct milter *m; 2479*0Sstevel@tonic-gate ENVELOPE *e; 2480*0Sstevel@tonic-gate char *state; 2481*0Sstevel@tonic-gate { 2482*0Sstevel@tonic-gate char bufchar = '\0'; 2483*0Sstevel@tonic-gate char prevchar = '\0'; 2484*0Sstevel@tonic-gate int c; 2485*0Sstevel@tonic-gate char *response = NULL; 2486*0Sstevel@tonic-gate char *bp; 2487*0Sstevel@tonic-gate char buf[MILTER_CHUNK_SIZE]; 2488*0Sstevel@tonic-gate 2489*0Sstevel@tonic-gate if (tTd(64, 10)) 2490*0Sstevel@tonic-gate sm_dprintf("milter_body\n"); 2491*0Sstevel@tonic-gate 2492*0Sstevel@tonic-gate if (bfrewind(e->e_dfp) < 0) 2493*0Sstevel@tonic-gate { 2494*0Sstevel@tonic-gate ExitStat = EX_IOERR; 2495*0Sstevel@tonic-gate *state = SMFIR_TEMPFAIL; 2496*0Sstevel@tonic-gate syserr("milter_body: %s/%cf%s: rewind error", 2497*0Sstevel@tonic-gate qid_printqueue(e->e_qgrp, e->e_qdir), 2498*0Sstevel@tonic-gate DATAFL_LETTER, e->e_id); 2499*0Sstevel@tonic-gate return NULL; 2500*0Sstevel@tonic-gate } 2501*0Sstevel@tonic-gate 2502*0Sstevel@tonic-gate if (MilterLogLevel > 17) 2503*0Sstevel@tonic-gate sm_syslog(LOG_INFO, e->e_id, "Milter (%s): body, send", 2504*0Sstevel@tonic-gate m->mf_name); 2505*0Sstevel@tonic-gate bp = buf; 2506*0Sstevel@tonic-gate while ((c = sm_io_getc(e->e_dfp, SM_TIME_DEFAULT)) != SM_IO_EOF) 2507*0Sstevel@tonic-gate { 2508*0Sstevel@tonic-gate /* Change LF to CRLF */ 2509*0Sstevel@tonic-gate if (c == '\n') 2510*0Sstevel@tonic-gate { 2511*0Sstevel@tonic-gate /* Not a CRLF already? */ 2512*0Sstevel@tonic-gate if (prevchar != '\r') 2513*0Sstevel@tonic-gate { 2514*0Sstevel@tonic-gate /* Room for CR now? */ 2515*0Sstevel@tonic-gate if (bp + 2 > &buf[sizeof buf]) 2516*0Sstevel@tonic-gate { 2517*0Sstevel@tonic-gate /* No room, buffer LF */ 2518*0Sstevel@tonic-gate bufchar = c; 2519*0Sstevel@tonic-gate 2520*0Sstevel@tonic-gate /* and send CR now */ 2521*0Sstevel@tonic-gate c = '\r'; 2522*0Sstevel@tonic-gate } 2523*0Sstevel@tonic-gate else 2524*0Sstevel@tonic-gate { 2525*0Sstevel@tonic-gate /* Room to do it now */ 2526*0Sstevel@tonic-gate *bp++ = '\r'; 2527*0Sstevel@tonic-gate prevchar = '\r'; 2528*0Sstevel@tonic-gate } 2529*0Sstevel@tonic-gate } 2530*0Sstevel@tonic-gate } 2531*0Sstevel@tonic-gate *bp++ = (char) c; 2532*0Sstevel@tonic-gate prevchar = c; 2533*0Sstevel@tonic-gate if (bp >= &buf[sizeof buf]) 2534*0Sstevel@tonic-gate { 2535*0Sstevel@tonic-gate /* send chunk */ 2536*0Sstevel@tonic-gate response = milter_send_command(m, SMFIC_BODY, buf, 2537*0Sstevel@tonic-gate bp - buf, e, state); 2538*0Sstevel@tonic-gate bp = buf; 2539*0Sstevel@tonic-gate if (bufchar != '\0') 2540*0Sstevel@tonic-gate { 2541*0Sstevel@tonic-gate *bp++ = bufchar; 2542*0Sstevel@tonic-gate bufchar = '\0'; 2543*0Sstevel@tonic-gate prevchar = bufchar; 2544*0Sstevel@tonic-gate } 2545*0Sstevel@tonic-gate } 2546*0Sstevel@tonic-gate if (m->mf_state == SMFS_ERROR || 2547*0Sstevel@tonic-gate m->mf_state == SMFS_DONE || 2548*0Sstevel@tonic-gate *state != SMFIR_CONTINUE) 2549*0Sstevel@tonic-gate break; 2550*0Sstevel@tonic-gate } 2551*0Sstevel@tonic-gate 2552*0Sstevel@tonic-gate /* check for read errors */ 2553*0Sstevel@tonic-gate if (sm_io_error(e->e_dfp)) 2554*0Sstevel@tonic-gate { 2555*0Sstevel@tonic-gate ExitStat = EX_IOERR; 2556*0Sstevel@tonic-gate if (*state == SMFIR_CONTINUE || 2557*0Sstevel@tonic-gate *state == SMFIR_ACCEPT) 2558*0Sstevel@tonic-gate { 2559*0Sstevel@tonic-gate *state = SMFIR_TEMPFAIL; 2560*0Sstevel@tonic-gate if (response != NULL) 2561*0Sstevel@tonic-gate { 2562*0Sstevel@tonic-gate sm_free(response); /* XXX */ 2563*0Sstevel@tonic-gate response = NULL; 2564*0Sstevel@tonic-gate } 2565*0Sstevel@tonic-gate } 2566*0Sstevel@tonic-gate syserr("milter_body: %s/%cf%s: read error", 2567*0Sstevel@tonic-gate qid_printqueue(e->e_qgrp, e->e_qdir), 2568*0Sstevel@tonic-gate DATAFL_LETTER, e->e_id); 2569*0Sstevel@tonic-gate return response; 2570*0Sstevel@tonic-gate } 2571*0Sstevel@tonic-gate 2572*0Sstevel@tonic-gate /* send last body chunk */ 2573*0Sstevel@tonic-gate if (bp > buf && 2574*0Sstevel@tonic-gate m->mf_state != SMFS_ERROR && 2575*0Sstevel@tonic-gate m->mf_state != SMFS_DONE && 2576*0Sstevel@tonic-gate *state == SMFIR_CONTINUE) 2577*0Sstevel@tonic-gate { 2578*0Sstevel@tonic-gate /* send chunk */ 2579*0Sstevel@tonic-gate response = milter_send_command(m, SMFIC_BODY, buf, bp - buf, 2580*0Sstevel@tonic-gate e, state); 2581*0Sstevel@tonic-gate bp = buf; 2582*0Sstevel@tonic-gate } 2583*0Sstevel@tonic-gate if (MilterLogLevel > 17) 2584*0Sstevel@tonic-gate sm_syslog(LOG_INFO, e->e_id, "Milter (%s): body, sent", 2585*0Sstevel@tonic-gate m->mf_name); 2586*0Sstevel@tonic-gate return response; 2587*0Sstevel@tonic-gate } 2588*0Sstevel@tonic-gate 2589*0Sstevel@tonic-gate /* 2590*0Sstevel@tonic-gate ** Actions 2591*0Sstevel@tonic-gate */ 2592*0Sstevel@tonic-gate 2593*0Sstevel@tonic-gate /* 2594*0Sstevel@tonic-gate ** MILTER_ADDHEADER -- Add the supplied header to the message 2595*0Sstevel@tonic-gate ** 2596*0Sstevel@tonic-gate ** Parameters: 2597*0Sstevel@tonic-gate ** response -- encoded form of header/value. 2598*0Sstevel@tonic-gate ** rlen -- length of response. 2599*0Sstevel@tonic-gate ** e -- current envelope. 2600*0Sstevel@tonic-gate ** 2601*0Sstevel@tonic-gate ** Returns: 2602*0Sstevel@tonic-gate ** none 2603*0Sstevel@tonic-gate */ 2604*0Sstevel@tonic-gate 2605*0Sstevel@tonic-gate static void 2606*0Sstevel@tonic-gate milter_addheader(response, rlen, e) 2607*0Sstevel@tonic-gate char *response; 2608*0Sstevel@tonic-gate ssize_t rlen; 2609*0Sstevel@tonic-gate ENVELOPE *e; 2610*0Sstevel@tonic-gate { 2611*0Sstevel@tonic-gate char *val; 2612*0Sstevel@tonic-gate HDR *h; 2613*0Sstevel@tonic-gate 2614*0Sstevel@tonic-gate if (tTd(64, 10)) 2615*0Sstevel@tonic-gate sm_dprintf("milter_addheader: "); 2616*0Sstevel@tonic-gate 2617*0Sstevel@tonic-gate /* sanity checks */ 2618*0Sstevel@tonic-gate if (response == NULL) 2619*0Sstevel@tonic-gate { 2620*0Sstevel@tonic-gate if (tTd(64, 10)) 2621*0Sstevel@tonic-gate sm_dprintf("NULL response\n"); 2622*0Sstevel@tonic-gate return; 2623*0Sstevel@tonic-gate } 2624*0Sstevel@tonic-gate 2625*0Sstevel@tonic-gate if (rlen < 2 || strlen(response) + 1 >= (size_t) rlen) 2626*0Sstevel@tonic-gate { 2627*0Sstevel@tonic-gate if (tTd(64, 10)) 2628*0Sstevel@tonic-gate sm_dprintf("didn't follow protocol (total len)\n"); 2629*0Sstevel@tonic-gate return; 2630*0Sstevel@tonic-gate } 2631*0Sstevel@tonic-gate 2632*0Sstevel@tonic-gate /* Find separating NUL */ 2633*0Sstevel@tonic-gate val = response + strlen(response) + 1; 2634*0Sstevel@tonic-gate 2635*0Sstevel@tonic-gate /* another sanity check */ 2636*0Sstevel@tonic-gate if (strlen(response) + strlen(val) + 2 != (size_t) rlen) 2637*0Sstevel@tonic-gate { 2638*0Sstevel@tonic-gate if (tTd(64, 10)) 2639*0Sstevel@tonic-gate sm_dprintf("didn't follow protocol (part len)\n"); 2640*0Sstevel@tonic-gate return; 2641*0Sstevel@tonic-gate } 2642*0Sstevel@tonic-gate 2643*0Sstevel@tonic-gate if (*response == '\0') 2644*0Sstevel@tonic-gate { 2645*0Sstevel@tonic-gate if (tTd(64, 10)) 2646*0Sstevel@tonic-gate sm_dprintf("empty field name\n"); 2647*0Sstevel@tonic-gate return; 2648*0Sstevel@tonic-gate } 2649*0Sstevel@tonic-gate 2650*0Sstevel@tonic-gate for (h = e->e_header; h != NULL; h = h->h_link) 2651*0Sstevel@tonic-gate { 2652*0Sstevel@tonic-gate if (sm_strcasecmp(h->h_field, response) == 0 && 2653*0Sstevel@tonic-gate !bitset(H_USER, h->h_flags) && 2654*0Sstevel@tonic-gate !bitset(H_TRACE, h->h_flags)) 2655*0Sstevel@tonic-gate break; 2656*0Sstevel@tonic-gate } 2657*0Sstevel@tonic-gate 2658*0Sstevel@tonic-gate /* add to e_msgsize */ 2659*0Sstevel@tonic-gate e->e_msgsize += strlen(response) + 2 + strlen(val); 2660*0Sstevel@tonic-gate 2661*0Sstevel@tonic-gate if (h != NULL) 2662*0Sstevel@tonic-gate { 2663*0Sstevel@tonic-gate if (tTd(64, 10)) 2664*0Sstevel@tonic-gate sm_dprintf("Replace default header %s value with %s\n", 2665*0Sstevel@tonic-gate h->h_field, val); 2666*0Sstevel@tonic-gate if (MilterLogLevel > 8) 2667*0Sstevel@tonic-gate sm_syslog(LOG_INFO, e->e_id, 2668*0Sstevel@tonic-gate "Milter change: default header %s value with %s", 2669*0Sstevel@tonic-gate h->h_field, val); 2670*0Sstevel@tonic-gate h->h_value = newstr(val); 2671*0Sstevel@tonic-gate h->h_flags |= H_USER; 2672*0Sstevel@tonic-gate } 2673*0Sstevel@tonic-gate else 2674*0Sstevel@tonic-gate { 2675*0Sstevel@tonic-gate if (tTd(64, 10)) 2676*0Sstevel@tonic-gate sm_dprintf("Add %s: %s\n", response, val); 2677*0Sstevel@tonic-gate if (MilterLogLevel > 8) 2678*0Sstevel@tonic-gate sm_syslog(LOG_INFO, e->e_id, "Milter add: header: %s: %s", 2679*0Sstevel@tonic-gate response, val); 2680*0Sstevel@tonic-gate addheader(newstr(response), val, H_USER, e); 2681*0Sstevel@tonic-gate } 2682*0Sstevel@tonic-gate } 2683*0Sstevel@tonic-gate /* 2684*0Sstevel@tonic-gate ** MILTER_INSHEADER -- Insert the supplied header 2685*0Sstevel@tonic-gate ** 2686*0Sstevel@tonic-gate ** Parameters: 2687*0Sstevel@tonic-gate ** response -- encoded form of header/value. 2688*0Sstevel@tonic-gate ** rlen -- length of response. 2689*0Sstevel@tonic-gate ** e -- current envelope. 2690*0Sstevel@tonic-gate ** 2691*0Sstevel@tonic-gate ** Returns: 2692*0Sstevel@tonic-gate ** none 2693*0Sstevel@tonic-gate ** 2694*0Sstevel@tonic-gate ** Notes: 2695*0Sstevel@tonic-gate ** Unlike milter_addheader(), this does not attempt to determine 2696*0Sstevel@tonic-gate ** if the header already exists in the envelope, even a 2697*0Sstevel@tonic-gate ** deleted version. It just blindly inserts. 2698*0Sstevel@tonic-gate */ 2699*0Sstevel@tonic-gate 2700*0Sstevel@tonic-gate static void 2701*0Sstevel@tonic-gate milter_insheader(response, rlen, e) 2702*0Sstevel@tonic-gate char *response; 2703*0Sstevel@tonic-gate ssize_t rlen; 2704*0Sstevel@tonic-gate ENVELOPE *e; 2705*0Sstevel@tonic-gate { 2706*0Sstevel@tonic-gate mi_int32 idx, i; 2707*0Sstevel@tonic-gate char *field; 2708*0Sstevel@tonic-gate char *val; 2709*0Sstevel@tonic-gate 2710*0Sstevel@tonic-gate if (tTd(64, 10)) 2711*0Sstevel@tonic-gate sm_dprintf("milter_insheader: "); 2712*0Sstevel@tonic-gate 2713*0Sstevel@tonic-gate /* sanity checks */ 2714*0Sstevel@tonic-gate if (response == NULL) 2715*0Sstevel@tonic-gate { 2716*0Sstevel@tonic-gate if (tTd(64, 10)) 2717*0Sstevel@tonic-gate sm_dprintf("NULL response\n"); 2718*0Sstevel@tonic-gate return; 2719*0Sstevel@tonic-gate } 2720*0Sstevel@tonic-gate 2721*0Sstevel@tonic-gate if (rlen < 2 || strlen(response) + 1 >= (size_t) rlen) 2722*0Sstevel@tonic-gate { 2723*0Sstevel@tonic-gate if (tTd(64, 10)) 2724*0Sstevel@tonic-gate sm_dprintf("didn't follow protocol (total len)\n"); 2725*0Sstevel@tonic-gate return; 2726*0Sstevel@tonic-gate } 2727*0Sstevel@tonic-gate 2728*0Sstevel@tonic-gate /* decode */ 2729*0Sstevel@tonic-gate (void) memcpy((char *) &i, response, MILTER_LEN_BYTES); 2730*0Sstevel@tonic-gate idx = ntohl(i); 2731*0Sstevel@tonic-gate field = response + MILTER_LEN_BYTES; 2732*0Sstevel@tonic-gate val = field + strlen(field) + 1; 2733*0Sstevel@tonic-gate 2734*0Sstevel@tonic-gate /* another sanity check */ 2735*0Sstevel@tonic-gate if (MILTER_LEN_BYTES + strlen(field) + 1 + 2736*0Sstevel@tonic-gate strlen(val) + 1 != (size_t) rlen) 2737*0Sstevel@tonic-gate { 2738*0Sstevel@tonic-gate if (tTd(64, 10)) 2739*0Sstevel@tonic-gate sm_dprintf("didn't follow protocol (part len)\n"); 2740*0Sstevel@tonic-gate return; 2741*0Sstevel@tonic-gate } 2742*0Sstevel@tonic-gate 2743*0Sstevel@tonic-gate if (*field == '\0') 2744*0Sstevel@tonic-gate { 2745*0Sstevel@tonic-gate if (tTd(64, 10)) 2746*0Sstevel@tonic-gate sm_dprintf("empty field name\n"); 2747*0Sstevel@tonic-gate return; 2748*0Sstevel@tonic-gate } 2749*0Sstevel@tonic-gate 2750*0Sstevel@tonic-gate /* add to e_msgsize */ 2751*0Sstevel@tonic-gate e->e_msgsize += strlen(response) + 2 + strlen(val); 2752*0Sstevel@tonic-gate 2753*0Sstevel@tonic-gate if (tTd(64, 10)) 2754*0Sstevel@tonic-gate sm_dprintf("Insert (%d) %s: %s\n", idx, response, val); 2755*0Sstevel@tonic-gate if (MilterLogLevel > 8) 2756*0Sstevel@tonic-gate sm_syslog(LOG_INFO, e->e_id, 2757*0Sstevel@tonic-gate "Milter insert (%d): header: %s: %s", 2758*0Sstevel@tonic-gate idx, field, val); 2759*0Sstevel@tonic-gate insheader(idx, newstr(field), val, H_USER, e); 2760*0Sstevel@tonic-gate } 2761*0Sstevel@tonic-gate /* 2762*0Sstevel@tonic-gate ** MILTER_CHANGEHEADER -- Change the supplied header in the message 2763*0Sstevel@tonic-gate ** 2764*0Sstevel@tonic-gate ** Parameters: 2765*0Sstevel@tonic-gate ** response -- encoded form of header/index/value. 2766*0Sstevel@tonic-gate ** rlen -- length of response. 2767*0Sstevel@tonic-gate ** e -- current envelope. 2768*0Sstevel@tonic-gate ** 2769*0Sstevel@tonic-gate ** Returns: 2770*0Sstevel@tonic-gate ** none 2771*0Sstevel@tonic-gate */ 2772*0Sstevel@tonic-gate 2773*0Sstevel@tonic-gate static void 2774*0Sstevel@tonic-gate milter_changeheader(response, rlen, e) 2775*0Sstevel@tonic-gate char *response; 2776*0Sstevel@tonic-gate ssize_t rlen; 2777*0Sstevel@tonic-gate ENVELOPE *e; 2778*0Sstevel@tonic-gate { 2779*0Sstevel@tonic-gate mi_int32 i, index; 2780*0Sstevel@tonic-gate char *field, *val; 2781*0Sstevel@tonic-gate HDR *h, *sysheader; 2782*0Sstevel@tonic-gate 2783*0Sstevel@tonic-gate if (tTd(64, 10)) 2784*0Sstevel@tonic-gate sm_dprintf("milter_changeheader: "); 2785*0Sstevel@tonic-gate 2786*0Sstevel@tonic-gate /* sanity checks */ 2787*0Sstevel@tonic-gate if (response == NULL) 2788*0Sstevel@tonic-gate { 2789*0Sstevel@tonic-gate if (tTd(64, 10)) 2790*0Sstevel@tonic-gate sm_dprintf("NULL response\n"); 2791*0Sstevel@tonic-gate return; 2792*0Sstevel@tonic-gate } 2793*0Sstevel@tonic-gate 2794*0Sstevel@tonic-gate if (rlen < 2 || strlen(response) + 1 >= (size_t) rlen) 2795*0Sstevel@tonic-gate { 2796*0Sstevel@tonic-gate if (tTd(64, 10)) 2797*0Sstevel@tonic-gate sm_dprintf("didn't follow protocol (total len)\n"); 2798*0Sstevel@tonic-gate return; 2799*0Sstevel@tonic-gate } 2800*0Sstevel@tonic-gate 2801*0Sstevel@tonic-gate /* Find separating NUL */ 2802*0Sstevel@tonic-gate (void) memcpy((char *) &i, response, MILTER_LEN_BYTES); 2803*0Sstevel@tonic-gate index = ntohl(i); 2804*0Sstevel@tonic-gate field = response + MILTER_LEN_BYTES; 2805*0Sstevel@tonic-gate val = field + strlen(field) + 1; 2806*0Sstevel@tonic-gate 2807*0Sstevel@tonic-gate /* another sanity check */ 2808*0Sstevel@tonic-gate if (MILTER_LEN_BYTES + strlen(field) + 1 + 2809*0Sstevel@tonic-gate strlen(val) + 1 != (size_t) rlen) 2810*0Sstevel@tonic-gate { 2811*0Sstevel@tonic-gate if (tTd(64, 10)) 2812*0Sstevel@tonic-gate sm_dprintf("didn't follow protocol (part len)\n"); 2813*0Sstevel@tonic-gate return; 2814*0Sstevel@tonic-gate } 2815*0Sstevel@tonic-gate 2816*0Sstevel@tonic-gate if (*field == '\0') 2817*0Sstevel@tonic-gate { 2818*0Sstevel@tonic-gate if (tTd(64, 10)) 2819*0Sstevel@tonic-gate sm_dprintf("empty field name\n"); 2820*0Sstevel@tonic-gate return; 2821*0Sstevel@tonic-gate } 2822*0Sstevel@tonic-gate 2823*0Sstevel@tonic-gate sysheader = NULL; 2824*0Sstevel@tonic-gate for (h = e->e_header; h != NULL; h = h->h_link) 2825*0Sstevel@tonic-gate { 2826*0Sstevel@tonic-gate if (sm_strcasecmp(h->h_field, field) == 0) 2827*0Sstevel@tonic-gate { 2828*0Sstevel@tonic-gate if (bitset(H_USER, h->h_flags) && 2829*0Sstevel@tonic-gate --index <= 0) 2830*0Sstevel@tonic-gate { 2831*0Sstevel@tonic-gate sysheader = NULL; 2832*0Sstevel@tonic-gate break; 2833*0Sstevel@tonic-gate } 2834*0Sstevel@tonic-gate else if (!bitset(H_USER, h->h_flags) && 2835*0Sstevel@tonic-gate !bitset(H_TRACE, h->h_flags)) 2836*0Sstevel@tonic-gate { 2837*0Sstevel@tonic-gate /* 2838*0Sstevel@tonic-gate ** DRUMS msg-fmt draft says can only have 2839*0Sstevel@tonic-gate ** multiple occurences of trace fields, 2840*0Sstevel@tonic-gate ** so make sure we replace any non-trace, 2841*0Sstevel@tonic-gate ** non-user field. 2842*0Sstevel@tonic-gate */ 2843*0Sstevel@tonic-gate 2844*0Sstevel@tonic-gate sysheader = h; 2845*0Sstevel@tonic-gate } 2846*0Sstevel@tonic-gate } 2847*0Sstevel@tonic-gate } 2848*0Sstevel@tonic-gate 2849*0Sstevel@tonic-gate /* if not found as user-provided header at index, use sysheader */ 2850*0Sstevel@tonic-gate if (h == NULL) 2851*0Sstevel@tonic-gate h = sysheader; 2852*0Sstevel@tonic-gate 2853*0Sstevel@tonic-gate if (h == NULL) 2854*0Sstevel@tonic-gate { 2855*0Sstevel@tonic-gate if (*val == '\0') 2856*0Sstevel@tonic-gate { 2857*0Sstevel@tonic-gate if (tTd(64, 10)) 2858*0Sstevel@tonic-gate sm_dprintf("Delete (noop) %s\n", field); 2859*0Sstevel@tonic-gate if (MilterLogLevel > 8) 2860*0Sstevel@tonic-gate sm_syslog(LOG_INFO, e->e_id, 2861*0Sstevel@tonic-gate "Milter delete (noop): header: %s" 2862*0Sstevel@tonic-gate , field); 2863*0Sstevel@tonic-gate } 2864*0Sstevel@tonic-gate else 2865*0Sstevel@tonic-gate { 2866*0Sstevel@tonic-gate /* treat modify value with no existing header as add */ 2867*0Sstevel@tonic-gate if (tTd(64, 10)) 2868*0Sstevel@tonic-gate sm_dprintf("Add %s: %s\n", field, val); 2869*0Sstevel@tonic-gate if (MilterLogLevel > 8) 2870*0Sstevel@tonic-gate sm_syslog(LOG_INFO, e->e_id, 2871*0Sstevel@tonic-gate "Milter change (add): header: %s: %s" 2872*0Sstevel@tonic-gate , field, val); 2873*0Sstevel@tonic-gate addheader(newstr(field), val, H_USER, e); 2874*0Sstevel@tonic-gate } 2875*0Sstevel@tonic-gate return; 2876*0Sstevel@tonic-gate } 2877*0Sstevel@tonic-gate 2878*0Sstevel@tonic-gate if (tTd(64, 10)) 2879*0Sstevel@tonic-gate { 2880*0Sstevel@tonic-gate if (*val == '\0') 2881*0Sstevel@tonic-gate { 2882*0Sstevel@tonic-gate sm_dprintf("Delete%s %s: %s\n", 2883*0Sstevel@tonic-gate h == sysheader ? " (default header)" : "", 2884*0Sstevel@tonic-gate field, 2885*0Sstevel@tonic-gate h->h_value == NULL ? "<NULL>" : h->h_value); 2886*0Sstevel@tonic-gate } 2887*0Sstevel@tonic-gate else 2888*0Sstevel@tonic-gate { 2889*0Sstevel@tonic-gate sm_dprintf("Change%s %s: from %s to %s\n", 2890*0Sstevel@tonic-gate h == sysheader ? " (default header)" : "", 2891*0Sstevel@tonic-gate field, 2892*0Sstevel@tonic-gate h->h_value == NULL ? "<NULL>" : h->h_value, 2893*0Sstevel@tonic-gate val); 2894*0Sstevel@tonic-gate } 2895*0Sstevel@tonic-gate } 2896*0Sstevel@tonic-gate 2897*0Sstevel@tonic-gate if (MilterLogLevel > 8) 2898*0Sstevel@tonic-gate { 2899*0Sstevel@tonic-gate if (*val == '\0') 2900*0Sstevel@tonic-gate { 2901*0Sstevel@tonic-gate sm_syslog(LOG_INFO, e->e_id, 2902*0Sstevel@tonic-gate "Milter delete: header%s %s: %s", 2903*0Sstevel@tonic-gate h == sysheader ? " (default header)" : "", 2904*0Sstevel@tonic-gate field, 2905*0Sstevel@tonic-gate h->h_value == NULL ? "<NULL>" : h->h_value); 2906*0Sstevel@tonic-gate } 2907*0Sstevel@tonic-gate else 2908*0Sstevel@tonic-gate { 2909*0Sstevel@tonic-gate sm_syslog(LOG_INFO, e->e_id, 2910*0Sstevel@tonic-gate "Milter change: header%s %s: from %s to %s", 2911*0Sstevel@tonic-gate h == sysheader ? " (default header)" : "", 2912*0Sstevel@tonic-gate field, 2913*0Sstevel@tonic-gate h->h_value == NULL ? "<NULL>" : h->h_value, 2914*0Sstevel@tonic-gate val); 2915*0Sstevel@tonic-gate } 2916*0Sstevel@tonic-gate } 2917*0Sstevel@tonic-gate 2918*0Sstevel@tonic-gate if (h != sysheader && h->h_value != NULL) 2919*0Sstevel@tonic-gate { 2920*0Sstevel@tonic-gate size_t l; 2921*0Sstevel@tonic-gate 2922*0Sstevel@tonic-gate l = strlen(h->h_value); 2923*0Sstevel@tonic-gate if (l > e->e_msgsize) 2924*0Sstevel@tonic-gate e->e_msgsize = 0; 2925*0Sstevel@tonic-gate else 2926*0Sstevel@tonic-gate e->e_msgsize -= l; 2927*0Sstevel@tonic-gate /* rpool, don't free: sm_free(h->h_value); XXX */ 2928*0Sstevel@tonic-gate } 2929*0Sstevel@tonic-gate 2930*0Sstevel@tonic-gate if (*val == '\0') 2931*0Sstevel@tonic-gate { 2932*0Sstevel@tonic-gate /* Remove "Field: " from message size */ 2933*0Sstevel@tonic-gate if (h != sysheader) 2934*0Sstevel@tonic-gate { 2935*0Sstevel@tonic-gate size_t l; 2936*0Sstevel@tonic-gate 2937*0Sstevel@tonic-gate l = strlen(h->h_field) + 2; 2938*0Sstevel@tonic-gate if (l > e->e_msgsize) 2939*0Sstevel@tonic-gate e->e_msgsize = 0; 2940*0Sstevel@tonic-gate else 2941*0Sstevel@tonic-gate e->e_msgsize -= l; 2942*0Sstevel@tonic-gate } 2943*0Sstevel@tonic-gate h->h_value = NULL; 2944*0Sstevel@tonic-gate } 2945*0Sstevel@tonic-gate else 2946*0Sstevel@tonic-gate { 2947*0Sstevel@tonic-gate h->h_value = newstr(val); 2948*0Sstevel@tonic-gate h->h_flags |= H_USER; 2949*0Sstevel@tonic-gate e->e_msgsize += strlen(h->h_value); 2950*0Sstevel@tonic-gate } 2951*0Sstevel@tonic-gate } 2952*0Sstevel@tonic-gate /* 2953*0Sstevel@tonic-gate ** MILTER_ADDRCPT -- Add the supplied recipient to the message 2954*0Sstevel@tonic-gate ** 2955*0Sstevel@tonic-gate ** Parameters: 2956*0Sstevel@tonic-gate ** response -- encoded form of recipient address. 2957*0Sstevel@tonic-gate ** rlen -- length of response. 2958*0Sstevel@tonic-gate ** e -- current envelope. 2959*0Sstevel@tonic-gate ** 2960*0Sstevel@tonic-gate ** Returns: 2961*0Sstevel@tonic-gate ** none 2962*0Sstevel@tonic-gate */ 2963*0Sstevel@tonic-gate 2964*0Sstevel@tonic-gate static void 2965*0Sstevel@tonic-gate milter_addrcpt(response, rlen, e) 2966*0Sstevel@tonic-gate char *response; 2967*0Sstevel@tonic-gate ssize_t rlen; 2968*0Sstevel@tonic-gate ENVELOPE *e; 2969*0Sstevel@tonic-gate { 2970*0Sstevel@tonic-gate int olderrors; 2971*0Sstevel@tonic-gate 2972*0Sstevel@tonic-gate if (tTd(64, 10)) 2973*0Sstevel@tonic-gate sm_dprintf("milter_addrcpt: "); 2974*0Sstevel@tonic-gate 2975*0Sstevel@tonic-gate /* sanity checks */ 2976*0Sstevel@tonic-gate if (response == NULL) 2977*0Sstevel@tonic-gate { 2978*0Sstevel@tonic-gate if (tTd(64, 10)) 2979*0Sstevel@tonic-gate sm_dprintf("NULL response\n"); 2980*0Sstevel@tonic-gate return; 2981*0Sstevel@tonic-gate } 2982*0Sstevel@tonic-gate 2983*0Sstevel@tonic-gate if (*response == '\0' || 2984*0Sstevel@tonic-gate strlen(response) + 1 != (size_t) rlen) 2985*0Sstevel@tonic-gate { 2986*0Sstevel@tonic-gate if (tTd(64, 10)) 2987*0Sstevel@tonic-gate sm_dprintf("didn't follow protocol (total len %d != rlen %d)\n", 2988*0Sstevel@tonic-gate (int) strlen(response), (int) (rlen - 1)); 2989*0Sstevel@tonic-gate return; 2990*0Sstevel@tonic-gate } 2991*0Sstevel@tonic-gate 2992*0Sstevel@tonic-gate if (tTd(64, 10)) 2993*0Sstevel@tonic-gate sm_dprintf("%s\n", response); 2994*0Sstevel@tonic-gate if (MilterLogLevel > 8) 2995*0Sstevel@tonic-gate sm_syslog(LOG_INFO, e->e_id, "Milter add: rcpt: %s", response); 2996*0Sstevel@tonic-gate olderrors = Errors; 2997*0Sstevel@tonic-gate (void) sendtolist(response, NULLADDR, &e->e_sendqueue, 0, e); 2998*0Sstevel@tonic-gate Errors = olderrors; 2999*0Sstevel@tonic-gate return; 3000*0Sstevel@tonic-gate } 3001*0Sstevel@tonic-gate /* 3002*0Sstevel@tonic-gate ** MILTER_DELRCPT -- Delete the supplied recipient from the message 3003*0Sstevel@tonic-gate ** 3004*0Sstevel@tonic-gate ** Parameters: 3005*0Sstevel@tonic-gate ** response -- encoded form of recipient address. 3006*0Sstevel@tonic-gate ** rlen -- length of response. 3007*0Sstevel@tonic-gate ** e -- current envelope. 3008*0Sstevel@tonic-gate ** 3009*0Sstevel@tonic-gate ** Returns: 3010*0Sstevel@tonic-gate ** none 3011*0Sstevel@tonic-gate */ 3012*0Sstevel@tonic-gate 3013*0Sstevel@tonic-gate static void 3014*0Sstevel@tonic-gate milter_delrcpt(response, rlen, e) 3015*0Sstevel@tonic-gate char *response; 3016*0Sstevel@tonic-gate ssize_t rlen; 3017*0Sstevel@tonic-gate ENVELOPE *e; 3018*0Sstevel@tonic-gate { 3019*0Sstevel@tonic-gate if (tTd(64, 10)) 3020*0Sstevel@tonic-gate sm_dprintf("milter_delrcpt: "); 3021*0Sstevel@tonic-gate 3022*0Sstevel@tonic-gate /* sanity checks */ 3023*0Sstevel@tonic-gate if (response == NULL) 3024*0Sstevel@tonic-gate { 3025*0Sstevel@tonic-gate if (tTd(64, 10)) 3026*0Sstevel@tonic-gate sm_dprintf("NULL response\n"); 3027*0Sstevel@tonic-gate return; 3028*0Sstevel@tonic-gate } 3029*0Sstevel@tonic-gate 3030*0Sstevel@tonic-gate if (*response == '\0' || 3031*0Sstevel@tonic-gate strlen(response) + 1 != (size_t) rlen) 3032*0Sstevel@tonic-gate { 3033*0Sstevel@tonic-gate if (tTd(64, 10)) 3034*0Sstevel@tonic-gate sm_dprintf("didn't follow protocol (total len)\n"); 3035*0Sstevel@tonic-gate return; 3036*0Sstevel@tonic-gate } 3037*0Sstevel@tonic-gate 3038*0Sstevel@tonic-gate if (tTd(64, 10)) 3039*0Sstevel@tonic-gate sm_dprintf("%s\n", response); 3040*0Sstevel@tonic-gate if (MilterLogLevel > 8) 3041*0Sstevel@tonic-gate sm_syslog(LOG_INFO, e->e_id, "Milter delete: rcpt %s", 3042*0Sstevel@tonic-gate response); 3043*0Sstevel@tonic-gate (void) removefromlist(response, &e->e_sendqueue, e); 3044*0Sstevel@tonic-gate return; 3045*0Sstevel@tonic-gate } 3046*0Sstevel@tonic-gate /* 3047*0Sstevel@tonic-gate ** MILTER_REPLBODY -- Replace the current data file with new body 3048*0Sstevel@tonic-gate ** 3049*0Sstevel@tonic-gate ** Parameters: 3050*0Sstevel@tonic-gate ** response -- encoded form of new body. 3051*0Sstevel@tonic-gate ** rlen -- length of response. 3052*0Sstevel@tonic-gate ** newfilter -- if first time called by a new filter 3053*0Sstevel@tonic-gate ** e -- current envelope. 3054*0Sstevel@tonic-gate ** 3055*0Sstevel@tonic-gate ** Returns: 3056*0Sstevel@tonic-gate ** 0 upon success, -1 upon failure 3057*0Sstevel@tonic-gate */ 3058*0Sstevel@tonic-gate 3059*0Sstevel@tonic-gate static int 3060*0Sstevel@tonic-gate milter_replbody(response, rlen, newfilter, e) 3061*0Sstevel@tonic-gate char *response; 3062*0Sstevel@tonic-gate ssize_t rlen; 3063*0Sstevel@tonic-gate bool newfilter; 3064*0Sstevel@tonic-gate ENVELOPE *e; 3065*0Sstevel@tonic-gate { 3066*0Sstevel@tonic-gate static char prevchar; 3067*0Sstevel@tonic-gate int i; 3068*0Sstevel@tonic-gate 3069*0Sstevel@tonic-gate if (tTd(64, 10)) 3070*0Sstevel@tonic-gate sm_dprintf("milter_replbody\n"); 3071*0Sstevel@tonic-gate 3072*0Sstevel@tonic-gate /* If a new filter, reset previous character and truncate data file */ 3073*0Sstevel@tonic-gate if (newfilter) 3074*0Sstevel@tonic-gate { 3075*0Sstevel@tonic-gate off_t prevsize; 3076*0Sstevel@tonic-gate char dfname[MAXPATHLEN]; 3077*0Sstevel@tonic-gate 3078*0Sstevel@tonic-gate (void) sm_strlcpy(dfname, queuename(e, DATAFL_LETTER), 3079*0Sstevel@tonic-gate sizeof dfname); 3080*0Sstevel@tonic-gate 3081*0Sstevel@tonic-gate /* Reset prevchar */ 3082*0Sstevel@tonic-gate prevchar = '\0'; 3083*0Sstevel@tonic-gate 3084*0Sstevel@tonic-gate /* Get the current data file information */ 3085*0Sstevel@tonic-gate prevsize = sm_io_getinfo(e->e_dfp, SM_IO_WHAT_SIZE, NULL); 3086*0Sstevel@tonic-gate if (prevsize < 0) 3087*0Sstevel@tonic-gate prevsize = 0; 3088*0Sstevel@tonic-gate 3089*0Sstevel@tonic-gate /* truncate current data file */ 3090*0Sstevel@tonic-gate if (sm_io_getinfo(e->e_dfp, SM_IO_WHAT_ISTYPE, BF_FILE_TYPE)) 3091*0Sstevel@tonic-gate { 3092*0Sstevel@tonic-gate if (sm_io_setinfo(e->e_dfp, SM_BF_TRUNCATE, NULL) < 0) 3093*0Sstevel@tonic-gate { 3094*0Sstevel@tonic-gate MILTER_DF_ERROR("milter_replbody: sm_io truncate %s: %s"); 3095*0Sstevel@tonic-gate return -1; 3096*0Sstevel@tonic-gate } 3097*0Sstevel@tonic-gate } 3098*0Sstevel@tonic-gate else 3099*0Sstevel@tonic-gate { 3100*0Sstevel@tonic-gate int err; 3101*0Sstevel@tonic-gate 3102*0Sstevel@tonic-gate err = sm_io_error(e->e_dfp); 3103*0Sstevel@tonic-gate (void) sm_io_flush(e->e_dfp, SM_TIME_DEFAULT); 3104*0Sstevel@tonic-gate 3105*0Sstevel@tonic-gate /* 3106*0Sstevel@tonic-gate ** Clear error if tried to fflush() 3107*0Sstevel@tonic-gate ** a read-only file pointer and 3108*0Sstevel@tonic-gate ** there wasn't a previous error. 3109*0Sstevel@tonic-gate */ 3110*0Sstevel@tonic-gate 3111*0Sstevel@tonic-gate if (err == 0) 3112*0Sstevel@tonic-gate sm_io_clearerr(e->e_dfp); 3113*0Sstevel@tonic-gate 3114*0Sstevel@tonic-gate /* errno is set implicitly by fseek() before return */ 3115*0Sstevel@tonic-gate err = sm_io_seek(e->e_dfp, SM_TIME_DEFAULT, 3116*0Sstevel@tonic-gate 0, SEEK_SET); 3117*0Sstevel@tonic-gate if (err < 0) 3118*0Sstevel@tonic-gate { 3119*0Sstevel@tonic-gate MILTER_DF_ERROR("milter_replbody: sm_io_seek %s: %s"); 3120*0Sstevel@tonic-gate return -1; 3121*0Sstevel@tonic-gate } 3122*0Sstevel@tonic-gate # if NOFTRUNCATE 3123*0Sstevel@tonic-gate /* XXX: Not much we can do except rewind it */ 3124*0Sstevel@tonic-gate errno = EINVAL; 3125*0Sstevel@tonic-gate MILTER_DF_ERROR("milter_replbody: ftruncate not available on this platform (%s:%s)"); 3126*0Sstevel@tonic-gate return -1; 3127*0Sstevel@tonic-gate # else /* NOFTRUNCATE */ 3128*0Sstevel@tonic-gate err = ftruncate(sm_io_getinfo(e->e_dfp, 3129*0Sstevel@tonic-gate SM_IO_WHAT_FD, NULL), 3130*0Sstevel@tonic-gate 0); 3131*0Sstevel@tonic-gate if (err < 0) 3132*0Sstevel@tonic-gate { 3133*0Sstevel@tonic-gate MILTER_DF_ERROR("milter_replbody: sm_io ftruncate %s: %s"); 3134*0Sstevel@tonic-gate return -1; 3135*0Sstevel@tonic-gate } 3136*0Sstevel@tonic-gate # endif /* NOFTRUNCATE */ 3137*0Sstevel@tonic-gate } 3138*0Sstevel@tonic-gate 3139*0Sstevel@tonic-gate if (prevsize > e->e_msgsize) 3140*0Sstevel@tonic-gate e->e_msgsize = 0; 3141*0Sstevel@tonic-gate else 3142*0Sstevel@tonic-gate e->e_msgsize -= prevsize; 3143*0Sstevel@tonic-gate } 3144*0Sstevel@tonic-gate 3145*0Sstevel@tonic-gate if (newfilter && MilterLogLevel > 8) 3146*0Sstevel@tonic-gate sm_syslog(LOG_INFO, e->e_id, "Milter message: body replaced"); 3147*0Sstevel@tonic-gate 3148*0Sstevel@tonic-gate if (response == NULL) 3149*0Sstevel@tonic-gate { 3150*0Sstevel@tonic-gate /* Flush the buffered '\r' */ 3151*0Sstevel@tonic-gate if (prevchar == '\r') 3152*0Sstevel@tonic-gate { 3153*0Sstevel@tonic-gate (void) sm_io_putc(e->e_dfp, SM_TIME_DEFAULT, prevchar); 3154*0Sstevel@tonic-gate e->e_msgsize++; 3155*0Sstevel@tonic-gate } 3156*0Sstevel@tonic-gate return 0; 3157*0Sstevel@tonic-gate } 3158*0Sstevel@tonic-gate 3159*0Sstevel@tonic-gate for (i = 0; i < rlen; i++) 3160*0Sstevel@tonic-gate { 3161*0Sstevel@tonic-gate /* Buffered char from last chunk */ 3162*0Sstevel@tonic-gate if (i == 0 && prevchar == '\r') 3163*0Sstevel@tonic-gate { 3164*0Sstevel@tonic-gate /* Not CRLF, output prevchar */ 3165*0Sstevel@tonic-gate if (response[i] != '\n') 3166*0Sstevel@tonic-gate { 3167*0Sstevel@tonic-gate (void) sm_io_putc(e->e_dfp, SM_TIME_DEFAULT, 3168*0Sstevel@tonic-gate prevchar); 3169*0Sstevel@tonic-gate e->e_msgsize++; 3170*0Sstevel@tonic-gate } 3171*0Sstevel@tonic-gate prevchar = '\0'; 3172*0Sstevel@tonic-gate } 3173*0Sstevel@tonic-gate 3174*0Sstevel@tonic-gate /* Turn CRLF into LF */ 3175*0Sstevel@tonic-gate if (response[i] == '\r') 3176*0Sstevel@tonic-gate { 3177*0Sstevel@tonic-gate /* check if at end of chunk */ 3178*0Sstevel@tonic-gate if (i + 1 < rlen) 3179*0Sstevel@tonic-gate { 3180*0Sstevel@tonic-gate /* If LF, strip CR */ 3181*0Sstevel@tonic-gate if (response[i + 1] == '\n') 3182*0Sstevel@tonic-gate i++; 3183*0Sstevel@tonic-gate } 3184*0Sstevel@tonic-gate else 3185*0Sstevel@tonic-gate { 3186*0Sstevel@tonic-gate /* check next chunk */ 3187*0Sstevel@tonic-gate prevchar = '\r'; 3188*0Sstevel@tonic-gate continue; 3189*0Sstevel@tonic-gate } 3190*0Sstevel@tonic-gate } 3191*0Sstevel@tonic-gate (void) sm_io_putc(e->e_dfp, SM_TIME_DEFAULT, response[i]); 3192*0Sstevel@tonic-gate e->e_msgsize++; 3193*0Sstevel@tonic-gate } 3194*0Sstevel@tonic-gate return 0; 3195*0Sstevel@tonic-gate } 3196*0Sstevel@tonic-gate 3197*0Sstevel@tonic-gate /* 3198*0Sstevel@tonic-gate ** MTA callouts 3199*0Sstevel@tonic-gate */ 3200*0Sstevel@tonic-gate 3201*0Sstevel@tonic-gate /* 3202*0Sstevel@tonic-gate ** MILTER_INIT -- open and negotiate with all of the filters 3203*0Sstevel@tonic-gate ** 3204*0Sstevel@tonic-gate ** Parameters: 3205*0Sstevel@tonic-gate ** e -- current envelope. 3206*0Sstevel@tonic-gate ** state -- return state from response. 3207*0Sstevel@tonic-gate ** 3208*0Sstevel@tonic-gate ** Returns: 3209*0Sstevel@tonic-gate ** true iff at least one filter is active 3210*0Sstevel@tonic-gate */ 3211*0Sstevel@tonic-gate 3212*0Sstevel@tonic-gate /* ARGSUSED */ 3213*0Sstevel@tonic-gate bool 3214*0Sstevel@tonic-gate milter_init(e, state) 3215*0Sstevel@tonic-gate ENVELOPE *e; 3216*0Sstevel@tonic-gate char *state; 3217*0Sstevel@tonic-gate { 3218*0Sstevel@tonic-gate int i; 3219*0Sstevel@tonic-gate 3220*0Sstevel@tonic-gate if (tTd(64, 10)) 3221*0Sstevel@tonic-gate sm_dprintf("milter_init\n"); 3222*0Sstevel@tonic-gate 3223*0Sstevel@tonic-gate *state = SMFIR_CONTINUE; 3224*0Sstevel@tonic-gate if (InputFilters[0] == NULL) 3225*0Sstevel@tonic-gate { 3226*0Sstevel@tonic-gate if (MilterLogLevel > 10) 3227*0Sstevel@tonic-gate sm_syslog(LOG_INFO, e->e_id, 3228*0Sstevel@tonic-gate "Milter: no active filter"); 3229*0Sstevel@tonic-gate return false; 3230*0Sstevel@tonic-gate } 3231*0Sstevel@tonic-gate 3232*0Sstevel@tonic-gate for (i = 0; InputFilters[i] != NULL; i++) 3233*0Sstevel@tonic-gate { 3234*0Sstevel@tonic-gate struct milter *m = InputFilters[i]; 3235*0Sstevel@tonic-gate 3236*0Sstevel@tonic-gate m->mf_sock = milter_open(m, false, e); 3237*0Sstevel@tonic-gate if (m->mf_state == SMFS_ERROR) 3238*0Sstevel@tonic-gate { 3239*0Sstevel@tonic-gate MILTER_CHECK_ERROR(true, continue); 3240*0Sstevel@tonic-gate break; 3241*0Sstevel@tonic-gate } 3242*0Sstevel@tonic-gate 3243*0Sstevel@tonic-gate if (m->mf_sock < 0 || 3244*0Sstevel@tonic-gate milter_negotiate(m, e) < 0 || 3245*0Sstevel@tonic-gate m->mf_state == SMFS_ERROR) 3246*0Sstevel@tonic-gate { 3247*0Sstevel@tonic-gate if (tTd(64, 5)) 3248*0Sstevel@tonic-gate sm_dprintf("milter_init(%s): failed to %s\n", 3249*0Sstevel@tonic-gate m->mf_name, 3250*0Sstevel@tonic-gate m->mf_sock < 0 ? "open" : 3251*0Sstevel@tonic-gate "negotiate"); 3252*0Sstevel@tonic-gate if (MilterLogLevel > 0) 3253*0Sstevel@tonic-gate sm_syslog(LOG_ERR, e->e_id, 3254*0Sstevel@tonic-gate "Milter (%s): init failed to %s", 3255*0Sstevel@tonic-gate m->mf_name, 3256*0Sstevel@tonic-gate m->mf_sock < 0 ? "open" : 3257*0Sstevel@tonic-gate "negotiate"); 3258*0Sstevel@tonic-gate 3259*0Sstevel@tonic-gate /* if negotation failure, close socket */ 3260*0Sstevel@tonic-gate milter_error(m, e); 3261*0Sstevel@tonic-gate MILTER_CHECK_ERROR(true, continue); 3262*0Sstevel@tonic-gate continue; 3263*0Sstevel@tonic-gate } 3264*0Sstevel@tonic-gate if (MilterLogLevel > 9) 3265*0Sstevel@tonic-gate sm_syslog(LOG_INFO, e->e_id, 3266*0Sstevel@tonic-gate "Milter (%s): init success to %s", 3267*0Sstevel@tonic-gate m->mf_name, 3268*0Sstevel@tonic-gate m->mf_sock < 0 ? "open" : "negotiate"); 3269*0Sstevel@tonic-gate } 3270*0Sstevel@tonic-gate 3271*0Sstevel@tonic-gate /* 3272*0Sstevel@tonic-gate ** If something temp/perm failed with one of the filters, 3273*0Sstevel@tonic-gate ** we won't be using any of them, so clear any existing 3274*0Sstevel@tonic-gate ** connections. 3275*0Sstevel@tonic-gate */ 3276*0Sstevel@tonic-gate 3277*0Sstevel@tonic-gate if (*state != SMFIR_CONTINUE) 3278*0Sstevel@tonic-gate milter_quit(e); 3279*0Sstevel@tonic-gate 3280*0Sstevel@tonic-gate return true; 3281*0Sstevel@tonic-gate } 3282*0Sstevel@tonic-gate /* 3283*0Sstevel@tonic-gate ** MILTER_CONNECT -- send connection info to milter filters 3284*0Sstevel@tonic-gate ** 3285*0Sstevel@tonic-gate ** Parameters: 3286*0Sstevel@tonic-gate ** hostname -- hostname of remote machine. 3287*0Sstevel@tonic-gate ** addr -- address of remote machine. 3288*0Sstevel@tonic-gate ** e -- current envelope. 3289*0Sstevel@tonic-gate ** state -- return state from response. 3290*0Sstevel@tonic-gate ** 3291*0Sstevel@tonic-gate ** Returns: 3292*0Sstevel@tonic-gate ** response string (may be NULL) 3293*0Sstevel@tonic-gate */ 3294*0Sstevel@tonic-gate 3295*0Sstevel@tonic-gate char * 3296*0Sstevel@tonic-gate milter_connect(hostname, addr, e, state) 3297*0Sstevel@tonic-gate char *hostname; 3298*0Sstevel@tonic-gate SOCKADDR addr; 3299*0Sstevel@tonic-gate ENVELOPE *e; 3300*0Sstevel@tonic-gate char *state; 3301*0Sstevel@tonic-gate { 3302*0Sstevel@tonic-gate char family; 3303*0Sstevel@tonic-gate unsigned short port; 3304*0Sstevel@tonic-gate char *buf, *bp; 3305*0Sstevel@tonic-gate char *response; 3306*0Sstevel@tonic-gate char *sockinfo = NULL; 3307*0Sstevel@tonic-gate ssize_t s; 3308*0Sstevel@tonic-gate # if NETINET6 3309*0Sstevel@tonic-gate char buf6[INET6_ADDRSTRLEN]; 3310*0Sstevel@tonic-gate # endif /* NETINET6 */ 3311*0Sstevel@tonic-gate 3312*0Sstevel@tonic-gate if (tTd(64, 10)) 3313*0Sstevel@tonic-gate sm_dprintf("milter_connect(%s)\n", hostname); 3314*0Sstevel@tonic-gate if (MilterLogLevel > 9) 3315*0Sstevel@tonic-gate sm_syslog(LOG_INFO, e->e_id, "Milter: connect to filters"); 3316*0Sstevel@tonic-gate 3317*0Sstevel@tonic-gate /* gather data */ 3318*0Sstevel@tonic-gate switch (addr.sa.sa_family) 3319*0Sstevel@tonic-gate { 3320*0Sstevel@tonic-gate # if NETUNIX 3321*0Sstevel@tonic-gate case AF_UNIX: 3322*0Sstevel@tonic-gate family = SMFIA_UNIX; 3323*0Sstevel@tonic-gate port = htons(0); 3324*0Sstevel@tonic-gate sockinfo = addr.sunix.sun_path; 3325*0Sstevel@tonic-gate break; 3326*0Sstevel@tonic-gate # endif /* NETUNIX */ 3327*0Sstevel@tonic-gate 3328*0Sstevel@tonic-gate # if NETINET 3329*0Sstevel@tonic-gate case AF_INET: 3330*0Sstevel@tonic-gate family = SMFIA_INET; 3331*0Sstevel@tonic-gate port = addr.sin.sin_port; 3332*0Sstevel@tonic-gate sockinfo = (char *) inet_ntoa(addr.sin.sin_addr); 3333*0Sstevel@tonic-gate break; 3334*0Sstevel@tonic-gate # endif /* NETINET */ 3335*0Sstevel@tonic-gate 3336*0Sstevel@tonic-gate # if NETINET6 3337*0Sstevel@tonic-gate case AF_INET6: 3338*0Sstevel@tonic-gate if (IN6_IS_ADDR_V4MAPPED(&addr.sin6.sin6_addr)) 3339*0Sstevel@tonic-gate family = SMFIA_INET; 3340*0Sstevel@tonic-gate else 3341*0Sstevel@tonic-gate family = SMFIA_INET6; 3342*0Sstevel@tonic-gate port = addr.sin6.sin6_port; 3343*0Sstevel@tonic-gate sockinfo = anynet_ntop(&addr.sin6.sin6_addr, buf6, 3344*0Sstevel@tonic-gate sizeof buf6); 3345*0Sstevel@tonic-gate if (sockinfo == NULL) 3346*0Sstevel@tonic-gate sockinfo = ""; 3347*0Sstevel@tonic-gate break; 3348*0Sstevel@tonic-gate # endif /* NETINET6 */ 3349*0Sstevel@tonic-gate 3350*0Sstevel@tonic-gate default: 3351*0Sstevel@tonic-gate family = SMFIA_UNKNOWN; 3352*0Sstevel@tonic-gate break; 3353*0Sstevel@tonic-gate } 3354*0Sstevel@tonic-gate 3355*0Sstevel@tonic-gate s = strlen(hostname) + 1 + sizeof(family); 3356*0Sstevel@tonic-gate if (family != SMFIA_UNKNOWN) 3357*0Sstevel@tonic-gate s += sizeof(port) + strlen(sockinfo) + 1; 3358*0Sstevel@tonic-gate 3359*0Sstevel@tonic-gate buf = (char *) xalloc(s); 3360*0Sstevel@tonic-gate bp = buf; 3361*0Sstevel@tonic-gate 3362*0Sstevel@tonic-gate /* put together data */ 3363*0Sstevel@tonic-gate (void) memcpy(bp, hostname, strlen(hostname)); 3364*0Sstevel@tonic-gate bp += strlen(hostname); 3365*0Sstevel@tonic-gate *bp++ = '\0'; 3366*0Sstevel@tonic-gate (void) memcpy(bp, &family, sizeof family); 3367*0Sstevel@tonic-gate bp += sizeof family; 3368*0Sstevel@tonic-gate if (family != SMFIA_UNKNOWN) 3369*0Sstevel@tonic-gate { 3370*0Sstevel@tonic-gate (void) memcpy(bp, &port, sizeof port); 3371*0Sstevel@tonic-gate bp += sizeof port; 3372*0Sstevel@tonic-gate 3373*0Sstevel@tonic-gate /* include trailing '\0' */ 3374*0Sstevel@tonic-gate (void) memcpy(bp, sockinfo, strlen(sockinfo) + 1); 3375*0Sstevel@tonic-gate } 3376*0Sstevel@tonic-gate 3377*0Sstevel@tonic-gate response = milter_command(SMFIC_CONNECT, buf, s, 3378*0Sstevel@tonic-gate MilterConnectMacros, e, state); 3379*0Sstevel@tonic-gate sm_free(buf); /* XXX */ 3380*0Sstevel@tonic-gate 3381*0Sstevel@tonic-gate /* 3382*0Sstevel@tonic-gate ** If this message connection is done for, 3383*0Sstevel@tonic-gate ** close the filters. 3384*0Sstevel@tonic-gate */ 3385*0Sstevel@tonic-gate 3386*0Sstevel@tonic-gate if (*state != SMFIR_CONTINUE) 3387*0Sstevel@tonic-gate { 3388*0Sstevel@tonic-gate if (MilterLogLevel > 9) 3389*0Sstevel@tonic-gate sm_syslog(LOG_INFO, e->e_id, "Milter: connect, ending"); 3390*0Sstevel@tonic-gate milter_quit(e); 3391*0Sstevel@tonic-gate } 3392*0Sstevel@tonic-gate else 3393*0Sstevel@tonic-gate milter_per_connection_check(e); 3394*0Sstevel@tonic-gate 3395*0Sstevel@tonic-gate /* 3396*0Sstevel@tonic-gate ** SMFIR_REPLYCODE can't work with connect due to 3397*0Sstevel@tonic-gate ** the requirements of SMTP. Therefore, ignore the 3398*0Sstevel@tonic-gate ** reply code text but keep the state it would reflect. 3399*0Sstevel@tonic-gate */ 3400*0Sstevel@tonic-gate 3401*0Sstevel@tonic-gate if (*state == SMFIR_REPLYCODE) 3402*0Sstevel@tonic-gate { 3403*0Sstevel@tonic-gate if (response != NULL && 3404*0Sstevel@tonic-gate *response == '4') 3405*0Sstevel@tonic-gate { 3406*0Sstevel@tonic-gate if (strncmp(response, "421 ", 4) == 0) 3407*0Sstevel@tonic-gate *state = SMFIR_SHUTDOWN; 3408*0Sstevel@tonic-gate else 3409*0Sstevel@tonic-gate *state = SMFIR_TEMPFAIL; 3410*0Sstevel@tonic-gate } 3411*0Sstevel@tonic-gate else 3412*0Sstevel@tonic-gate *state = SMFIR_REJECT; 3413*0Sstevel@tonic-gate if (response != NULL) 3414*0Sstevel@tonic-gate { 3415*0Sstevel@tonic-gate sm_free(response); /* XXX */ 3416*0Sstevel@tonic-gate response = NULL; 3417*0Sstevel@tonic-gate } 3418*0Sstevel@tonic-gate } 3419*0Sstevel@tonic-gate return response; 3420*0Sstevel@tonic-gate } 3421*0Sstevel@tonic-gate /* 3422*0Sstevel@tonic-gate ** MILTER_HELO -- send SMTP HELO/EHLO command info to milter filters 3423*0Sstevel@tonic-gate ** 3424*0Sstevel@tonic-gate ** Parameters: 3425*0Sstevel@tonic-gate ** helo -- argument to SMTP HELO/EHLO command. 3426*0Sstevel@tonic-gate ** e -- current envelope. 3427*0Sstevel@tonic-gate ** state -- return state from response. 3428*0Sstevel@tonic-gate ** 3429*0Sstevel@tonic-gate ** Returns: 3430*0Sstevel@tonic-gate ** response string (may be NULL) 3431*0Sstevel@tonic-gate */ 3432*0Sstevel@tonic-gate 3433*0Sstevel@tonic-gate char * 3434*0Sstevel@tonic-gate milter_helo(helo, e, state) 3435*0Sstevel@tonic-gate char *helo; 3436*0Sstevel@tonic-gate ENVELOPE *e; 3437*0Sstevel@tonic-gate char *state; 3438*0Sstevel@tonic-gate { 3439*0Sstevel@tonic-gate int i; 3440*0Sstevel@tonic-gate char *response; 3441*0Sstevel@tonic-gate 3442*0Sstevel@tonic-gate if (tTd(64, 10)) 3443*0Sstevel@tonic-gate sm_dprintf("milter_helo(%s)\n", helo); 3444*0Sstevel@tonic-gate 3445*0Sstevel@tonic-gate /* HELO/EHLO can come at any point */ 3446*0Sstevel@tonic-gate for (i = 0; InputFilters[i] != NULL; i++) 3447*0Sstevel@tonic-gate { 3448*0Sstevel@tonic-gate struct milter *m = InputFilters[i]; 3449*0Sstevel@tonic-gate 3450*0Sstevel@tonic-gate switch (m->mf_state) 3451*0Sstevel@tonic-gate { 3452*0Sstevel@tonic-gate case SMFS_INMSG: 3453*0Sstevel@tonic-gate /* abort in message filters */ 3454*0Sstevel@tonic-gate milter_abort_filter(m, e); 3455*0Sstevel@tonic-gate /* FALLTHROUGH */ 3456*0Sstevel@tonic-gate 3457*0Sstevel@tonic-gate case SMFS_DONE: 3458*0Sstevel@tonic-gate /* reset done filters */ 3459*0Sstevel@tonic-gate m->mf_state = SMFS_OPEN; 3460*0Sstevel@tonic-gate break; 3461*0Sstevel@tonic-gate } 3462*0Sstevel@tonic-gate } 3463*0Sstevel@tonic-gate 3464*0Sstevel@tonic-gate response = milter_command(SMFIC_HELO, helo, strlen(helo) + 1, 3465*0Sstevel@tonic-gate MilterHeloMacros, e, state); 3466*0Sstevel@tonic-gate milter_per_connection_check(e); 3467*0Sstevel@tonic-gate return response; 3468*0Sstevel@tonic-gate } 3469*0Sstevel@tonic-gate /* 3470*0Sstevel@tonic-gate ** MILTER_ENVFROM -- send SMTP MAIL command info to milter filters 3471*0Sstevel@tonic-gate ** 3472*0Sstevel@tonic-gate ** Parameters: 3473*0Sstevel@tonic-gate ** args -- SMTP MAIL command args (args[0] == sender). 3474*0Sstevel@tonic-gate ** e -- current envelope. 3475*0Sstevel@tonic-gate ** state -- return state from response. 3476*0Sstevel@tonic-gate ** 3477*0Sstevel@tonic-gate ** Returns: 3478*0Sstevel@tonic-gate ** response string (may be NULL) 3479*0Sstevel@tonic-gate */ 3480*0Sstevel@tonic-gate 3481*0Sstevel@tonic-gate char * 3482*0Sstevel@tonic-gate milter_envfrom(args, e, state) 3483*0Sstevel@tonic-gate char **args; 3484*0Sstevel@tonic-gate ENVELOPE *e; 3485*0Sstevel@tonic-gate char *state; 3486*0Sstevel@tonic-gate { 3487*0Sstevel@tonic-gate int i; 3488*0Sstevel@tonic-gate char *buf, *bp; 3489*0Sstevel@tonic-gate char *response; 3490*0Sstevel@tonic-gate ssize_t s; 3491*0Sstevel@tonic-gate 3492*0Sstevel@tonic-gate if (tTd(64, 10)) 3493*0Sstevel@tonic-gate { 3494*0Sstevel@tonic-gate sm_dprintf("milter_envfrom:"); 3495*0Sstevel@tonic-gate for (i = 0; args[i] != NULL; i++) 3496*0Sstevel@tonic-gate sm_dprintf(" %s", args[i]); 3497*0Sstevel@tonic-gate sm_dprintf("\n"); 3498*0Sstevel@tonic-gate } 3499*0Sstevel@tonic-gate 3500*0Sstevel@tonic-gate /* sanity check */ 3501*0Sstevel@tonic-gate if (args[0] == NULL) 3502*0Sstevel@tonic-gate { 3503*0Sstevel@tonic-gate *state = SMFIR_REJECT; 3504*0Sstevel@tonic-gate if (MilterLogLevel > 10) 3505*0Sstevel@tonic-gate sm_syslog(LOG_INFO, e->e_id, 3506*0Sstevel@tonic-gate "Milter: reject, no sender"); 3507*0Sstevel@tonic-gate return NULL; 3508*0Sstevel@tonic-gate } 3509*0Sstevel@tonic-gate 3510*0Sstevel@tonic-gate /* new message, so ... */ 3511*0Sstevel@tonic-gate for (i = 0; InputFilters[i] != NULL; i++) 3512*0Sstevel@tonic-gate { 3513*0Sstevel@tonic-gate struct milter *m = InputFilters[i]; 3514*0Sstevel@tonic-gate 3515*0Sstevel@tonic-gate switch (m->mf_state) 3516*0Sstevel@tonic-gate { 3517*0Sstevel@tonic-gate case SMFS_INMSG: 3518*0Sstevel@tonic-gate /* abort in message filters */ 3519*0Sstevel@tonic-gate milter_abort_filter(m, e); 3520*0Sstevel@tonic-gate /* FALLTHROUGH */ 3521*0Sstevel@tonic-gate 3522*0Sstevel@tonic-gate case SMFS_DONE: 3523*0Sstevel@tonic-gate /* reset done filters */ 3524*0Sstevel@tonic-gate m->mf_state = SMFS_OPEN; 3525*0Sstevel@tonic-gate break; 3526*0Sstevel@tonic-gate } 3527*0Sstevel@tonic-gate } 3528*0Sstevel@tonic-gate 3529*0Sstevel@tonic-gate /* put together data */ 3530*0Sstevel@tonic-gate s = 0; 3531*0Sstevel@tonic-gate for (i = 0; args[i] != NULL; i++) 3532*0Sstevel@tonic-gate s += strlen(args[i]) + 1; 3533*0Sstevel@tonic-gate 3534*0Sstevel@tonic-gate if (s < 0) 3535*0Sstevel@tonic-gate { 3536*0Sstevel@tonic-gate *state = SMFIR_TEMPFAIL; 3537*0Sstevel@tonic-gate return NULL; 3538*0Sstevel@tonic-gate } 3539*0Sstevel@tonic-gate 3540*0Sstevel@tonic-gate buf = (char *) xalloc(s); 3541*0Sstevel@tonic-gate bp = buf; 3542*0Sstevel@tonic-gate for (i = 0; args[i] != NULL; i++) 3543*0Sstevel@tonic-gate { 3544*0Sstevel@tonic-gate (void) sm_strlcpy(bp, args[i], s - (bp - buf)); 3545*0Sstevel@tonic-gate bp += strlen(bp) + 1; 3546*0Sstevel@tonic-gate } 3547*0Sstevel@tonic-gate 3548*0Sstevel@tonic-gate if (MilterLogLevel > 14) 3549*0Sstevel@tonic-gate sm_syslog(LOG_INFO, e->e_id, "Milter: senders: %s", buf); 3550*0Sstevel@tonic-gate 3551*0Sstevel@tonic-gate /* send it over */ 3552*0Sstevel@tonic-gate response = milter_command(SMFIC_MAIL, buf, s, 3553*0Sstevel@tonic-gate MilterEnvFromMacros, e, state); 3554*0Sstevel@tonic-gate sm_free(buf); /* XXX */ 3555*0Sstevel@tonic-gate 3556*0Sstevel@tonic-gate /* 3557*0Sstevel@tonic-gate ** If filter rejects/discards a per message command, 3558*0Sstevel@tonic-gate ** abort the other filters since we are done with the 3559*0Sstevel@tonic-gate ** current message. 3560*0Sstevel@tonic-gate */ 3561*0Sstevel@tonic-gate 3562*0Sstevel@tonic-gate MILTER_CHECK_DONE_MSG(); 3563*0Sstevel@tonic-gate if (MilterLogLevel > 10 && *state == SMFIR_REJECT) 3564*0Sstevel@tonic-gate sm_syslog(LOG_INFO, e->e_id, "Milter: reject, senders"); 3565*0Sstevel@tonic-gate return response; 3566*0Sstevel@tonic-gate } 3567*0Sstevel@tonic-gate 3568*0Sstevel@tonic-gate /* 3569*0Sstevel@tonic-gate ** MILTER_ENVRCPT -- send SMTP RCPT command info to milter filters 3570*0Sstevel@tonic-gate ** 3571*0Sstevel@tonic-gate ** Parameters: 3572*0Sstevel@tonic-gate ** args -- SMTP MAIL command args (args[0] == recipient). 3573*0Sstevel@tonic-gate ** e -- current envelope. 3574*0Sstevel@tonic-gate ** state -- return state from response. 3575*0Sstevel@tonic-gate ** 3576*0Sstevel@tonic-gate ** Returns: 3577*0Sstevel@tonic-gate ** response string (may be NULL) 3578*0Sstevel@tonic-gate */ 3579*0Sstevel@tonic-gate 3580*0Sstevel@tonic-gate char * 3581*0Sstevel@tonic-gate milter_envrcpt(args, e, state) 3582*0Sstevel@tonic-gate char **args; 3583*0Sstevel@tonic-gate ENVELOPE *e; 3584*0Sstevel@tonic-gate char *state; 3585*0Sstevel@tonic-gate { 3586*0Sstevel@tonic-gate int i; 3587*0Sstevel@tonic-gate char *buf, *bp; 3588*0Sstevel@tonic-gate char *response; 3589*0Sstevel@tonic-gate ssize_t s; 3590*0Sstevel@tonic-gate 3591*0Sstevel@tonic-gate if (tTd(64, 10)) 3592*0Sstevel@tonic-gate { 3593*0Sstevel@tonic-gate sm_dprintf("milter_envrcpt:"); 3594*0Sstevel@tonic-gate for (i = 0; args[i] != NULL; i++) 3595*0Sstevel@tonic-gate sm_dprintf(" %s", args[i]); 3596*0Sstevel@tonic-gate sm_dprintf("\n"); 3597*0Sstevel@tonic-gate } 3598*0Sstevel@tonic-gate 3599*0Sstevel@tonic-gate /* sanity check */ 3600*0Sstevel@tonic-gate if (args[0] == NULL) 3601*0Sstevel@tonic-gate { 3602*0Sstevel@tonic-gate *state = SMFIR_REJECT; 3603*0Sstevel@tonic-gate if (MilterLogLevel > 10) 3604*0Sstevel@tonic-gate sm_syslog(LOG_INFO, e->e_id, "Milter: reject, no rcpt"); 3605*0Sstevel@tonic-gate return NULL; 3606*0Sstevel@tonic-gate } 3607*0Sstevel@tonic-gate 3608*0Sstevel@tonic-gate /* put together data */ 3609*0Sstevel@tonic-gate s = 0; 3610*0Sstevel@tonic-gate for (i = 0; args[i] != NULL; i++) 3611*0Sstevel@tonic-gate s += strlen(args[i]) + 1; 3612*0Sstevel@tonic-gate 3613*0Sstevel@tonic-gate if (s < 0) 3614*0Sstevel@tonic-gate { 3615*0Sstevel@tonic-gate *state = SMFIR_TEMPFAIL; 3616*0Sstevel@tonic-gate return NULL; 3617*0Sstevel@tonic-gate } 3618*0Sstevel@tonic-gate 3619*0Sstevel@tonic-gate buf = (char *) xalloc(s); 3620*0Sstevel@tonic-gate bp = buf; 3621*0Sstevel@tonic-gate for (i = 0; args[i] != NULL; i++) 3622*0Sstevel@tonic-gate { 3623*0Sstevel@tonic-gate (void) sm_strlcpy(bp, args[i], s - (bp - buf)); 3624*0Sstevel@tonic-gate bp += strlen(bp) + 1; 3625*0Sstevel@tonic-gate } 3626*0Sstevel@tonic-gate 3627*0Sstevel@tonic-gate if (MilterLogLevel > 14) 3628*0Sstevel@tonic-gate sm_syslog(LOG_INFO, e->e_id, "Milter: rcpts: %s", buf); 3629*0Sstevel@tonic-gate 3630*0Sstevel@tonic-gate /* send it over */ 3631*0Sstevel@tonic-gate response = milter_command(SMFIC_RCPT, buf, s, 3632*0Sstevel@tonic-gate MilterEnvRcptMacros, e, state); 3633*0Sstevel@tonic-gate sm_free(buf); /* XXX */ 3634*0Sstevel@tonic-gate return response; 3635*0Sstevel@tonic-gate } 3636*0Sstevel@tonic-gate 3637*0Sstevel@tonic-gate #if SMFI_VERSION > 3 3638*0Sstevel@tonic-gate /* 3639*0Sstevel@tonic-gate ** MILTER_DATA_CMD -- send SMTP DATA command info to milter filters 3640*0Sstevel@tonic-gate ** 3641*0Sstevel@tonic-gate ** Parameters: 3642*0Sstevel@tonic-gate ** e -- current envelope. 3643*0Sstevel@tonic-gate ** state -- return state from response. 3644*0Sstevel@tonic-gate ** 3645*0Sstevel@tonic-gate ** Returns: 3646*0Sstevel@tonic-gate ** response string (may be NULL) 3647*0Sstevel@tonic-gate */ 3648*0Sstevel@tonic-gate 3649*0Sstevel@tonic-gate char * 3650*0Sstevel@tonic-gate milter_data_cmd(e, state) 3651*0Sstevel@tonic-gate ENVELOPE *e; 3652*0Sstevel@tonic-gate char *state; 3653*0Sstevel@tonic-gate { 3654*0Sstevel@tonic-gate if (tTd(64, 10)) 3655*0Sstevel@tonic-gate sm_dprintf("milter_data_cmd\n"); 3656*0Sstevel@tonic-gate 3657*0Sstevel@tonic-gate /* send it over */ 3658*0Sstevel@tonic-gate return milter_command(SMFIC_DATA, NULL, 0, MilterDataMacros, e, state); 3659*0Sstevel@tonic-gate } 3660*0Sstevel@tonic-gate #endif /* SMFI_VERSION > 3 */ 3661*0Sstevel@tonic-gate 3662*0Sstevel@tonic-gate /* 3663*0Sstevel@tonic-gate ** MILTER_DATA -- send message headers/body and gather final message results 3664*0Sstevel@tonic-gate ** 3665*0Sstevel@tonic-gate ** Parameters: 3666*0Sstevel@tonic-gate ** e -- current envelope. 3667*0Sstevel@tonic-gate ** state -- return state from response. 3668*0Sstevel@tonic-gate ** 3669*0Sstevel@tonic-gate ** Returns: 3670*0Sstevel@tonic-gate ** response string (may be NULL) 3671*0Sstevel@tonic-gate ** 3672*0Sstevel@tonic-gate ** Side effects: 3673*0Sstevel@tonic-gate ** - Uses e->e_dfp for access to the body 3674*0Sstevel@tonic-gate ** - Can call the various milter action routines to 3675*0Sstevel@tonic-gate ** modify the envelope or message. 3676*0Sstevel@tonic-gate */ 3677*0Sstevel@tonic-gate 3678*0Sstevel@tonic-gate # define MILTER_CHECK_RESULTS() \ 3679*0Sstevel@tonic-gate if (*state == SMFIR_ACCEPT || \ 3680*0Sstevel@tonic-gate m->mf_state == SMFS_DONE || \ 3681*0Sstevel@tonic-gate m->mf_state == SMFS_ERROR) \ 3682*0Sstevel@tonic-gate { \ 3683*0Sstevel@tonic-gate if (m->mf_state != SMFS_ERROR) \ 3684*0Sstevel@tonic-gate m->mf_state = SMFS_DONE; \ 3685*0Sstevel@tonic-gate continue; /* to next filter */ \ 3686*0Sstevel@tonic-gate } \ 3687*0Sstevel@tonic-gate if (*state != SMFIR_CONTINUE) \ 3688*0Sstevel@tonic-gate { \ 3689*0Sstevel@tonic-gate m->mf_state = SMFS_DONE; \ 3690*0Sstevel@tonic-gate goto finishup; \ 3691*0Sstevel@tonic-gate } 3692*0Sstevel@tonic-gate 3693*0Sstevel@tonic-gate char * 3694*0Sstevel@tonic-gate milter_data(e, state) 3695*0Sstevel@tonic-gate ENVELOPE *e; 3696*0Sstevel@tonic-gate char *state; 3697*0Sstevel@tonic-gate { 3698*0Sstevel@tonic-gate bool replbody = false; /* milter_replbody() called? */ 3699*0Sstevel@tonic-gate bool replfailed = false; /* milter_replbody() failed? */ 3700*0Sstevel@tonic-gate bool rewind = false; /* rewind data file? */ 3701*0Sstevel@tonic-gate bool dfopen = false; /* data file open for writing? */ 3702*0Sstevel@tonic-gate bool newfilter; /* reset on each new filter */ 3703*0Sstevel@tonic-gate char rcmd; 3704*0Sstevel@tonic-gate int i; 3705*0Sstevel@tonic-gate int save_errno; 3706*0Sstevel@tonic-gate char *response = NULL; 3707*0Sstevel@tonic-gate time_t eomsent; 3708*0Sstevel@tonic-gate ssize_t rlen; 3709*0Sstevel@tonic-gate 3710*0Sstevel@tonic-gate if (tTd(64, 10)) 3711*0Sstevel@tonic-gate sm_dprintf("milter_data\n"); 3712*0Sstevel@tonic-gate 3713*0Sstevel@tonic-gate *state = SMFIR_CONTINUE; 3714*0Sstevel@tonic-gate 3715*0Sstevel@tonic-gate /* 3716*0Sstevel@tonic-gate ** XXX: Should actually send body chunks to each filter 3717*0Sstevel@tonic-gate ** a chunk at a time instead of sending the whole body to 3718*0Sstevel@tonic-gate ** each filter in turn. However, only if the filters don't 3719*0Sstevel@tonic-gate ** change the body. 3720*0Sstevel@tonic-gate */ 3721*0Sstevel@tonic-gate 3722*0Sstevel@tonic-gate for (i = 0; InputFilters[i] != NULL; i++) 3723*0Sstevel@tonic-gate { 3724*0Sstevel@tonic-gate struct milter *m = InputFilters[i]; 3725*0Sstevel@tonic-gate 3726*0Sstevel@tonic-gate if (*state != SMFIR_CONTINUE && 3727*0Sstevel@tonic-gate *state != SMFIR_ACCEPT) 3728*0Sstevel@tonic-gate { 3729*0Sstevel@tonic-gate /* 3730*0Sstevel@tonic-gate ** A previous filter has dealt with the message, 3731*0Sstevel@tonic-gate ** safe to stop processing the filters. 3732*0Sstevel@tonic-gate */ 3733*0Sstevel@tonic-gate 3734*0Sstevel@tonic-gate break; 3735*0Sstevel@tonic-gate } 3736*0Sstevel@tonic-gate 3737*0Sstevel@tonic-gate /* Now reset state for later evaluation */ 3738*0Sstevel@tonic-gate *state = SMFIR_CONTINUE; 3739*0Sstevel@tonic-gate newfilter = true; 3740*0Sstevel@tonic-gate 3741*0Sstevel@tonic-gate /* previous problem? */ 3742*0Sstevel@tonic-gate if (m->mf_state == SMFS_ERROR) 3743*0Sstevel@tonic-gate { 3744*0Sstevel@tonic-gate MILTER_CHECK_ERROR(false, continue); 3745*0Sstevel@tonic-gate break; 3746*0Sstevel@tonic-gate } 3747*0Sstevel@tonic-gate 3748*0Sstevel@tonic-gate /* sanity checks */ 3749*0Sstevel@tonic-gate if (m->mf_sock < 0 || 3750*0Sstevel@tonic-gate (m->mf_state != SMFS_OPEN && m->mf_state != SMFS_INMSG)) 3751*0Sstevel@tonic-gate continue; 3752*0Sstevel@tonic-gate 3753*0Sstevel@tonic-gate m->mf_state = SMFS_INMSG; 3754*0Sstevel@tonic-gate 3755*0Sstevel@tonic-gate /* check if filter wants the headers */ 3756*0Sstevel@tonic-gate if (!bitset(SMFIP_NOHDRS, m->mf_pflags)) 3757*0Sstevel@tonic-gate { 3758*0Sstevel@tonic-gate response = milter_headers(m, e, state); 3759*0Sstevel@tonic-gate MILTER_CHECK_RESULTS(); 3760*0Sstevel@tonic-gate } 3761*0Sstevel@tonic-gate 3762*0Sstevel@tonic-gate /* check if filter wants EOH */ 3763*0Sstevel@tonic-gate if (!bitset(SMFIP_NOEOH, m->mf_pflags)) 3764*0Sstevel@tonic-gate { 3765*0Sstevel@tonic-gate if (tTd(64, 10)) 3766*0Sstevel@tonic-gate sm_dprintf("milter_data: eoh\n"); 3767*0Sstevel@tonic-gate 3768*0Sstevel@tonic-gate /* send it over */ 3769*0Sstevel@tonic-gate response = milter_send_command(m, SMFIC_EOH, NULL, 0, 3770*0Sstevel@tonic-gate e, state); 3771*0Sstevel@tonic-gate MILTER_CHECK_RESULTS(); 3772*0Sstevel@tonic-gate } 3773*0Sstevel@tonic-gate 3774*0Sstevel@tonic-gate /* check if filter wants the body */ 3775*0Sstevel@tonic-gate if (!bitset(SMFIP_NOBODY, m->mf_pflags) && 3776*0Sstevel@tonic-gate e->e_dfp != NULL) 3777*0Sstevel@tonic-gate { 3778*0Sstevel@tonic-gate rewind = true; 3779*0Sstevel@tonic-gate response = milter_body(m, e, state); 3780*0Sstevel@tonic-gate MILTER_CHECK_RESULTS(); 3781*0Sstevel@tonic-gate } 3782*0Sstevel@tonic-gate 3783*0Sstevel@tonic-gate if (MilterEOMMacros[0] != NULL) 3784*0Sstevel@tonic-gate { 3785*0Sstevel@tonic-gate milter_send_macros(m, MilterEOMMacros, 3786*0Sstevel@tonic-gate SMFIC_BODYEOB, e); 3787*0Sstevel@tonic-gate MILTER_CHECK_RESULTS(); 3788*0Sstevel@tonic-gate } 3789*0Sstevel@tonic-gate 3790*0Sstevel@tonic-gate /* send the final body chunk */ 3791*0Sstevel@tonic-gate (void) milter_write(m, SMFIC_BODYEOB, NULL, 0, 3792*0Sstevel@tonic-gate m->mf_timeout[SMFTO_WRITE], e); 3793*0Sstevel@tonic-gate 3794*0Sstevel@tonic-gate /* Get time EOM sent for timeout */ 3795*0Sstevel@tonic-gate eomsent = curtime(); 3796*0Sstevel@tonic-gate 3797*0Sstevel@tonic-gate /* deal with the possibility of multiple responses */ 3798*0Sstevel@tonic-gate while (*state == SMFIR_CONTINUE) 3799*0Sstevel@tonic-gate { 3800*0Sstevel@tonic-gate /* Check total timeout from EOM to final ACK/NAK */ 3801*0Sstevel@tonic-gate if (m->mf_timeout[SMFTO_EOM] > 0 && 3802*0Sstevel@tonic-gate curtime() - eomsent >= m->mf_timeout[SMFTO_EOM]) 3803*0Sstevel@tonic-gate { 3804*0Sstevel@tonic-gate if (tTd(64, 5)) 3805*0Sstevel@tonic-gate sm_dprintf("milter_data(%s): EOM ACK/NAK timeout\n", 3806*0Sstevel@tonic-gate m->mf_name); 3807*0Sstevel@tonic-gate if (MilterLogLevel > 0) 3808*0Sstevel@tonic-gate sm_syslog(LOG_ERR, e->e_id, 3809*0Sstevel@tonic-gate "milter_data(%s): EOM ACK/NAK timeout", 3810*0Sstevel@tonic-gate m->mf_name); 3811*0Sstevel@tonic-gate milter_error(m, e); 3812*0Sstevel@tonic-gate MILTER_CHECK_ERROR(false, break); 3813*0Sstevel@tonic-gate break; 3814*0Sstevel@tonic-gate } 3815*0Sstevel@tonic-gate 3816*0Sstevel@tonic-gate response = milter_read(m, &rcmd, &rlen, 3817*0Sstevel@tonic-gate m->mf_timeout[SMFTO_READ], e); 3818*0Sstevel@tonic-gate if (m->mf_state == SMFS_ERROR) 3819*0Sstevel@tonic-gate break; 3820*0Sstevel@tonic-gate 3821*0Sstevel@tonic-gate if (tTd(64, 10)) 3822*0Sstevel@tonic-gate sm_dprintf("milter_data(%s): state %c\n", 3823*0Sstevel@tonic-gate m->mf_name, (char) rcmd); 3824*0Sstevel@tonic-gate 3825*0Sstevel@tonic-gate switch (rcmd) 3826*0Sstevel@tonic-gate { 3827*0Sstevel@tonic-gate case SMFIR_REPLYCODE: 3828*0Sstevel@tonic-gate MILTER_CHECK_REPLYCODE("554 5.7.1 Command rejected"); 3829*0Sstevel@tonic-gate if (MilterLogLevel > 12) 3830*0Sstevel@tonic-gate sm_syslog(LOG_INFO, e->e_id, "milter=%s, reject=%s", 3831*0Sstevel@tonic-gate m->mf_name, response); 3832*0Sstevel@tonic-gate *state = rcmd; 3833*0Sstevel@tonic-gate m->mf_state = SMFS_DONE; 3834*0Sstevel@tonic-gate break; 3835*0Sstevel@tonic-gate 3836*0Sstevel@tonic-gate case SMFIR_REJECT: /* log msg at end of function */ 3837*0Sstevel@tonic-gate if (MilterLogLevel > 12) 3838*0Sstevel@tonic-gate sm_syslog(LOG_INFO, e->e_id, "milter=%s, reject", 3839*0Sstevel@tonic-gate m->mf_name); 3840*0Sstevel@tonic-gate *state = rcmd; 3841*0Sstevel@tonic-gate m->mf_state = SMFS_DONE; 3842*0Sstevel@tonic-gate break; 3843*0Sstevel@tonic-gate 3844*0Sstevel@tonic-gate case SMFIR_DISCARD: 3845*0Sstevel@tonic-gate if (MilterLogLevel > 12) 3846*0Sstevel@tonic-gate sm_syslog(LOG_INFO, e->e_id, "milter=%s, discard", 3847*0Sstevel@tonic-gate m->mf_name); 3848*0Sstevel@tonic-gate *state = rcmd; 3849*0Sstevel@tonic-gate m->mf_state = SMFS_DONE; 3850*0Sstevel@tonic-gate break; 3851*0Sstevel@tonic-gate 3852*0Sstevel@tonic-gate case SMFIR_TEMPFAIL: 3853*0Sstevel@tonic-gate if (MilterLogLevel > 12) 3854*0Sstevel@tonic-gate sm_syslog(LOG_INFO, e->e_id, "milter=%s, tempfail", 3855*0Sstevel@tonic-gate m->mf_name); 3856*0Sstevel@tonic-gate *state = rcmd; 3857*0Sstevel@tonic-gate m->mf_state = SMFS_DONE; 3858*0Sstevel@tonic-gate break; 3859*0Sstevel@tonic-gate 3860*0Sstevel@tonic-gate case SMFIR_CONTINUE: 3861*0Sstevel@tonic-gate case SMFIR_ACCEPT: 3862*0Sstevel@tonic-gate /* this filter is done with message */ 3863*0Sstevel@tonic-gate if (replfailed) 3864*0Sstevel@tonic-gate *state = SMFIR_TEMPFAIL; 3865*0Sstevel@tonic-gate else 3866*0Sstevel@tonic-gate *state = SMFIR_ACCEPT; 3867*0Sstevel@tonic-gate m->mf_state = SMFS_DONE; 3868*0Sstevel@tonic-gate break; 3869*0Sstevel@tonic-gate 3870*0Sstevel@tonic-gate case SMFIR_PROGRESS: 3871*0Sstevel@tonic-gate break; 3872*0Sstevel@tonic-gate 3873*0Sstevel@tonic-gate case SMFIR_QUARANTINE: 3874*0Sstevel@tonic-gate if (!bitset(SMFIF_QUARANTINE, m->mf_fflags)) 3875*0Sstevel@tonic-gate { 3876*0Sstevel@tonic-gate if (MilterLogLevel > 9) 3877*0Sstevel@tonic-gate sm_syslog(LOG_WARNING, e->e_id, 3878*0Sstevel@tonic-gate "milter_data(%s): lied about quarantining, honoring request anyway", 3879*0Sstevel@tonic-gate m->mf_name); 3880*0Sstevel@tonic-gate } 3881*0Sstevel@tonic-gate if (response == NULL) 3882*0Sstevel@tonic-gate response = newstr(""); 3883*0Sstevel@tonic-gate if (MilterLogLevel > 3) 3884*0Sstevel@tonic-gate sm_syslog(LOG_INFO, e->e_id, 3885*0Sstevel@tonic-gate "milter=%s, quarantine=%s", 3886*0Sstevel@tonic-gate m->mf_name, response); 3887*0Sstevel@tonic-gate e->e_quarmsg = sm_rpool_strdup_x(e->e_rpool, 3888*0Sstevel@tonic-gate response); 3889*0Sstevel@tonic-gate macdefine(&e->e_macro, A_PERM, 3890*0Sstevel@tonic-gate macid("{quarantine}"), e->e_quarmsg); 3891*0Sstevel@tonic-gate break; 3892*0Sstevel@tonic-gate 3893*0Sstevel@tonic-gate case SMFIR_ADDHEADER: 3894*0Sstevel@tonic-gate if (!bitset(SMFIF_ADDHDRS, m->mf_fflags)) 3895*0Sstevel@tonic-gate { 3896*0Sstevel@tonic-gate if (MilterLogLevel > 9) 3897*0Sstevel@tonic-gate sm_syslog(LOG_WARNING, e->e_id, 3898*0Sstevel@tonic-gate "milter_data(%s): lied about adding headers, honoring request anyway", 3899*0Sstevel@tonic-gate m->mf_name); 3900*0Sstevel@tonic-gate } 3901*0Sstevel@tonic-gate milter_addheader(response, rlen, e); 3902*0Sstevel@tonic-gate break; 3903*0Sstevel@tonic-gate 3904*0Sstevel@tonic-gate case SMFIR_INSHEADER: 3905*0Sstevel@tonic-gate if (!bitset(SMFIF_ADDHDRS, m->mf_fflags)) 3906*0Sstevel@tonic-gate { 3907*0Sstevel@tonic-gate if (MilterLogLevel > 9) 3908*0Sstevel@tonic-gate sm_syslog(LOG_WARNING, e->e_id, 3909*0Sstevel@tonic-gate "milter_data(%s): lied about adding headers, honoring request anyway", 3910*0Sstevel@tonic-gate m->mf_name); 3911*0Sstevel@tonic-gate } 3912*0Sstevel@tonic-gate milter_insheader(response, rlen, e); 3913*0Sstevel@tonic-gate break; 3914*0Sstevel@tonic-gate 3915*0Sstevel@tonic-gate case SMFIR_CHGHEADER: 3916*0Sstevel@tonic-gate if (!bitset(SMFIF_CHGHDRS, m->mf_fflags)) 3917*0Sstevel@tonic-gate { 3918*0Sstevel@tonic-gate if (MilterLogLevel > 9) 3919*0Sstevel@tonic-gate sm_syslog(LOG_WARNING, e->e_id, 3920*0Sstevel@tonic-gate "milter_data(%s): lied about changing headers, honoring request anyway", 3921*0Sstevel@tonic-gate m->mf_name); 3922*0Sstevel@tonic-gate } 3923*0Sstevel@tonic-gate milter_changeheader(response, rlen, e); 3924*0Sstevel@tonic-gate break; 3925*0Sstevel@tonic-gate 3926*0Sstevel@tonic-gate case SMFIR_ADDRCPT: 3927*0Sstevel@tonic-gate if (!bitset(SMFIF_ADDRCPT, m->mf_fflags)) 3928*0Sstevel@tonic-gate { 3929*0Sstevel@tonic-gate if (MilterLogLevel > 9) 3930*0Sstevel@tonic-gate sm_syslog(LOG_WARNING, e->e_id, 3931*0Sstevel@tonic-gate "milter_data(%s) lied about adding recipients, honoring request anyway", 3932*0Sstevel@tonic-gate m->mf_name); 3933*0Sstevel@tonic-gate } 3934*0Sstevel@tonic-gate milter_addrcpt(response, rlen, e); 3935*0Sstevel@tonic-gate break; 3936*0Sstevel@tonic-gate 3937*0Sstevel@tonic-gate case SMFIR_DELRCPT: 3938*0Sstevel@tonic-gate if (!bitset(SMFIF_DELRCPT, m->mf_fflags)) 3939*0Sstevel@tonic-gate { 3940*0Sstevel@tonic-gate if (MilterLogLevel > 9) 3941*0Sstevel@tonic-gate sm_syslog(LOG_WARNING, e->e_id, 3942*0Sstevel@tonic-gate "milter_data(%s): lied about removing recipients, honoring request anyway", 3943*0Sstevel@tonic-gate m->mf_name); 3944*0Sstevel@tonic-gate } 3945*0Sstevel@tonic-gate milter_delrcpt(response, rlen, e); 3946*0Sstevel@tonic-gate break; 3947*0Sstevel@tonic-gate 3948*0Sstevel@tonic-gate case SMFIR_REPLBODY: 3949*0Sstevel@tonic-gate if (!bitset(SMFIF_MODBODY, m->mf_fflags)) 3950*0Sstevel@tonic-gate { 3951*0Sstevel@tonic-gate if (MilterLogLevel > 0) 3952*0Sstevel@tonic-gate sm_syslog(LOG_ERR, e->e_id, 3953*0Sstevel@tonic-gate "milter_data(%s): lied about replacing body, rejecting request and tempfailing message", 3954*0Sstevel@tonic-gate m->mf_name); 3955*0Sstevel@tonic-gate replfailed = true; 3956*0Sstevel@tonic-gate break; 3957*0Sstevel@tonic-gate } 3958*0Sstevel@tonic-gate 3959*0Sstevel@tonic-gate /* already failed in attempt */ 3960*0Sstevel@tonic-gate if (replfailed) 3961*0Sstevel@tonic-gate break; 3962*0Sstevel@tonic-gate 3963*0Sstevel@tonic-gate if (!dfopen) 3964*0Sstevel@tonic-gate { 3965*0Sstevel@tonic-gate if (milter_reopen_df(e) < 0) 3966*0Sstevel@tonic-gate { 3967*0Sstevel@tonic-gate replfailed = true; 3968*0Sstevel@tonic-gate break; 3969*0Sstevel@tonic-gate } 3970*0Sstevel@tonic-gate dfopen = true; 3971*0Sstevel@tonic-gate rewind = true; 3972*0Sstevel@tonic-gate } 3973*0Sstevel@tonic-gate 3974*0Sstevel@tonic-gate if (milter_replbody(response, rlen, 3975*0Sstevel@tonic-gate newfilter, e) < 0) 3976*0Sstevel@tonic-gate replfailed = true; 3977*0Sstevel@tonic-gate newfilter = false; 3978*0Sstevel@tonic-gate replbody = true; 3979*0Sstevel@tonic-gate break; 3980*0Sstevel@tonic-gate 3981*0Sstevel@tonic-gate default: 3982*0Sstevel@tonic-gate /* Invalid response to command */ 3983*0Sstevel@tonic-gate if (MilterLogLevel > 0) 3984*0Sstevel@tonic-gate sm_syslog(LOG_ERR, e->e_id, 3985*0Sstevel@tonic-gate "milter_data(%s): returned bogus response %c", 3986*0Sstevel@tonic-gate m->mf_name, rcmd); 3987*0Sstevel@tonic-gate milter_error(m, e); 3988*0Sstevel@tonic-gate break; 3989*0Sstevel@tonic-gate } 3990*0Sstevel@tonic-gate if (rcmd != SMFIR_REPLYCODE && response != NULL) 3991*0Sstevel@tonic-gate { 3992*0Sstevel@tonic-gate sm_free(response); /* XXX */ 3993*0Sstevel@tonic-gate response = NULL; 3994*0Sstevel@tonic-gate } 3995*0Sstevel@tonic-gate 3996*0Sstevel@tonic-gate if (m->mf_state == SMFS_ERROR) 3997*0Sstevel@tonic-gate break; 3998*0Sstevel@tonic-gate } 3999*0Sstevel@tonic-gate 4000*0Sstevel@tonic-gate if (replbody && !replfailed) 4001*0Sstevel@tonic-gate { 4002*0Sstevel@tonic-gate /* flush possible buffered character */ 4003*0Sstevel@tonic-gate milter_replbody(NULL, 0, !replbody, e); 4004*0Sstevel@tonic-gate replbody = false; 4005*0Sstevel@tonic-gate } 4006*0Sstevel@tonic-gate 4007*0Sstevel@tonic-gate if (m->mf_state == SMFS_ERROR) 4008*0Sstevel@tonic-gate { 4009*0Sstevel@tonic-gate MILTER_CHECK_ERROR(false, continue); 4010*0Sstevel@tonic-gate goto finishup; 4011*0Sstevel@tonic-gate } 4012*0Sstevel@tonic-gate } 4013*0Sstevel@tonic-gate 4014*0Sstevel@tonic-gate finishup: 4015*0Sstevel@tonic-gate /* leave things in the expected state if we touched it */ 4016*0Sstevel@tonic-gate if (replfailed) 4017*0Sstevel@tonic-gate { 4018*0Sstevel@tonic-gate if (*state == SMFIR_CONTINUE || 4019*0Sstevel@tonic-gate *state == SMFIR_ACCEPT) 4020*0Sstevel@tonic-gate { 4021*0Sstevel@tonic-gate *state = SMFIR_TEMPFAIL; 4022*0Sstevel@tonic-gate SM_FREE_CLR(response); 4023*0Sstevel@tonic-gate } 4024*0Sstevel@tonic-gate 4025*0Sstevel@tonic-gate if (dfopen) 4026*0Sstevel@tonic-gate { 4027*0Sstevel@tonic-gate (void) sm_io_close(e->e_dfp, SM_TIME_DEFAULT); 4028*0Sstevel@tonic-gate e->e_dfp = NULL; 4029*0Sstevel@tonic-gate e->e_flags &= ~EF_HAS_DF; 4030*0Sstevel@tonic-gate dfopen = false; 4031*0Sstevel@tonic-gate } 4032*0Sstevel@tonic-gate rewind = false; 4033*0Sstevel@tonic-gate } 4034*0Sstevel@tonic-gate 4035*0Sstevel@tonic-gate if ((dfopen && milter_reset_df(e) < 0) || 4036*0Sstevel@tonic-gate (rewind && bfrewind(e->e_dfp) < 0)) 4037*0Sstevel@tonic-gate { 4038*0Sstevel@tonic-gate save_errno = errno; 4039*0Sstevel@tonic-gate ExitStat = EX_IOERR; 4040*0Sstevel@tonic-gate 4041*0Sstevel@tonic-gate /* 4042*0Sstevel@tonic-gate ** If filter told us to keep message but we had 4043*0Sstevel@tonic-gate ** an error, we can't really keep it, tempfail it. 4044*0Sstevel@tonic-gate */ 4045*0Sstevel@tonic-gate 4046*0Sstevel@tonic-gate if (*state == SMFIR_CONTINUE || 4047*0Sstevel@tonic-gate *state == SMFIR_ACCEPT) 4048*0Sstevel@tonic-gate { 4049*0Sstevel@tonic-gate *state = SMFIR_TEMPFAIL; 4050*0Sstevel@tonic-gate SM_FREE_CLR(response); 4051*0Sstevel@tonic-gate } 4052*0Sstevel@tonic-gate 4053*0Sstevel@tonic-gate errno = save_errno; 4054*0Sstevel@tonic-gate syserr("milter_data: %s/%cf%s: read error", 4055*0Sstevel@tonic-gate qid_printqueue(e->e_qgrp, e->e_qdir), 4056*0Sstevel@tonic-gate DATAFL_LETTER, e->e_id); 4057*0Sstevel@tonic-gate } 4058*0Sstevel@tonic-gate 4059*0Sstevel@tonic-gate MILTER_CHECK_DONE_MSG(); 4060*0Sstevel@tonic-gate if (MilterLogLevel > 10 && *state == SMFIR_REJECT) 4061*0Sstevel@tonic-gate sm_syslog(LOG_INFO, e->e_id, "Milter: reject, data"); 4062*0Sstevel@tonic-gate return response; 4063*0Sstevel@tonic-gate } 4064*0Sstevel@tonic-gate 4065*0Sstevel@tonic-gate #if SMFI_VERSION > 2 4066*0Sstevel@tonic-gate /* 4067*0Sstevel@tonic-gate ** MILTER_UNKNOWN -- send any unrecognized or unimplemented command 4068*0Sstevel@tonic-gate ** string to milter filters 4069*0Sstevel@tonic-gate ** 4070*0Sstevel@tonic-gate ** Parameters: 4071*0Sstevel@tonic-gate ** cmd -- the string itself. 4072*0Sstevel@tonic-gate ** e -- current envelope. 4073*0Sstevel@tonic-gate ** state -- return state from response. 4074*0Sstevel@tonic-gate ** 4075*0Sstevel@tonic-gate ** 4076*0Sstevel@tonic-gate ** Returns: 4077*0Sstevel@tonic-gate ** response string (may be NULL) 4078*0Sstevel@tonic-gate */ 4079*0Sstevel@tonic-gate 4080*0Sstevel@tonic-gate char * 4081*0Sstevel@tonic-gate milter_unknown(cmd, e, state) 4082*0Sstevel@tonic-gate char *cmd; 4083*0Sstevel@tonic-gate ENVELOPE *e; 4084*0Sstevel@tonic-gate char *state; 4085*0Sstevel@tonic-gate { 4086*0Sstevel@tonic-gate if (tTd(64, 10)) 4087*0Sstevel@tonic-gate sm_dprintf("milter_unknown(%s)\n", cmd); 4088*0Sstevel@tonic-gate 4089*0Sstevel@tonic-gate return milter_command(SMFIC_UNKNOWN, cmd, strlen(cmd) + 1, 4090*0Sstevel@tonic-gate NULL, e, state); 4091*0Sstevel@tonic-gate } 4092*0Sstevel@tonic-gate #endif /* SMFI_VERSION > 2 */ 4093*0Sstevel@tonic-gate 4094*0Sstevel@tonic-gate /* 4095*0Sstevel@tonic-gate ** MILTER_QUIT -- informs the filter(s) we are done and closes connection(s) 4096*0Sstevel@tonic-gate ** 4097*0Sstevel@tonic-gate ** Parameters: 4098*0Sstevel@tonic-gate ** e -- current envelope. 4099*0Sstevel@tonic-gate ** 4100*0Sstevel@tonic-gate ** Returns: 4101*0Sstevel@tonic-gate ** none 4102*0Sstevel@tonic-gate */ 4103*0Sstevel@tonic-gate 4104*0Sstevel@tonic-gate void 4105*0Sstevel@tonic-gate milter_quit(e) 4106*0Sstevel@tonic-gate ENVELOPE *e; 4107*0Sstevel@tonic-gate { 4108*0Sstevel@tonic-gate int i; 4109*0Sstevel@tonic-gate 4110*0Sstevel@tonic-gate if (tTd(64, 10)) 4111*0Sstevel@tonic-gate sm_dprintf("milter_quit(%s)\n", e->e_id); 4112*0Sstevel@tonic-gate 4113*0Sstevel@tonic-gate for (i = 0; InputFilters[i] != NULL; i++) 4114*0Sstevel@tonic-gate milter_quit_filter(InputFilters[i], e); 4115*0Sstevel@tonic-gate } 4116*0Sstevel@tonic-gate /* 4117*0Sstevel@tonic-gate ** MILTER_ABORT -- informs the filter(s) that we are aborting current message 4118*0Sstevel@tonic-gate ** 4119*0Sstevel@tonic-gate ** Parameters: 4120*0Sstevel@tonic-gate ** e -- current envelope. 4121*0Sstevel@tonic-gate ** 4122*0Sstevel@tonic-gate ** Returns: 4123*0Sstevel@tonic-gate ** none 4124*0Sstevel@tonic-gate */ 4125*0Sstevel@tonic-gate 4126*0Sstevel@tonic-gate void 4127*0Sstevel@tonic-gate milter_abort(e) 4128*0Sstevel@tonic-gate ENVELOPE *e; 4129*0Sstevel@tonic-gate { 4130*0Sstevel@tonic-gate int i; 4131*0Sstevel@tonic-gate 4132*0Sstevel@tonic-gate if (tTd(64, 10)) 4133*0Sstevel@tonic-gate sm_dprintf("milter_abort\n"); 4134*0Sstevel@tonic-gate 4135*0Sstevel@tonic-gate for (i = 0; InputFilters[i] != NULL; i++) 4136*0Sstevel@tonic-gate { 4137*0Sstevel@tonic-gate struct milter *m = InputFilters[i]; 4138*0Sstevel@tonic-gate 4139*0Sstevel@tonic-gate /* sanity checks */ 4140*0Sstevel@tonic-gate if (m->mf_sock < 0 || m->mf_state != SMFS_INMSG) 4141*0Sstevel@tonic-gate continue; 4142*0Sstevel@tonic-gate 4143*0Sstevel@tonic-gate milter_abort_filter(m, e); 4144*0Sstevel@tonic-gate } 4145*0Sstevel@tonic-gate } 4146*0Sstevel@tonic-gate #endif /* MILTER */ 4147