10Sstevel@tonic-gate /*
2*11440SJohn.Beck@Sun.COM * Copyright (c) 1999-2004, 2009 Sendmail, Inc. and its suppliers.
30Sstevel@tonic-gate * All rights reserved.
40Sstevel@tonic-gate *
50Sstevel@tonic-gate * By using this file, you agree to the terms and conditions set
60Sstevel@tonic-gate * forth in the LICENSE file which can be found at the top level of
70Sstevel@tonic-gate * the sendmail distribution.
80Sstevel@tonic-gate *
90Sstevel@tonic-gate */
100Sstevel@tonic-gate
110Sstevel@tonic-gate #include <sm/gen.h>
12*11440SJohn.Beck@Sun.COM SM_RCSID("@(#)$Id: comm.c,v 8.70 2009/12/16 16:33:48 ca Exp $")
130Sstevel@tonic-gate
140Sstevel@tonic-gate #include "libmilter.h"
150Sstevel@tonic-gate #include <sm/errstring.h>
160Sstevel@tonic-gate #include <sys/uio.h>
170Sstevel@tonic-gate
180Sstevel@tonic-gate static ssize_t retry_writev __P((socket_t, struct iovec *, int, struct timeval *));
190Sstevel@tonic-gate static size_t Maxdatasize = MILTER_MAX_DATA_SIZE;
200Sstevel@tonic-gate
210Sstevel@tonic-gate /*
220Sstevel@tonic-gate ** SMFI_SETMAXDATASIZE -- set limit for milter data read/write.
230Sstevel@tonic-gate **
240Sstevel@tonic-gate ** Parameters:
250Sstevel@tonic-gate ** sz -- new limit.
260Sstevel@tonic-gate **
270Sstevel@tonic-gate ** Returns:
280Sstevel@tonic-gate ** old limit
290Sstevel@tonic-gate */
300Sstevel@tonic-gate
310Sstevel@tonic-gate size_t
smfi_setmaxdatasize(sz)320Sstevel@tonic-gate smfi_setmaxdatasize(sz)
330Sstevel@tonic-gate size_t sz;
340Sstevel@tonic-gate {
350Sstevel@tonic-gate size_t old;
360Sstevel@tonic-gate
370Sstevel@tonic-gate old = Maxdatasize;
380Sstevel@tonic-gate Maxdatasize = sz;
390Sstevel@tonic-gate return old;
400Sstevel@tonic-gate }
410Sstevel@tonic-gate
420Sstevel@tonic-gate /*
430Sstevel@tonic-gate ** MI_RD_CMD -- read a command
440Sstevel@tonic-gate **
450Sstevel@tonic-gate ** Parameters:
460Sstevel@tonic-gate ** sd -- socket descriptor
470Sstevel@tonic-gate ** timeout -- maximum time to wait
480Sstevel@tonic-gate ** cmd -- single character command read from sd
490Sstevel@tonic-gate ** rlen -- pointer to length of result
500Sstevel@tonic-gate ** name -- name of milter
510Sstevel@tonic-gate **
520Sstevel@tonic-gate ** Returns:
530Sstevel@tonic-gate ** buffer with rest of command
540Sstevel@tonic-gate ** (malloc()ed here, should be free()d)
550Sstevel@tonic-gate ** hack: encode error in cmd
560Sstevel@tonic-gate */
570Sstevel@tonic-gate
580Sstevel@tonic-gate char *
mi_rd_cmd(sd,timeout,cmd,rlen,name)590Sstevel@tonic-gate mi_rd_cmd(sd, timeout, cmd, rlen, name)
600Sstevel@tonic-gate socket_t sd;
610Sstevel@tonic-gate struct timeval *timeout;
620Sstevel@tonic-gate char *cmd;
630Sstevel@tonic-gate size_t *rlen;
640Sstevel@tonic-gate char *name;
650Sstevel@tonic-gate {
660Sstevel@tonic-gate ssize_t len;
670Sstevel@tonic-gate mi_int32 expl;
680Sstevel@tonic-gate ssize_t i;
690Sstevel@tonic-gate FD_RD_VAR(rds, excs);
700Sstevel@tonic-gate int ret;
710Sstevel@tonic-gate int save_errno;
720Sstevel@tonic-gate char *buf;
730Sstevel@tonic-gate char data[MILTER_LEN_BYTES + 1];
740Sstevel@tonic-gate
750Sstevel@tonic-gate *cmd = '\0';
760Sstevel@tonic-gate *rlen = 0;
770Sstevel@tonic-gate
780Sstevel@tonic-gate i = 0;
790Sstevel@tonic-gate for (;;)
800Sstevel@tonic-gate {
810Sstevel@tonic-gate FD_RD_INIT(sd, rds, excs);
820Sstevel@tonic-gate ret = FD_RD_READY(sd, rds, excs, timeout);
830Sstevel@tonic-gate if (ret == 0)
840Sstevel@tonic-gate break;
850Sstevel@tonic-gate else if (ret < 0)
860Sstevel@tonic-gate {
870Sstevel@tonic-gate if (errno == EINTR)
880Sstevel@tonic-gate continue;
890Sstevel@tonic-gate break;
900Sstevel@tonic-gate }
910Sstevel@tonic-gate if (FD_IS_RD_EXC(sd, rds, excs))
920Sstevel@tonic-gate {
930Sstevel@tonic-gate *cmd = SMFIC_SELECT;
940Sstevel@tonic-gate return NULL;
950Sstevel@tonic-gate }
960Sstevel@tonic-gate
970Sstevel@tonic-gate len = MI_SOCK_READ(sd, data + i, sizeof data - i);
980Sstevel@tonic-gate if (MI_SOCK_READ_FAIL(len))
990Sstevel@tonic-gate {
1000Sstevel@tonic-gate smi_log(SMI_LOG_ERR,
1010Sstevel@tonic-gate "%s, mi_rd_cmd: read returned %d: %s",
1020Sstevel@tonic-gate name, (int) len, sm_errstring(errno));
1030Sstevel@tonic-gate *cmd = SMFIC_RECVERR;
1040Sstevel@tonic-gate return NULL;
1050Sstevel@tonic-gate }
1060Sstevel@tonic-gate if (len == 0)
1070Sstevel@tonic-gate {
1080Sstevel@tonic-gate *cmd = SMFIC_EOF;
1090Sstevel@tonic-gate return NULL;
1100Sstevel@tonic-gate }
1110Sstevel@tonic-gate if (len >= (ssize_t) sizeof data - i)
1120Sstevel@tonic-gate break;
1130Sstevel@tonic-gate i += len;
1140Sstevel@tonic-gate }
1150Sstevel@tonic-gate if (ret == 0)
1160Sstevel@tonic-gate {
1170Sstevel@tonic-gate *cmd = SMFIC_TIMEOUT;
1180Sstevel@tonic-gate return NULL;
1190Sstevel@tonic-gate }
1200Sstevel@tonic-gate else if (ret < 0)
1210Sstevel@tonic-gate {
1220Sstevel@tonic-gate smi_log(SMI_LOG_ERR,
123*11440SJohn.Beck@Sun.COM "%s: mi_rd_cmd: %s() returned %d: %s",
124*11440SJohn.Beck@Sun.COM name, MI_POLLSELECT, ret, sm_errstring(errno));
1250Sstevel@tonic-gate *cmd = SMFIC_RECVERR;
1260Sstevel@tonic-gate return NULL;
1270Sstevel@tonic-gate }
1280Sstevel@tonic-gate
1290Sstevel@tonic-gate *cmd = data[MILTER_LEN_BYTES];
1300Sstevel@tonic-gate data[MILTER_LEN_BYTES] = '\0';
1310Sstevel@tonic-gate (void) memcpy((void *) &expl, (void *) &(data[0]), MILTER_LEN_BYTES);
1320Sstevel@tonic-gate expl = ntohl(expl) - 1;
1330Sstevel@tonic-gate if (expl <= 0)
1340Sstevel@tonic-gate return NULL;
1350Sstevel@tonic-gate if (expl > Maxdatasize)
1360Sstevel@tonic-gate {
1370Sstevel@tonic-gate *cmd = SMFIC_TOOBIG;
1380Sstevel@tonic-gate return NULL;
1390Sstevel@tonic-gate }
1400Sstevel@tonic-gate #if _FFR_ADD_NULL
1410Sstevel@tonic-gate buf = malloc(expl + 1);
1420Sstevel@tonic-gate #else /* _FFR_ADD_NULL */
1430Sstevel@tonic-gate buf = malloc(expl);
1440Sstevel@tonic-gate #endif /* _FFR_ADD_NULL */
1450Sstevel@tonic-gate if (buf == NULL)
1460Sstevel@tonic-gate {
1470Sstevel@tonic-gate *cmd = SMFIC_MALLOC;
1480Sstevel@tonic-gate return NULL;
1490Sstevel@tonic-gate }
1500Sstevel@tonic-gate
1510Sstevel@tonic-gate i = 0;
1520Sstevel@tonic-gate for (;;)
1530Sstevel@tonic-gate {
1540Sstevel@tonic-gate FD_RD_INIT(sd, rds, excs);
1550Sstevel@tonic-gate ret = FD_RD_READY(sd, rds, excs, timeout);
1560Sstevel@tonic-gate if (ret == 0)
1570Sstevel@tonic-gate break;
1580Sstevel@tonic-gate else if (ret < 0)
1590Sstevel@tonic-gate {
1600Sstevel@tonic-gate if (errno == EINTR)
1610Sstevel@tonic-gate continue;
1620Sstevel@tonic-gate break;
1630Sstevel@tonic-gate }
1640Sstevel@tonic-gate if (FD_IS_RD_EXC(sd, rds, excs))
1650Sstevel@tonic-gate {
1660Sstevel@tonic-gate *cmd = SMFIC_SELECT;
1670Sstevel@tonic-gate free(buf);
1680Sstevel@tonic-gate return NULL;
1690Sstevel@tonic-gate }
1700Sstevel@tonic-gate len = MI_SOCK_READ(sd, buf + i, expl - i);
1710Sstevel@tonic-gate if (MI_SOCK_READ_FAIL(len))
1720Sstevel@tonic-gate {
1730Sstevel@tonic-gate smi_log(SMI_LOG_ERR,
1740Sstevel@tonic-gate "%s: mi_rd_cmd: read returned %d: %s",
1750Sstevel@tonic-gate name, (int) len, sm_errstring(errno));
1760Sstevel@tonic-gate ret = -1;
1770Sstevel@tonic-gate break;
1780Sstevel@tonic-gate }
1790Sstevel@tonic-gate if (len == 0)
1800Sstevel@tonic-gate {
1810Sstevel@tonic-gate *cmd = SMFIC_EOF;
1820Sstevel@tonic-gate free(buf);
1830Sstevel@tonic-gate return NULL;
1840Sstevel@tonic-gate }
1850Sstevel@tonic-gate if (len > expl - i)
1860Sstevel@tonic-gate {
1870Sstevel@tonic-gate *cmd = SMFIC_RECVERR;
1880Sstevel@tonic-gate free(buf);
1890Sstevel@tonic-gate return NULL;
1900Sstevel@tonic-gate }
1910Sstevel@tonic-gate if (len >= expl - i)
1920Sstevel@tonic-gate {
1930Sstevel@tonic-gate *rlen = expl;
1940Sstevel@tonic-gate #if _FFR_ADD_NULL
1950Sstevel@tonic-gate /* makes life simpler for common string routines */
1960Sstevel@tonic-gate buf[expl] = '\0';
1970Sstevel@tonic-gate #endif /* _FFR_ADD_NULL */
1980Sstevel@tonic-gate return buf;
1990Sstevel@tonic-gate }
2000Sstevel@tonic-gate i += len;
2010Sstevel@tonic-gate }
2020Sstevel@tonic-gate
2030Sstevel@tonic-gate save_errno = errno;
2040Sstevel@tonic-gate free(buf);
2050Sstevel@tonic-gate
2060Sstevel@tonic-gate /* select returned 0 (timeout) or < 0 (error) */
2070Sstevel@tonic-gate if (ret == 0)
2080Sstevel@tonic-gate {
2090Sstevel@tonic-gate *cmd = SMFIC_TIMEOUT;
2100Sstevel@tonic-gate return NULL;
2110Sstevel@tonic-gate }
2120Sstevel@tonic-gate if (ret < 0)
2130Sstevel@tonic-gate {
2140Sstevel@tonic-gate smi_log(SMI_LOG_ERR,
215*11440SJohn.Beck@Sun.COM "%s: mi_rd_cmd: %s() returned %d: %s",
216*11440SJohn.Beck@Sun.COM name, MI_POLLSELECT, ret, sm_errstring(save_errno));
2170Sstevel@tonic-gate *cmd = SMFIC_RECVERR;
2180Sstevel@tonic-gate return NULL;
2190Sstevel@tonic-gate }
2200Sstevel@tonic-gate *cmd = SMFIC_UNKNERR;
2210Sstevel@tonic-gate return NULL;
2220Sstevel@tonic-gate }
2230Sstevel@tonic-gate
2240Sstevel@tonic-gate /*
2250Sstevel@tonic-gate ** RETRY_WRITEV -- Keep calling the writev() system call
2260Sstevel@tonic-gate ** until all the data is written out or an error occurs.
2270Sstevel@tonic-gate **
2280Sstevel@tonic-gate ** Parameters:
2290Sstevel@tonic-gate ** fd -- socket descriptor
2300Sstevel@tonic-gate ** iov -- io vector
2310Sstevel@tonic-gate ** iovcnt -- number of elements in io vector
2320Sstevel@tonic-gate ** must NOT exceed UIO_MAXIOV.
2330Sstevel@tonic-gate ** timeout -- maximum time to wait
2340Sstevel@tonic-gate **
2350Sstevel@tonic-gate ** Returns:
2360Sstevel@tonic-gate ** success: number of bytes written
2370Sstevel@tonic-gate ** otherwise: MI_FAILURE
2380Sstevel@tonic-gate */
2390Sstevel@tonic-gate
2400Sstevel@tonic-gate static ssize_t
retry_writev(fd,iov,iovcnt,timeout)2410Sstevel@tonic-gate retry_writev(fd, iov, iovcnt, timeout)
2420Sstevel@tonic-gate socket_t fd;
2430Sstevel@tonic-gate struct iovec *iov;
2440Sstevel@tonic-gate int iovcnt;
2450Sstevel@tonic-gate struct timeval *timeout;
2460Sstevel@tonic-gate {
2470Sstevel@tonic-gate int i;
2480Sstevel@tonic-gate ssize_t n, written;
2490Sstevel@tonic-gate FD_WR_VAR(wrs);
2500Sstevel@tonic-gate
2510Sstevel@tonic-gate written = 0;
2520Sstevel@tonic-gate for (;;)
2530Sstevel@tonic-gate {
2540Sstevel@tonic-gate while (iovcnt > 0 && iov[0].iov_len == 0)
2550Sstevel@tonic-gate {
2560Sstevel@tonic-gate iov++;
2570Sstevel@tonic-gate iovcnt--;
2580Sstevel@tonic-gate }
2590Sstevel@tonic-gate if (iovcnt <= 0)
2600Sstevel@tonic-gate return written;
2610Sstevel@tonic-gate
2620Sstevel@tonic-gate /*
2630Sstevel@tonic-gate ** We don't care much about the timeout here,
2640Sstevel@tonic-gate ** it's very long anyway; correct solution would be
2650Sstevel@tonic-gate ** to take the time before the loop and reduce the
2660Sstevel@tonic-gate ** timeout after each invocation.
2670Sstevel@tonic-gate ** FD_SETSIZE is checked when socket is created.
2680Sstevel@tonic-gate */
2690Sstevel@tonic-gate
2700Sstevel@tonic-gate FD_WR_INIT(fd, wrs);
2710Sstevel@tonic-gate i = FD_WR_READY(fd, wrs, timeout);
2720Sstevel@tonic-gate if (i == 0)
2730Sstevel@tonic-gate return MI_FAILURE;
2740Sstevel@tonic-gate if (i < 0)
2750Sstevel@tonic-gate {
2760Sstevel@tonic-gate if (errno == EINTR || errno == EAGAIN)
2770Sstevel@tonic-gate continue;
2780Sstevel@tonic-gate return MI_FAILURE;
2790Sstevel@tonic-gate }
2800Sstevel@tonic-gate n = writev(fd, iov, iovcnt);
2810Sstevel@tonic-gate if (n == -1)
2820Sstevel@tonic-gate {
2830Sstevel@tonic-gate if (errno == EINTR || errno == EAGAIN)
2840Sstevel@tonic-gate continue;
2850Sstevel@tonic-gate return MI_FAILURE;
2860Sstevel@tonic-gate }
2870Sstevel@tonic-gate
2880Sstevel@tonic-gate written += n;
2890Sstevel@tonic-gate for (i = 0; i < iovcnt; i++)
2900Sstevel@tonic-gate {
2910Sstevel@tonic-gate if (iov[i].iov_len > (unsigned int) n)
2920Sstevel@tonic-gate {
2930Sstevel@tonic-gate iov[i].iov_base = (char *)iov[i].iov_base + n;
2940Sstevel@tonic-gate iov[i].iov_len -= (unsigned int) n;
2950Sstevel@tonic-gate break;
2960Sstevel@tonic-gate }
2970Sstevel@tonic-gate n -= (int) iov[i].iov_len;
2980Sstevel@tonic-gate iov[i].iov_len = 0;
2990Sstevel@tonic-gate }
3000Sstevel@tonic-gate if (i == iovcnt)
3010Sstevel@tonic-gate return written;
3020Sstevel@tonic-gate }
3030Sstevel@tonic-gate }
3040Sstevel@tonic-gate
3050Sstevel@tonic-gate /*
3060Sstevel@tonic-gate ** MI_WR_CMD -- write a cmd to sd
3070Sstevel@tonic-gate **
3080Sstevel@tonic-gate ** Parameters:
3090Sstevel@tonic-gate ** sd -- socket descriptor
3100Sstevel@tonic-gate ** timeout -- maximum time to wait
3110Sstevel@tonic-gate ** cmd -- single character command to write
3120Sstevel@tonic-gate ** buf -- buffer with further data
3130Sstevel@tonic-gate ** len -- length of buffer (without cmd!)
3140Sstevel@tonic-gate **
3150Sstevel@tonic-gate ** Returns:
3160Sstevel@tonic-gate ** MI_SUCCESS/MI_FAILURE
3170Sstevel@tonic-gate */
3180Sstevel@tonic-gate
3190Sstevel@tonic-gate int
mi_wr_cmd(sd,timeout,cmd,buf,len)3200Sstevel@tonic-gate mi_wr_cmd(sd, timeout, cmd, buf, len)
3210Sstevel@tonic-gate socket_t sd;
3220Sstevel@tonic-gate struct timeval *timeout;
3230Sstevel@tonic-gate int cmd;
3240Sstevel@tonic-gate char *buf;
3250Sstevel@tonic-gate size_t len;
3260Sstevel@tonic-gate {
327*11440SJohn.Beck@Sun.COM size_t sl;
3280Sstevel@tonic-gate ssize_t l;
3290Sstevel@tonic-gate mi_int32 nl;
3300Sstevel@tonic-gate int iovcnt;
3310Sstevel@tonic-gate struct iovec iov[2];
3320Sstevel@tonic-gate char data[MILTER_LEN_BYTES + 1];
3330Sstevel@tonic-gate
3340Sstevel@tonic-gate if (len > Maxdatasize || (len > 0 && buf == NULL))
3350Sstevel@tonic-gate return MI_FAILURE;
3360Sstevel@tonic-gate
3370Sstevel@tonic-gate nl = htonl(len + 1); /* add 1 for the cmd char */
3380Sstevel@tonic-gate (void) memcpy(data, (void *) &nl, MILTER_LEN_BYTES);
3390Sstevel@tonic-gate data[MILTER_LEN_BYTES] = (char) cmd;
3400Sstevel@tonic-gate sl = MILTER_LEN_BYTES + 1;
3410Sstevel@tonic-gate
3420Sstevel@tonic-gate /* set up the vector for the size / command */
3430Sstevel@tonic-gate iov[0].iov_base = (void *) data;
3440Sstevel@tonic-gate iov[0].iov_len = sl;
3450Sstevel@tonic-gate iovcnt = 1;
3460Sstevel@tonic-gate if (len >= 0 && buf != NULL)
3470Sstevel@tonic-gate {
3480Sstevel@tonic-gate iov[1].iov_base = (void *) buf;
3490Sstevel@tonic-gate iov[1].iov_len = len;
3500Sstevel@tonic-gate iovcnt = 2;
3510Sstevel@tonic-gate }
352*11440SJohn.Beck@Sun.COM
3530Sstevel@tonic-gate l = retry_writev(sd, iov, iovcnt, timeout);
3540Sstevel@tonic-gate if (l == MI_FAILURE)
3550Sstevel@tonic-gate return MI_FAILURE;
3560Sstevel@tonic-gate return MI_SUCCESS;
3570Sstevel@tonic-gate }
358