]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - contrib/sendmail/libmilter/comm.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / contrib / sendmail / libmilter / comm.c
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 }