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