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