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