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