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