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