164562Sgshapiro/*
2261194Sgshapiro *  Copyright (c) 1999-2004, 2009 Proofpoint, Inc. and its suppliers.
364562Sgshapiro *	All rights reserved.
464562Sgshapiro *
564562Sgshapiro * By using this file, you agree to the terms and conditions set
664562Sgshapiro * forth in the LICENSE file which can be found at the top level of
764562Sgshapiro * the sendmail distribution.
864562Sgshapiro *
964562Sgshapiro */
1064562Sgshapiro
1190792Sgshapiro#include <sm/gen.h>
12266527SgshapiroSM_RCSID("@(#)$Id: comm.c,v 8.71 2013-11-22 20:51:36 ca Exp $")
1364562Sgshapiro
1464562Sgshapiro#include "libmilter.h"
1590792Sgshapiro#include <sm/errstring.h>
16132943Sgshapiro#include <sys/uio.h>
1764562Sgshapiro
18141858Sgshapirostatic ssize_t	retry_writev __P((socket_t, struct iovec *, int, struct timeval *));
19132943Sgshapirostatic size_t Maxdatasize = MILTER_MAX_DATA_SIZE;
20132943Sgshapiro
2164562Sgshapiro/*
22132943Sgshapiro**  SMFI_SETMAXDATASIZE -- set limit for milter data read/write.
23132943Sgshapiro**
24132943Sgshapiro**	Parameters:
25132943Sgshapiro**		sz -- new limit.
26132943Sgshapiro**
27132943Sgshapiro**	Returns:
28132943Sgshapiro**		old limit
29132943Sgshapiro*/
30132943Sgshapiro
31132943Sgshapirosize_t
32132943Sgshapirosmfi_setmaxdatasize(sz)
33132943Sgshapiro	size_t sz;
34132943Sgshapiro{
35132943Sgshapiro	size_t old;
36132943Sgshapiro
37132943Sgshapiro	old = Maxdatasize;
38132943Sgshapiro	Maxdatasize = sz;
39132943Sgshapiro	return old;
40132943Sgshapiro}
41132943Sgshapiro
42132943Sgshapiro/*
4364562Sgshapiro**  MI_RD_CMD -- read a command
4464562Sgshapiro**
4564562Sgshapiro**	Parameters:
4664562Sgshapiro**		sd -- socket descriptor
4764562Sgshapiro**		timeout -- maximum time to wait
4864562Sgshapiro**		cmd -- single character command read from sd
4964562Sgshapiro**		rlen -- pointer to length of result
5064562Sgshapiro**		name -- name of milter
5164562Sgshapiro**
5264562Sgshapiro**	Returns:
5364562Sgshapiro**		buffer with rest of command
5464562Sgshapiro**		(malloc()ed here, should be free()d)
5564562Sgshapiro**		hack: encode error in cmd
5664562Sgshapiro*/
5764562Sgshapiro
5864562Sgshapirochar *
5964562Sgshapiromi_rd_cmd(sd, timeout, cmd, rlen, name)
6064562Sgshapiro	socket_t sd;
6164562Sgshapiro	struct timeval *timeout;
6264562Sgshapiro	char *cmd;
6364562Sgshapiro	size_t *rlen;
6464562Sgshapiro	char *name;
6564562Sgshapiro{
6664562Sgshapiro	ssize_t len;
6764562Sgshapiro	mi_int32 expl;
6864562Sgshapiro	ssize_t i;
69111823Sgshapiro	FD_RD_VAR(rds, excs);
7064562Sgshapiro	int ret;
7164562Sgshapiro	int save_errno;
7264562Sgshapiro	char *buf;
7364562Sgshapiro	char data[MILTER_LEN_BYTES + 1];
7464562Sgshapiro
7564562Sgshapiro	*cmd = '\0';
7664562Sgshapiro	*rlen = 0;
7771345Sgshapiro
7864562Sgshapiro	i = 0;
79102528Sgshapiro	for (;;)
8064562Sgshapiro	{
81111823Sgshapiro		FD_RD_INIT(sd, rds, excs);
82111823Sgshapiro		ret = FD_RD_READY(sd, rds, excs, timeout);
83102528Sgshapiro		if (ret == 0)
84102528Sgshapiro			break;
85102528Sgshapiro		else if (ret < 0)
86102528Sgshapiro		{
87102528Sgshapiro			if (errno == EINTR)
88102528Sgshapiro				continue;
89102528Sgshapiro			break;
90102528Sgshapiro		}
91111823Sgshapiro		if (FD_IS_RD_EXC(sd, rds, excs))
9264562Sgshapiro		{
9364562Sgshapiro			*cmd = SMFIC_SELECT;
9464562Sgshapiro			return NULL;
9564562Sgshapiro		}
9690792Sgshapiro
9790792Sgshapiro		len = MI_SOCK_READ(sd, data + i, sizeof data - i);
9890792Sgshapiro		if (MI_SOCK_READ_FAIL(len))
9964562Sgshapiro		{
10064562Sgshapiro			smi_log(SMI_LOG_ERR,
10164562Sgshapiro				"%s, mi_rd_cmd: read returned %d: %s",
102110560Sgshapiro				name, (int) len, sm_errstring(errno));
10364562Sgshapiro			*cmd = SMFIC_RECVERR;
10464562Sgshapiro			return NULL;
10564562Sgshapiro		}
10664562Sgshapiro		if (len == 0)
10764562Sgshapiro		{
10864562Sgshapiro			*cmd = SMFIC_EOF;
10964562Sgshapiro			return NULL;
11064562Sgshapiro		}
11164562Sgshapiro		if (len >= (ssize_t) sizeof data - i)
11264562Sgshapiro			break;
11364562Sgshapiro		i += len;
11464562Sgshapiro	}
11564562Sgshapiro	if (ret == 0)
11664562Sgshapiro	{
11764562Sgshapiro		*cmd = SMFIC_TIMEOUT;
11864562Sgshapiro		return NULL;
11964562Sgshapiro	}
12064562Sgshapiro	else if (ret < 0)
12164562Sgshapiro	{
12264562Sgshapiro		smi_log(SMI_LOG_ERR,
123203004Sgshapiro			"%s: mi_rd_cmd: %s() returned %d: %s",
124203004Sgshapiro			name, MI_POLLSELECT, ret, sm_errstring(errno));
12564562Sgshapiro		*cmd = SMFIC_RECVERR;
12664562Sgshapiro		return NULL;
12764562Sgshapiro	}
12864562Sgshapiro
12964562Sgshapiro	*cmd = data[MILTER_LEN_BYTES];
13064562Sgshapiro	data[MILTER_LEN_BYTES] = '\0';
13164562Sgshapiro	(void) memcpy((void *) &expl, (void *) &(data[0]), MILTER_LEN_BYTES);
13264562Sgshapiro	expl = ntohl(expl) - 1;
13364562Sgshapiro	if (expl <= 0)
13464562Sgshapiro		return NULL;
135132943Sgshapiro	if (expl > Maxdatasize)
13664562Sgshapiro	{
13764562Sgshapiro		*cmd = SMFIC_TOOBIG;
13864562Sgshapiro		return NULL;
13964562Sgshapiro	}
14090792Sgshapiro#if _FFR_ADD_NULL
14190792Sgshapiro	buf = malloc(expl + 1);
142363466Sgshapiro#else
14364562Sgshapiro	buf = malloc(expl);
144363466Sgshapiro#endif
14564562Sgshapiro	if (buf == NULL)
14664562Sgshapiro	{
14764562Sgshapiro		*cmd = SMFIC_MALLOC;
14864562Sgshapiro		return NULL;
14964562Sgshapiro	}
15064562Sgshapiro
15164562Sgshapiro	i = 0;
152102528Sgshapiro	for (;;)
15364562Sgshapiro	{
154111823Sgshapiro		FD_RD_INIT(sd, rds, excs);
155111823Sgshapiro		ret = FD_RD_READY(sd, rds, excs, timeout);
156102528Sgshapiro		if (ret == 0)
157102528Sgshapiro			break;
158102528Sgshapiro		else if (ret < 0)
159102528Sgshapiro		{
160102528Sgshapiro			if (errno == EINTR)
161102528Sgshapiro				continue;
162102528Sgshapiro			break;
163102528Sgshapiro		}
164111823Sgshapiro		if (FD_IS_RD_EXC(sd, rds, excs))
16564562Sgshapiro		{
16664562Sgshapiro			*cmd = SMFIC_SELECT;
16764562Sgshapiro			free(buf);
16864562Sgshapiro			return NULL;
16964562Sgshapiro		}
17090792Sgshapiro		len = MI_SOCK_READ(sd, buf + i, expl - i);
17190792Sgshapiro		if (MI_SOCK_READ_FAIL(len))
17264562Sgshapiro		{
17364562Sgshapiro			smi_log(SMI_LOG_ERR,
17464562Sgshapiro				"%s: mi_rd_cmd: read returned %d: %s",
175110560Sgshapiro				name, (int) len, sm_errstring(errno));
17664562Sgshapiro			ret = -1;
17764562Sgshapiro			break;
17864562Sgshapiro		}
17964562Sgshapiro		if (len == 0)
18064562Sgshapiro		{
18164562Sgshapiro			*cmd = SMFIC_EOF;
18264562Sgshapiro			free(buf);
18364562Sgshapiro			return NULL;
18464562Sgshapiro		}
18564562Sgshapiro		if (len > expl - i)
18664562Sgshapiro		{
18764562Sgshapiro			*cmd = SMFIC_RECVERR;
18864562Sgshapiro			free(buf);
18964562Sgshapiro			return NULL;
19064562Sgshapiro		}
19164562Sgshapiro		if (len >= expl - i)
19264562Sgshapiro		{
19364562Sgshapiro			*rlen = expl;
19490792Sgshapiro#if _FFR_ADD_NULL
19590792Sgshapiro			/* makes life simpler for common string routines */
19690792Sgshapiro			buf[expl] = '\0';
197363466Sgshapiro#endif
19864562Sgshapiro			return buf;
19964562Sgshapiro		}
20064562Sgshapiro		i += len;
20164562Sgshapiro	}
20264562Sgshapiro
20364562Sgshapiro	save_errno = errno;
20464562Sgshapiro	free(buf);
20564562Sgshapiro
20664562Sgshapiro	/* select returned 0 (timeout) or < 0 (error) */
20764562Sgshapiro	if (ret == 0)
20864562Sgshapiro	{
20964562Sgshapiro		*cmd = SMFIC_TIMEOUT;
21064562Sgshapiro		return NULL;
21164562Sgshapiro	}
21264562Sgshapiro	if (ret < 0)
21364562Sgshapiro	{
21464562Sgshapiro		smi_log(SMI_LOG_ERR,
215203004Sgshapiro			"%s: mi_rd_cmd: %s() returned %d: %s",
216203004Sgshapiro			name, MI_POLLSELECT, ret, sm_errstring(save_errno));
21764562Sgshapiro		*cmd = SMFIC_RECVERR;
21864562Sgshapiro		return NULL;
21964562Sgshapiro	}
22064562Sgshapiro	*cmd = SMFIC_UNKNERR;
22164562Sgshapiro	return NULL;
22264562Sgshapiro}
223132943Sgshapiro
22490792Sgshapiro/*
225132943Sgshapiro**  RETRY_WRITEV -- Keep calling the writev() system call
226132943Sgshapiro**	until all the data is written out or an error occurs.
227132943Sgshapiro**
228132943Sgshapiro**	Parameters:
229132943Sgshapiro**		fd -- socket descriptor
230132943Sgshapiro**		iov -- io vector
231132943Sgshapiro**		iovcnt -- number of elements in io vector
232132943Sgshapiro**			must NOT exceed UIO_MAXIOV.
233132943Sgshapiro**		timeout -- maximum time to wait
234132943Sgshapiro**
235132943Sgshapiro**	Returns:
236132943Sgshapiro**		success: number of bytes written
237132943Sgshapiro**		otherwise: MI_FAILURE
238132943Sgshapiro*/
239132943Sgshapiro
240132943Sgshapirostatic ssize_t
241132943Sgshapiroretry_writev(fd, iov, iovcnt, timeout)
242132943Sgshapiro	socket_t fd;
243132943Sgshapiro	struct iovec *iov;
244132943Sgshapiro	int iovcnt;
245132943Sgshapiro	struct timeval *timeout;
246132943Sgshapiro{
247132943Sgshapiro	int i;
248132943Sgshapiro	ssize_t n, written;
249132943Sgshapiro	FD_WR_VAR(wrs);
250132943Sgshapiro
251132943Sgshapiro	written = 0;
252132943Sgshapiro	for (;;)
253132943Sgshapiro	{
254132943Sgshapiro		while (iovcnt > 0 && iov[0].iov_len == 0)
255132943Sgshapiro		{
256132943Sgshapiro			iov++;
257132943Sgshapiro			iovcnt--;
258132943Sgshapiro		}
259132943Sgshapiro		if (iovcnt <= 0)
260132943Sgshapiro			return written;
261132943Sgshapiro
262132943Sgshapiro		/*
263132943Sgshapiro		**  We don't care much about the timeout here,
264132943Sgshapiro		**  it's very long anyway; correct solution would be
265132943Sgshapiro		**  to take the time before the loop and reduce the
266132943Sgshapiro		**  timeout after each invocation.
267132943Sgshapiro		**  FD_SETSIZE is checked when socket is created.
268132943Sgshapiro		*/
269132943Sgshapiro
270132943Sgshapiro		FD_WR_INIT(fd, wrs);
271132943Sgshapiro		i = FD_WR_READY(fd, wrs, timeout);
272132943Sgshapiro		if (i == 0)
273132943Sgshapiro			return MI_FAILURE;
274132943Sgshapiro		if (i < 0)
275132943Sgshapiro		{
276132943Sgshapiro			if (errno == EINTR || errno == EAGAIN)
277132943Sgshapiro				continue;
278132943Sgshapiro			return MI_FAILURE;
279132943Sgshapiro		}
280132943Sgshapiro		n = writev(fd, iov, iovcnt);
281132943Sgshapiro		if (n == -1)
282132943Sgshapiro		{
283132943Sgshapiro			if (errno == EINTR || errno == EAGAIN)
284132943Sgshapiro				continue;
285132943Sgshapiro			return MI_FAILURE;
286132943Sgshapiro		}
287132943Sgshapiro
288132943Sgshapiro		written += n;
289132943Sgshapiro		for (i = 0; i < iovcnt; i++)
290132943Sgshapiro		{
291132943Sgshapiro			if (iov[i].iov_len > (unsigned int) n)
292132943Sgshapiro			{
293132943Sgshapiro				iov[i].iov_base = (char *)iov[i].iov_base + n;
294132943Sgshapiro				iov[i].iov_len -= (unsigned int) n;
295132943Sgshapiro				break;
296132943Sgshapiro			}
297132943Sgshapiro			n -= (int) iov[i].iov_len;
298132943Sgshapiro			iov[i].iov_len = 0;
299132943Sgshapiro		}
300132943Sgshapiro		if (i == iovcnt)
301132943Sgshapiro			return written;
302132943Sgshapiro	}
303132943Sgshapiro}
304132943Sgshapiro
305132943Sgshapiro/*
30664562Sgshapiro**  MI_WR_CMD -- write a cmd to sd
30764562Sgshapiro**
30864562Sgshapiro**	Parameters:
30964562Sgshapiro**		sd -- socket descriptor
310132943Sgshapiro**		timeout -- maximum time to wait
31164562Sgshapiro**		cmd -- single character command to write
31264562Sgshapiro**		buf -- buffer with further data
31364562Sgshapiro**		len -- length of buffer (without cmd!)
31464562Sgshapiro**
31564562Sgshapiro**	Returns:
31664562Sgshapiro**		MI_SUCCESS/MI_FAILURE
31764562Sgshapiro*/
31864562Sgshapiro
31964562Sgshapiroint
32064562Sgshapiromi_wr_cmd(sd, timeout, cmd, buf, len)
32164562Sgshapiro	socket_t sd;
32264562Sgshapiro	struct timeval *timeout;
32364562Sgshapiro	int cmd;
32464562Sgshapiro	char *buf;
32564562Sgshapiro	size_t len;
32664562Sgshapiro{
327203004Sgshapiro	size_t sl;
32864562Sgshapiro	ssize_t l;
32964562Sgshapiro	mi_int32 nl;
330132943Sgshapiro	int iovcnt;
331132943Sgshapiro	struct iovec iov[2];
33264562Sgshapiro	char data[MILTER_LEN_BYTES + 1];
33364562Sgshapiro
334132943Sgshapiro	if (len > Maxdatasize || (len > 0 && buf == NULL))
33564562Sgshapiro		return MI_FAILURE;
336132943Sgshapiro
33764562Sgshapiro	nl = htonl(len + 1);	/* add 1 for the cmd char */
33864562Sgshapiro	(void) memcpy(data, (void *) &nl, MILTER_LEN_BYTES);
33964562Sgshapiro	data[MILTER_LEN_BYTES] = (char) cmd;
34064562Sgshapiro	sl = MILTER_LEN_BYTES + 1;
34164562Sgshapiro
342132943Sgshapiro	/* set up the vector for the size / command */
343132943Sgshapiro	iov[0].iov_base = (void *) data;
344132943Sgshapiro	iov[0].iov_len  = sl;
345132943Sgshapiro	iovcnt = 1;
346132943Sgshapiro	if (len >= 0 && buf != NULL)
347132943Sgshapiro	{
348132943Sgshapiro		iov[1].iov_base = (void *) buf;
349132943Sgshapiro		iov[1].iov_len  = len;
350132943Sgshapiro		iovcnt = 2;
351132943Sgshapiro	}
352168515Sgshapiro
353132943Sgshapiro	l = retry_writev(sd, iov, iovcnt, timeout);
354132943Sgshapiro	if (l == MI_FAILURE)
35564562Sgshapiro		return MI_FAILURE;
35664562Sgshapiro	return MI_SUCCESS;
35764562Sgshapiro}
358