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