comm.c revision 168515
1/*
2 *  Copyright (c) 1999-2004 Sendmail, Inc. and its suppliers.
3 *	All rights reserved.
4 *
5 * By using this file, you agree to the terms and conditions set
6 * forth in the LICENSE file which can be found at the top level of
7 * the sendmail distribution.
8 *
9 */
10
11#include <sm/gen.h>
12SM_RCSID("@(#)$Id: comm.c,v 8.67 2006/11/02 17:54:44 ca Exp $")
13
14#include "libmilter.h"
15#include <sm/errstring.h>
16#include <sys/uio.h>
17
18static ssize_t	retry_writev __P((socket_t, struct iovec *, int, struct timeval *));
19static size_t Maxdatasize = MILTER_MAX_DATA_SIZE;
20
21#if _FFR_MAXDATASIZE
22/*
23**  SMFI_SETMAXDATASIZE -- set limit for milter data read/write.
24**
25**	Parameters:
26**		sz -- new limit.
27**
28**	Returns:
29**		old limit
30*/
31
32size_t
33smfi_setmaxdatasize(sz)
34	size_t sz;
35{
36	size_t old;
37
38	old = Maxdatasize;
39	Maxdatasize = sz;
40	return old;
41}
42#endif /* _FFR_MAXDATASIZE */
43
44/*
45**  MI_RD_CMD -- read a command
46**
47**	Parameters:
48**		sd -- socket descriptor
49**		timeout -- maximum time to wait
50**		cmd -- single character command read from sd
51**		rlen -- pointer to length of result
52**		name -- name of milter
53**
54**	Returns:
55**		buffer with rest of command
56**		(malloc()ed here, should be free()d)
57**		hack: encode error in cmd
58*/
59
60char *
61mi_rd_cmd(sd, timeout, cmd, rlen, name)
62	socket_t sd;
63	struct timeval *timeout;
64	char *cmd;
65	size_t *rlen;
66	char *name;
67{
68	ssize_t len;
69	mi_int32 expl;
70	ssize_t i;
71	FD_RD_VAR(rds, excs);
72	int ret;
73	int save_errno;
74	char *buf;
75	char data[MILTER_LEN_BYTES + 1];
76
77	*cmd = '\0';
78	*rlen = 0;
79
80	i = 0;
81	for (;;)
82	{
83		FD_RD_INIT(sd, rds, excs);
84		ret = FD_RD_READY(sd, rds, excs, timeout);
85		if (ret == 0)
86			break;
87		else if (ret < 0)
88		{
89			if (errno == EINTR)
90				continue;
91			break;
92		}
93		if (FD_IS_RD_EXC(sd, rds, excs))
94		{
95			*cmd = SMFIC_SELECT;
96			return NULL;
97		}
98
99		len = MI_SOCK_READ(sd, data + i, sizeof data - i);
100		if (MI_SOCK_READ_FAIL(len))
101		{
102			smi_log(SMI_LOG_ERR,
103				"%s, mi_rd_cmd: read returned %d: %s",
104				name, (int) len, sm_errstring(errno));
105			*cmd = SMFIC_RECVERR;
106			return NULL;
107		}
108		if (len == 0)
109		{
110			*cmd = SMFIC_EOF;
111			return NULL;
112		}
113		if (len >= (ssize_t) sizeof data - i)
114			break;
115		i += len;
116	}
117	if (ret == 0)
118	{
119		*cmd = SMFIC_TIMEOUT;
120		return NULL;
121	}
122	else if (ret < 0)
123	{
124		smi_log(SMI_LOG_ERR,
125			"%s: mi_rd_cmd: select returned %d: %s",
126			name, ret, sm_errstring(errno));
127		*cmd = SMFIC_RECVERR;
128		return NULL;
129	}
130
131	*cmd = data[MILTER_LEN_BYTES];
132	data[MILTER_LEN_BYTES] = '\0';
133	(void) memcpy((void *) &expl, (void *) &(data[0]), MILTER_LEN_BYTES);
134	expl = ntohl(expl) - 1;
135	if (expl <= 0)
136		return NULL;
137	if (expl > Maxdatasize)
138	{
139		*cmd = SMFIC_TOOBIG;
140		return NULL;
141	}
142#if _FFR_ADD_NULL
143	buf = malloc(expl + 1);
144#else /* _FFR_ADD_NULL */
145	buf = malloc(expl);
146#endif /* _FFR_ADD_NULL */
147	if (buf == NULL)
148	{
149		*cmd = SMFIC_MALLOC;
150		return NULL;
151	}
152
153	i = 0;
154	for (;;)
155	{
156		FD_RD_INIT(sd, rds, excs);
157		ret = FD_RD_READY(sd, rds, excs, timeout);
158		if (ret == 0)
159			break;
160		else if (ret < 0)
161		{
162			if (errno == EINTR)
163				continue;
164			break;
165		}
166		if (FD_IS_RD_EXC(sd, rds, excs))
167		{
168			*cmd = SMFIC_SELECT;
169			free(buf);
170			return NULL;
171		}
172		len = MI_SOCK_READ(sd, buf + i, expl - i);
173		if (MI_SOCK_READ_FAIL(len))
174		{
175			smi_log(SMI_LOG_ERR,
176				"%s: mi_rd_cmd: read returned %d: %s",
177				name, (int) len, sm_errstring(errno));
178			ret = -1;
179			break;
180		}
181		if (len == 0)
182		{
183			*cmd = SMFIC_EOF;
184			free(buf);
185			return NULL;
186		}
187		if (len > expl - i)
188		{
189			*cmd = SMFIC_RECVERR;
190			free(buf);
191			return NULL;
192		}
193		if (len >= expl - i)
194		{
195			*rlen = expl;
196#if _FFR_ADD_NULL
197			/* makes life simpler for common string routines */
198			buf[expl] = '\0';
199#endif /* _FFR_ADD_NULL */
200			return buf;
201		}
202		i += len;
203	}
204
205	save_errno = errno;
206	free(buf);
207
208	/* select returned 0 (timeout) or < 0 (error) */
209	if (ret == 0)
210	{
211		*cmd = SMFIC_TIMEOUT;
212		return NULL;
213	}
214	if (ret < 0)
215	{
216		smi_log(SMI_LOG_ERR,
217			"%s: mi_rd_cmd: select returned %d: %s",
218			name, ret, sm_errstring(save_errno));
219		*cmd = SMFIC_RECVERR;
220		return NULL;
221	}
222	*cmd = SMFIC_UNKNERR;
223	return NULL;
224}
225
226/*
227**  RETRY_WRITEV -- Keep calling the writev() system call
228**	until all the data is written out or an error occurs.
229**
230**	Parameters:
231**		fd -- socket descriptor
232**		iov -- io vector
233**		iovcnt -- number of elements in io vector
234**			must NOT exceed UIO_MAXIOV.
235**		timeout -- maximum time to wait
236**
237**	Returns:
238**		success: number of bytes written
239**		otherwise: MI_FAILURE
240*/
241
242static ssize_t
243retry_writev(fd, iov, iovcnt, timeout)
244	socket_t fd;
245	struct iovec *iov;
246	int iovcnt;
247	struct timeval *timeout;
248{
249	int i;
250	ssize_t n, written;
251	FD_WR_VAR(wrs);
252
253	written = 0;
254	for (;;)
255	{
256		while (iovcnt > 0 && iov[0].iov_len == 0)
257		{
258			iov++;
259			iovcnt--;
260		}
261		if (iovcnt <= 0)
262			return written;
263
264		/*
265		**  We don't care much about the timeout here,
266		**  it's very long anyway; correct solution would be
267		**  to take the time before the loop and reduce the
268		**  timeout after each invocation.
269		**  FD_SETSIZE is checked when socket is created.
270		*/
271
272		FD_WR_INIT(fd, wrs);
273		i = FD_WR_READY(fd, wrs, timeout);
274		if (i == 0)
275			return MI_FAILURE;
276		if (i < 0)
277		{
278			if (errno == EINTR || errno == EAGAIN)
279				continue;
280			return MI_FAILURE;
281		}
282		n = writev(fd, iov, iovcnt);
283		if (n == -1)
284		{
285			if (errno == EINTR || errno == EAGAIN)
286				continue;
287			return MI_FAILURE;
288		}
289
290		written += n;
291		for (i = 0; i < iovcnt; i++)
292		{
293			if (iov[i].iov_len > (unsigned int) n)
294			{
295				iov[i].iov_base = (char *)iov[i].iov_base + n;
296				iov[i].iov_len -= (unsigned int) n;
297				break;
298			}
299			n -= (int) iov[i].iov_len;
300			iov[i].iov_len = 0;
301		}
302		if (i == iovcnt)
303			return written;
304	}
305}
306
307/*
308**  MI_WR_CMD -- write a cmd to sd
309**
310**	Parameters:
311**		sd -- socket descriptor
312**		timeout -- maximum time to wait
313**		cmd -- single character command to write
314**		buf -- buffer with further data
315**		len -- length of buffer (without cmd!)
316**
317**	Returns:
318**		MI_SUCCESS/MI_FAILURE
319*/
320
321int
322mi_wr_cmd(sd, timeout, cmd, buf, len)
323	socket_t sd;
324	struct timeval *timeout;
325	int cmd;
326	char *buf;
327	size_t len;
328{
329	size_t sl, i;
330	ssize_t l;
331	mi_int32 nl;
332	int iovcnt;
333	struct iovec iov[2];
334	char data[MILTER_LEN_BYTES + 1];
335
336	if (len > Maxdatasize || (len > 0 && buf == NULL))
337		return MI_FAILURE;
338
339	nl = htonl(len + 1);	/* add 1 for the cmd char */
340	(void) memcpy(data, (void *) &nl, MILTER_LEN_BYTES);
341	data[MILTER_LEN_BYTES] = (char) cmd;
342	i = 0;
343	sl = MILTER_LEN_BYTES + 1;
344
345	/* set up the vector for the size / command */
346	iov[0].iov_base = (void *) data;
347	iov[0].iov_len  = sl;
348	iovcnt = 1;
349	if (len >= 0 && buf != NULL)
350	{
351		iov[1].iov_base = (void *) buf;
352		iov[1].iov_len  = len;
353		iovcnt = 2;
354	}
355
356	l = retry_writev(sd, iov, iovcnt, timeout);
357	if (l == MI_FAILURE)
358		return MI_FAILURE;
359	return MI_SUCCESS;
360}
361