comm.c revision 64562
1192830Sed/*
2192830Sed *  Copyright (c) 1999-2000 Sendmail, Inc. and its suppliers.
3192830Sed *	All rights reserved.
4192830Sed *
5192830Sed * By using this file, you agree to the terms and conditions set
6192830Sed * forth in the LICENSE file which can be found at the top level of
7192830Sed * the sendmail distribution.
8192830Sed *
9192830Sed */
10192830Sed
11192830Sed#ifndef lint
12192830Sedstatic char id[] = "@(#)$Id: comm.c,v 8.30.4.3 2000/06/12 14:53:01 ca Exp $";
13192830Sed#endif /* ! lint */
14192830Sed
15192830Sed#if _FFR_MILTER
16192830Sed#include "libmilter.h"
17192830Sed
18192830Sed#define FD_Z	FD_ZERO(&readset);	\
19192830Sed		FD_SET((u_int) sd, &readset);	\
20192830Sed		FD_ZERO(&excset);	\
21192830Sed		FD_SET((u_int) sd, &excset)
22192830Sed
23192830Sed/*
24192830Sed**  MI_RD_CMD -- read a command
25192830Sed**
26192830Sed**	Parameters:
27192830Sed**		sd -- socket descriptor
28192830Sed**		timeout -- maximum time to wait
29192830Sed**		cmd -- single character command read from sd
30192830Sed**		rlen -- pointer to length of result
31192830Sed**		name -- name of milter
32192830Sed**
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	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	FD_Z;
66	i = 0;
67	while ((ret = select(sd + 1, &readset, NULL, &excset, timeout)) >= 1)
68	{
69		if (FD_ISSET(sd, &excset))
70		{
71			*cmd = SMFIC_SELECT;
72			return NULL;
73		}
74		if ((len = read(sd, data + i, sizeof data - i)) < 0)
75		{
76			smi_log(SMI_LOG_ERR,
77				"%s, mi_rd_cmd: read returned %d: %s",
78				name, len, strerror(errno));
79			*cmd = SMFIC_RECVERR;
80			return NULL;
81		}
82		if (len == 0)
83		{
84			*cmd = SMFIC_EOF;
85			return NULL;
86		}
87		if (len >= (ssize_t) sizeof data - i)
88			break;
89		i += len;
90		FD_Z;
91	}
92	if (ret == 0)
93	{
94		*cmd = SMFIC_TIMEOUT;
95		return NULL;
96	}
97	else if (ret < 0)
98	{
99		smi_log(SMI_LOG_ERR,
100			"%s: mi_rd_cmd: select returned %d: %s",
101			name, ret, strerror(errno));
102		*cmd = SMFIC_RECVERR;
103		return NULL;
104	}
105
106	*cmd = data[MILTER_LEN_BYTES];
107	data[MILTER_LEN_BYTES] = '\0';
108	(void) memcpy((void *) &expl, (void *) &(data[0]), MILTER_LEN_BYTES);
109	expl = ntohl(expl) - 1;
110	if (expl <= 0)
111		return NULL;
112	if (expl > MILTER_CHUNK_SIZE)
113	{
114		*cmd = SMFIC_TOOBIG;
115		return NULL;
116	}
117	buf = malloc(expl);
118	if (buf == NULL)
119	{
120		*cmd = SMFIC_MALLOC;
121		return NULL;
122	}
123
124	i = 0;
125	FD_Z;
126	while ((ret = select(sd + 1, &readset, NULL, &excset, timeout)) == 1)
127	{
128		if (FD_ISSET(sd, &excset))
129		{
130			*cmd = SMFIC_SELECT;
131			free(buf);
132			return NULL;
133		}
134		if ((len = read(sd, buf + i, expl - i)) < 0)
135		{
136			smi_log(SMI_LOG_ERR,
137				"%s: mi_rd_cmd: read returned %d: %s",
138				name, len, strerror(errno));
139			ret = -1;
140			break;
141		}
142		if (len == 0)
143		{
144			*cmd = SMFIC_EOF;
145			free(buf);
146			return NULL;
147		}
148		if (len > expl - i)
149		{
150			*cmd = SMFIC_RECVERR;
151			free(buf);
152			return NULL;
153		}
154		if (len >= expl - i)
155		{
156			*rlen = expl;
157			return buf;
158		}
159		i += len;
160		FD_Z;
161	}
162
163	save_errno = errno;
164	free(buf);
165
166	/* select returned 0 (timeout) or < 0 (error) */
167	if (ret == 0)
168	{
169		*cmd = SMFIC_TIMEOUT;
170		return NULL;
171	}
172	if (ret < 0)
173	{
174		smi_log(SMI_LOG_ERR,
175			"%s: mi_rd_cmd: select returned %d: %s",
176			name, ret, strerror(save_errno));
177		*cmd = SMFIC_RECVERR;
178		return NULL;
179	}
180	*cmd = SMFIC_UNKNERR;
181	return NULL;
182}
183/*
184**  MI_WR_CMD -- write a cmd to sd
185**
186**	Parameters:
187**		sd -- socket descriptor
188**		timeout -- maximum time to wait (currently unused)
189**		cmd -- single character command to write
190**		buf -- buffer with further data
191**		len -- length of buffer (without cmd!)
192**
193**	Returns:
194**		MI_SUCCESS/MI_FAILURE
195*/
196
197int
198mi_wr_cmd(sd, timeout, cmd, buf, len)
199	socket_t sd;
200	struct timeval *timeout;
201	int cmd;
202	char *buf;
203	size_t len;
204{
205	size_t sl, i;
206	ssize_t l;
207	mi_int32 nl;
208	int ret;
209	fd_set wrtset;
210	char data[MILTER_LEN_BYTES + 1];
211
212	if (len > MILTER_CHUNK_SIZE)
213		return MI_FAILURE;
214	nl = htonl(len + 1);	/* add 1 for the cmd char */
215	(void) memcpy(data, (void *) &nl, MILTER_LEN_BYTES);
216	data[MILTER_LEN_BYTES] = (char) cmd;
217	i = 0;
218	sl = MILTER_LEN_BYTES + 1;
219
220	do {
221		FD_ZERO(&wrtset);
222		FD_SET((u_int) sd, &wrtset);
223		if ((ret = select(sd + 1, NULL, &wrtset, NULL, timeout)) == 0)
224			return MI_FAILURE;
225	} while (ret < 0 && errno == EINTR);
226	if (ret < 0)
227		return MI_FAILURE;
228
229	/* use writev() instead to send the whole stuff at once? */
230	while ((l = write(sd, (void *) (data + i), sl - i)) < (ssize_t) sl)
231	{
232		if (l < 0)
233			return MI_FAILURE;
234		i += l;
235		sl -= l;
236	}
237
238	if (len > 0 && buf == NULL)
239		return MI_FAILURE;
240	if (len == 0 || buf == NULL)
241		return MI_SUCCESS;
242	i = 0;
243	sl = len;
244	do {
245		FD_ZERO(&wrtset);
246		FD_SET((u_int) sd, &wrtset);
247		if ((ret = select(sd + 1, NULL, &wrtset, NULL, timeout)) == 0)
248			return MI_FAILURE;
249	} while (ret < 0 && errno == EINTR);
250	if (ret < 0)
251		return MI_FAILURE;
252	while ((l = write(sd, (void *) (buf + i), sl - i)) < (ssize_t) sl)
253	{
254		if (l < 0)
255			return MI_FAILURE;
256		i += l;
257		sl -= l;
258	}
259	return MI_SUCCESS;
260}
261#endif /* _FFR_MILTER */
262