]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/sendmail/libmilter/comm.c
This commit was generated by cvs2svn to compensate for changes in r136632,
[FreeBSD/FreeBSD.git] / contrib / sendmail / libmilter / comm.c
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 }