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
smfi_setmaxdatasize(sz)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 *
mi_rd_cmd(sd,timeout,cmd,rlen,name)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
retry_writev(fd,iov,iovcnt,timeout)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
mi_wr_cmd(sd,timeout,cmd,buf,len)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