xref: /freebsd/contrib/sendmail/libmilter/comm.c (revision 2ef40764f06885f97d380ee8de0ced64930423db)
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>
12 SM_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 
38 char *
39 mi_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 
250 int
251 mi_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