xref: /freebsd/contrib/sendmail/libmilter/comm.c (revision 9336e0699bda8a301cd2bfa37106b6ec5e32012e)
1 /*
2  *  Copyright (c) 1999-2004 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.67 2006/11/02 17:54:44 ca Exp $")
13 
14 #include "libmilter.h"
15 #include <sm/errstring.h>
16 #include <sys/uio.h>
17 
18 static ssize_t	retry_writev __P((socket_t, struct iovec *, int, struct timeval *));
19 static size_t Maxdatasize = MILTER_MAX_DATA_SIZE;
20 
21 #if _FFR_MAXDATASIZE
22 /*
23 **  SMFI_SETMAXDATASIZE -- set limit for milter data read/write.
24 **
25 **	Parameters:
26 **		sz -- new limit.
27 **
28 **	Returns:
29 **		old limit
30 */
31 
32 size_t
33 smfi_setmaxdatasize(sz)
34 	size_t sz;
35 {
36 	size_t old;
37 
38 	old = Maxdatasize;
39 	Maxdatasize = sz;
40 	return old;
41 }
42 #endif /* _FFR_MAXDATASIZE */
43 
44 /*
45 **  MI_RD_CMD -- read a command
46 **
47 **	Parameters:
48 **		sd -- socket descriptor
49 **		timeout -- maximum time to wait
50 **		cmd -- single character command read from sd
51 **		rlen -- pointer to length of result
52 **		name -- name of milter
53 **
54 **	Returns:
55 **		buffer with rest of command
56 **		(malloc()ed here, should be free()d)
57 **		hack: encode error in cmd
58 */
59 
60 char *
61 mi_rd_cmd(sd, timeout, cmd, rlen, name)
62 	socket_t sd;
63 	struct timeval *timeout;
64 	char *cmd;
65 	size_t *rlen;
66 	char *name;
67 {
68 	ssize_t len;
69 	mi_int32 expl;
70 	ssize_t i;
71 	FD_RD_VAR(rds, excs);
72 	int ret;
73 	int save_errno;
74 	char *buf;
75 	char data[MILTER_LEN_BYTES + 1];
76 
77 	*cmd = '\0';
78 	*rlen = 0;
79 
80 	i = 0;
81 	for (;;)
82 	{
83 		FD_RD_INIT(sd, rds, excs);
84 		ret = FD_RD_READY(sd, rds, excs, timeout);
85 		if (ret == 0)
86 			break;
87 		else if (ret < 0)
88 		{
89 			if (errno == EINTR)
90 				continue;
91 			break;
92 		}
93 		if (FD_IS_RD_EXC(sd, rds, excs))
94 		{
95 			*cmd = SMFIC_SELECT;
96 			return NULL;
97 		}
98 
99 		len = MI_SOCK_READ(sd, data + i, sizeof data - i);
100 		if (MI_SOCK_READ_FAIL(len))
101 		{
102 			smi_log(SMI_LOG_ERR,
103 				"%s, mi_rd_cmd: read returned %d: %s",
104 				name, (int) len, sm_errstring(errno));
105 			*cmd = SMFIC_RECVERR;
106 			return NULL;
107 		}
108 		if (len == 0)
109 		{
110 			*cmd = SMFIC_EOF;
111 			return NULL;
112 		}
113 		if (len >= (ssize_t) sizeof data - i)
114 			break;
115 		i += len;
116 	}
117 	if (ret == 0)
118 	{
119 		*cmd = SMFIC_TIMEOUT;
120 		return NULL;
121 	}
122 	else if (ret < 0)
123 	{
124 		smi_log(SMI_LOG_ERR,
125 			"%s: mi_rd_cmd: select returned %d: %s",
126 			name, ret, sm_errstring(errno));
127 		*cmd = SMFIC_RECVERR;
128 		return NULL;
129 	}
130 
131 	*cmd = data[MILTER_LEN_BYTES];
132 	data[MILTER_LEN_BYTES] = '\0';
133 	(void) memcpy((void *) &expl, (void *) &(data[0]), MILTER_LEN_BYTES);
134 	expl = ntohl(expl) - 1;
135 	if (expl <= 0)
136 		return NULL;
137 	if (expl > Maxdatasize)
138 	{
139 		*cmd = SMFIC_TOOBIG;
140 		return NULL;
141 	}
142 #if _FFR_ADD_NULL
143 	buf = malloc(expl + 1);
144 #else /* _FFR_ADD_NULL */
145 	buf = malloc(expl);
146 #endif /* _FFR_ADD_NULL */
147 	if (buf == NULL)
148 	{
149 		*cmd = SMFIC_MALLOC;
150 		return NULL;
151 	}
152 
153 	i = 0;
154 	for (;;)
155 	{
156 		FD_RD_INIT(sd, rds, excs);
157 		ret = FD_RD_READY(sd, rds, excs, timeout);
158 		if (ret == 0)
159 			break;
160 		else if (ret < 0)
161 		{
162 			if (errno == EINTR)
163 				continue;
164 			break;
165 		}
166 		if (FD_IS_RD_EXC(sd, rds, excs))
167 		{
168 			*cmd = SMFIC_SELECT;
169 			free(buf);
170 			return NULL;
171 		}
172 		len = MI_SOCK_READ(sd, buf + i, expl - i);
173 		if (MI_SOCK_READ_FAIL(len))
174 		{
175 			smi_log(SMI_LOG_ERR,
176 				"%s: mi_rd_cmd: read returned %d: %s",
177 				name, (int) len, sm_errstring(errno));
178 			ret = -1;
179 			break;
180 		}
181 		if (len == 0)
182 		{
183 			*cmd = SMFIC_EOF;
184 			free(buf);
185 			return NULL;
186 		}
187 		if (len > expl - i)
188 		{
189 			*cmd = SMFIC_RECVERR;
190 			free(buf);
191 			return NULL;
192 		}
193 		if (len >= expl - i)
194 		{
195 			*rlen = expl;
196 #if _FFR_ADD_NULL
197 			/* makes life simpler for common string routines */
198 			buf[expl] = '\0';
199 #endif /* _FFR_ADD_NULL */
200 			return buf;
201 		}
202 		i += len;
203 	}
204 
205 	save_errno = errno;
206 	free(buf);
207 
208 	/* select returned 0 (timeout) or < 0 (error) */
209 	if (ret == 0)
210 	{
211 		*cmd = SMFIC_TIMEOUT;
212 		return NULL;
213 	}
214 	if (ret < 0)
215 	{
216 		smi_log(SMI_LOG_ERR,
217 			"%s: mi_rd_cmd: select returned %d: %s",
218 			name, ret, sm_errstring(save_errno));
219 		*cmd = SMFIC_RECVERR;
220 		return NULL;
221 	}
222 	*cmd = SMFIC_UNKNERR;
223 	return NULL;
224 }
225 
226 /*
227 **  RETRY_WRITEV -- Keep calling the writev() system call
228 **	until all the data is written out or an error occurs.
229 **
230 **	Parameters:
231 **		fd -- socket descriptor
232 **		iov -- io vector
233 **		iovcnt -- number of elements in io vector
234 **			must NOT exceed UIO_MAXIOV.
235 **		timeout -- maximum time to wait
236 **
237 **	Returns:
238 **		success: number of bytes written
239 **		otherwise: MI_FAILURE
240 */
241 
242 static ssize_t
243 retry_writev(fd, iov, iovcnt, timeout)
244 	socket_t fd;
245 	struct iovec *iov;
246 	int iovcnt;
247 	struct timeval *timeout;
248 {
249 	int i;
250 	ssize_t n, written;
251 	FD_WR_VAR(wrs);
252 
253 	written = 0;
254 	for (;;)
255 	{
256 		while (iovcnt > 0 && iov[0].iov_len == 0)
257 		{
258 			iov++;
259 			iovcnt--;
260 		}
261 		if (iovcnt <= 0)
262 			return written;
263 
264 		/*
265 		**  We don't care much about the timeout here,
266 		**  it's very long anyway; correct solution would be
267 		**  to take the time before the loop and reduce the
268 		**  timeout after each invocation.
269 		**  FD_SETSIZE is checked when socket is created.
270 		*/
271 
272 		FD_WR_INIT(fd, wrs);
273 		i = FD_WR_READY(fd, wrs, timeout);
274 		if (i == 0)
275 			return MI_FAILURE;
276 		if (i < 0)
277 		{
278 			if (errno == EINTR || errno == EAGAIN)
279 				continue;
280 			return MI_FAILURE;
281 		}
282 		n = writev(fd, iov, iovcnt);
283 		if (n == -1)
284 		{
285 			if (errno == EINTR || errno == EAGAIN)
286 				continue;
287 			return MI_FAILURE;
288 		}
289 
290 		written += n;
291 		for (i = 0; i < iovcnt; i++)
292 		{
293 			if (iov[i].iov_len > (unsigned int) n)
294 			{
295 				iov[i].iov_base = (char *)iov[i].iov_base + n;
296 				iov[i].iov_len -= (unsigned int) n;
297 				break;
298 			}
299 			n -= (int) iov[i].iov_len;
300 			iov[i].iov_len = 0;
301 		}
302 		if (i == iovcnt)
303 			return written;
304 	}
305 }
306 
307 /*
308 **  MI_WR_CMD -- write a cmd to sd
309 **
310 **	Parameters:
311 **		sd -- socket descriptor
312 **		timeout -- maximum time to wait
313 **		cmd -- single character command to write
314 **		buf -- buffer with further data
315 **		len -- length of buffer (without cmd!)
316 **
317 **	Returns:
318 **		MI_SUCCESS/MI_FAILURE
319 */
320 
321 int
322 mi_wr_cmd(sd, timeout, cmd, buf, len)
323 	socket_t sd;
324 	struct timeval *timeout;
325 	int cmd;
326 	char *buf;
327 	size_t len;
328 {
329 	size_t sl, i;
330 	ssize_t l;
331 	mi_int32 nl;
332 	int iovcnt;
333 	struct iovec iov[2];
334 	char data[MILTER_LEN_BYTES + 1];
335 
336 	if (len > Maxdatasize || (len > 0 && buf == NULL))
337 		return MI_FAILURE;
338 
339 	nl = htonl(len + 1);	/* add 1 for the cmd char */
340 	(void) memcpy(data, (void *) &nl, MILTER_LEN_BYTES);
341 	data[MILTER_LEN_BYTES] = (char) cmd;
342 	i = 0;
343 	sl = MILTER_LEN_BYTES + 1;
344 
345 	/* set up the vector for the size / command */
346 	iov[0].iov_base = (void *) data;
347 	iov[0].iov_len  = sl;
348 	iovcnt = 1;
349 	if (len >= 0 && buf != NULL)
350 	{
351 		iov[1].iov_base = (void *) buf;
352 		iov[1].iov_len  = len;
353 		iovcnt = 2;
354 	}
355 
356 	l = retry_writev(sd, iov, iovcnt, timeout);
357 	if (l == MI_FAILURE)
358 		return MI_FAILURE;
359 	return MI_SUCCESS;
360 }
361