comm.c revision 102528
1/*
2 *  Copyright (c) 1999-2002 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.54.2.2 2002/08/16 17:09:13 ca Exp $")
13
14#include "libmilter.h"
15#include <sm/errstring.h>
16
17#define FD_Z	FD_ZERO(&readset);			\
18		FD_SET((unsigned int) sd, &readset);	\
19		FD_ZERO(&excset);			\
20		FD_SET((unsigned int) sd, &excset)
21
22/*
23**  MI_RD_CMD -- read a command
24**
25**	Parameters:
26**		sd -- socket descriptor
27**		timeout -- maximum time to wait
28**		cmd -- single character command read from sd
29**		rlen -- pointer to length of result
30**		name -- name of milter
31**
32**	Returns:
33**		buffer with rest of command
34**		(malloc()ed here, should be free()d)
35**		hack: encode error in cmd
36*/
37
38char *
39mi_rd_cmd(sd, timeout, cmd, rlen, name)
40	socket_t sd;
41	struct timeval *timeout;
42	char *cmd;
43	size_t *rlen;
44	char *name;
45{
46	ssize_t len;
47	mi_int32 expl;
48	ssize_t i;
49	fd_set readset, excset;
50	int ret;
51	int save_errno;
52	char *buf;
53	char data[MILTER_LEN_BYTES + 1];
54
55	*cmd = '\0';
56	*rlen = 0;
57
58	if (sd >= FD_SETSIZE)
59	{
60		smi_log(SMI_LOG_ERR, "%s: fd %d is larger than FD_SETSIZE %d",
61			name, sd, FD_SETSIZE);
62		*cmd = SMFIC_SELECT;
63		return NULL;
64	}
65
66	i = 0;
67	for (;;)
68	{
69		FD_Z;
70		ret = select(sd + 1, &readset, NULL, &excset, timeout);
71		if (ret == 0)
72			break;
73		else if (ret < 0)
74		{
75			if (errno == EINTR)
76				continue;
77			break;
78		}
79		if (FD_ISSET(sd, &excset))
80		{
81			*cmd = SMFIC_SELECT;
82			return NULL;
83		}
84
85		len = MI_SOCK_READ(sd, data + i, sizeof data - i);
86		if (MI_SOCK_READ_FAIL(len))
87		{
88			smi_log(SMI_LOG_ERR,
89				"%s, mi_rd_cmd: read returned %d: %s",
90				name, len, sm_errstring(errno));
91			*cmd = SMFIC_RECVERR;
92			return NULL;
93		}
94		if (len == 0)
95		{
96			*cmd = SMFIC_EOF;
97			return NULL;
98		}
99		if (len >= (ssize_t) sizeof data - i)
100			break;
101		i += len;
102	}
103	if (ret == 0)
104	{
105		*cmd = SMFIC_TIMEOUT;
106		return NULL;
107	}
108	else if (ret < 0)
109	{
110		smi_log(SMI_LOG_ERR,
111			"%s: mi_rd_cmd: select returned %d: %s",
112			name, ret, sm_errstring(errno));
113		*cmd = SMFIC_RECVERR;
114		return NULL;
115	}
116
117	*cmd = data[MILTER_LEN_BYTES];
118	data[MILTER_LEN_BYTES] = '\0';
119	(void) memcpy((void *) &expl, (void *) &(data[0]), MILTER_LEN_BYTES);
120	expl = ntohl(expl) - 1;
121	if (expl <= 0)
122		return NULL;
123	if (expl > MILTER_CHUNK_SIZE)
124	{
125		*cmd = SMFIC_TOOBIG;
126		return NULL;
127	}
128#if _FFR_ADD_NULL
129	buf = malloc(expl + 1);
130#else /* _FFR_ADD_NULL */
131	buf = malloc(expl);
132#endif /* _FFR_ADD_NULL */
133	if (buf == NULL)
134	{
135		*cmd = SMFIC_MALLOC;
136		return NULL;
137	}
138
139	i = 0;
140	for (;;)
141	{
142		FD_Z;
143		ret = select(sd + 1, &readset, NULL, &excset, timeout);
144		if (ret == 0)
145			break;
146		else if (ret < 0)
147		{
148			if (errno == EINTR)
149				continue;
150			break;
151		}
152		if (FD_ISSET(sd, &excset))
153		{
154			*cmd = SMFIC_SELECT;
155			free(buf);
156			return NULL;
157		}
158		len = MI_SOCK_READ(sd, buf + i, expl - i);
159		if (MI_SOCK_READ_FAIL(len))
160		{
161			smi_log(SMI_LOG_ERR,
162				"%s: mi_rd_cmd: read returned %d: %s",
163				name, len, sm_errstring(errno));
164			ret = -1;
165			break;
166		}
167		if (len == 0)
168		{
169			*cmd = SMFIC_EOF;
170			free(buf);
171			return NULL;
172		}
173		if (len > expl - i)
174		{
175			*cmd = SMFIC_RECVERR;
176			free(buf);
177			return NULL;
178		}
179		if (len >= expl - i)
180		{
181			*rlen = expl;
182#if _FFR_ADD_NULL
183			/* makes life simpler for common string routines */
184			buf[expl] = '\0';
185#endif /* _FFR_ADD_NULL */
186			return buf;
187		}
188		i += len;
189	}
190
191	save_errno = errno;
192	free(buf);
193
194	/* select returned 0 (timeout) or < 0 (error) */
195	if (ret == 0)
196	{
197		*cmd = SMFIC_TIMEOUT;
198		return NULL;
199	}
200	if (ret < 0)
201	{
202		smi_log(SMI_LOG_ERR,
203			"%s: mi_rd_cmd: select returned %d: %s",
204			name, ret, sm_errstring(save_errno));
205		*cmd = SMFIC_RECVERR;
206		return NULL;
207	}
208	*cmd = SMFIC_UNKNERR;
209	return NULL;
210}
211/*
212**  MI_WR_CMD -- write a cmd to sd
213**
214**	Parameters:
215**		sd -- socket descriptor
216**		timeout -- maximum time to wait (currently unused)
217**		cmd -- single character command to write
218**		buf -- buffer with further data
219**		len -- length of buffer (without cmd!)
220**
221**	Returns:
222**		MI_SUCCESS/MI_FAILURE
223*/
224
225/*
226**  we don't care much about the timeout here, it's very long anyway
227**  FD_SETSIZE is only checked in mi_rd_cmd.
228**  XXX l == 0 ?
229*/
230
231#define MI_WR(data)	\
232	while (sl > 0)							\
233	{								\
234		FD_ZERO(&wrtset);					\
235		FD_SET((unsigned int) sd, &wrtset);			\
236		ret = select(sd + 1, NULL, &wrtset, NULL, timeout);	\
237		if (ret == 0)						\
238			return MI_FAILURE;				\
239		if (ret < 0)						\
240		{							\
241			if (errno == EINTR)				\
242				continue;				\
243			else						\
244				return MI_FAILURE;			\
245		}							\
246		l = MI_SOCK_WRITE(sd, (void *) ((data) + i), sl);	\
247		if (l < 0)						\
248		{							\
249			if (errno == EINTR)				\
250				continue;				\
251			else						\
252				return MI_FAILURE;			\
253		}							\
254		i += l;							\
255		sl -= l;						\
256	}
257
258int
259mi_wr_cmd(sd, timeout, cmd, buf, len)
260	socket_t sd;
261	struct timeval *timeout;
262	int cmd;
263	char *buf;
264	size_t len;
265{
266	size_t sl, i;
267	ssize_t l;
268	mi_int32 nl;
269	int ret;
270	fd_set wrtset;
271	char data[MILTER_LEN_BYTES + 1];
272
273	if (len > MILTER_CHUNK_SIZE)
274		return MI_FAILURE;
275	nl = htonl(len + 1);	/* add 1 for the cmd char */
276	(void) memcpy(data, (void *) &nl, MILTER_LEN_BYTES);
277	data[MILTER_LEN_BYTES] = (char) cmd;
278	i = 0;
279	sl = MILTER_LEN_BYTES + 1;
280
281	/* use writev() instead to send the whole stuff at once? */
282
283	MI_WR(data);
284	if (len > 0 && buf == NULL)
285		return MI_FAILURE;
286	if (len == 0 || buf == NULL)
287		return MI_SUCCESS;
288	i = 0;
289	sl = len;
290	MI_WR(buf);
291	return MI_SUCCESS;
292}
293