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