Initial import from FreeBSD RELENG_4:
[dragonfly.git] / contrib / sendmail / libmilter / comm.c
1 /*
2  *  Copyright (c) 1999-2003 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.54.2.6 2003/01/03 22:14:40 ca Exp $")
13
14 #include "libmilter.h"
15 #include <sm/errstring.h>
16
17 /*
18 **  MI_RD_CMD -- read a command
19 **
20 **      Parameters:
21 **              sd -- socket descriptor
22 **              timeout -- maximum time to wait
23 **              cmd -- single character command read from sd
24 **              rlen -- pointer to length of result
25 **              name -- name of milter
26 **
27 **      Returns:
28 **              buffer with rest of command
29 **              (malloc()ed here, should be free()d)
30 **              hack: encode error in cmd
31 */
32
33 char *
34 mi_rd_cmd(sd, timeout, cmd, rlen, name)
35         socket_t sd;
36         struct timeval *timeout;
37         char *cmd;
38         size_t *rlen;
39         char *name;
40 {
41         ssize_t len;
42         mi_int32 expl;
43         ssize_t i;
44         FD_RD_VAR(rds, excs);
45         int ret;
46         int save_errno;
47         char *buf;
48         char data[MILTER_LEN_BYTES + 1];
49
50         *cmd = '\0';
51         *rlen = 0;
52
53         i = 0;
54         for (;;)
55         {
56                 FD_RD_INIT(sd, rds, excs);
57                 ret = FD_RD_READY(sd, rds, excs, timeout);
58                 if (ret == 0)
59                         break;
60                 else if (ret < 0)
61                 {
62                         if (errno == EINTR)
63                                 continue;
64                         break;
65                 }
66                 if (FD_IS_RD_EXC(sd, rds, excs))
67                 {
68                         *cmd = SMFIC_SELECT;
69                         return NULL;
70                 }
71
72                 len = MI_SOCK_READ(sd, data + i, sizeof data - i);
73                 if (MI_SOCK_READ_FAIL(len))
74                 {
75                         smi_log(SMI_LOG_ERR,
76                                 "%s, mi_rd_cmd: read returned %d: %s",
77                                 name, (int) len, sm_errstring(errno));
78                         *cmd = SMFIC_RECVERR;
79                         return NULL;
80                 }
81                 if (len == 0)
82                 {
83                         *cmd = SMFIC_EOF;
84                         return NULL;
85                 }
86                 if (len >= (ssize_t) sizeof data - i)
87                         break;
88                 i += len;
89         }
90         if (ret == 0)
91         {
92                 *cmd = SMFIC_TIMEOUT;
93                 return NULL;
94         }
95         else if (ret < 0)
96         {
97                 smi_log(SMI_LOG_ERR,
98                         "%s: mi_rd_cmd: select returned %d: %s",
99                         name, ret, sm_errstring(errno));
100                 *cmd = SMFIC_RECVERR;
101                 return NULL;
102         }
103
104         *cmd = data[MILTER_LEN_BYTES];
105         data[MILTER_LEN_BYTES] = '\0';
106         (void) memcpy((void *) &expl, (void *) &(data[0]), MILTER_LEN_BYTES);
107         expl = ntohl(expl) - 1;
108         if (expl <= 0)
109                 return NULL;
110         if (expl > MILTER_CHUNK_SIZE)
111         {
112                 *cmd = SMFIC_TOOBIG;
113                 return NULL;
114         }
115 #if _FFR_ADD_NULL
116         buf = malloc(expl + 1);
117 #else /* _FFR_ADD_NULL */
118         buf = malloc(expl);
119 #endif /* _FFR_ADD_NULL */
120         if (buf == NULL)
121         {
122                 *cmd = SMFIC_MALLOC;
123                 return NULL;
124         }
125
126         i = 0;
127         for (;;)
128         {
129                 FD_RD_INIT(sd, rds, excs);
130                 ret = FD_RD_READY(sd, rds, excs, timeout);
131                 if (ret == 0)
132                         break;
133                 else if (ret < 0)
134                 {
135                         if (errno == EINTR)
136                                 continue;
137                         break;
138                 }
139                 if (FD_IS_RD_EXC(sd, rds, excs))
140                 {
141                         *cmd = SMFIC_SELECT;
142                         free(buf);
143                         return NULL;
144                 }
145                 len = MI_SOCK_READ(sd, buf + i, expl - i);
146                 if (MI_SOCK_READ_FAIL(len))
147                 {
148                         smi_log(SMI_LOG_ERR,
149                                 "%s: mi_rd_cmd: read returned %d: %s",
150                                 name, (int) len, sm_errstring(errno));
151                         ret = -1;
152                         break;
153                 }
154                 if (len == 0)
155                 {
156                         *cmd = SMFIC_EOF;
157                         free(buf);
158                         return NULL;
159                 }
160                 if (len > expl - i)
161                 {
162                         *cmd = SMFIC_RECVERR;
163                         free(buf);
164                         return NULL;
165                 }
166                 if (len >= expl - i)
167                 {
168                         *rlen = expl;
169 #if _FFR_ADD_NULL
170                         /* makes life simpler for common string routines */
171                         buf[expl] = '\0';
172 #endif /* _FFR_ADD_NULL */
173                         return buf;
174                 }
175                 i += len;
176         }
177
178         save_errno = errno;
179         free(buf);
180
181         /* select returned 0 (timeout) or < 0 (error) */
182         if (ret == 0)
183         {
184                 *cmd = SMFIC_TIMEOUT;
185                 return NULL;
186         }
187         if (ret < 0)
188         {
189                 smi_log(SMI_LOG_ERR,
190                         "%s: mi_rd_cmd: select returned %d: %s",
191                         name, ret, sm_errstring(save_errno));
192                 *cmd = SMFIC_RECVERR;
193                 return NULL;
194         }
195         *cmd = SMFIC_UNKNERR;
196         return NULL;
197 }
198 /*
199 **  MI_WR_CMD -- write a cmd to sd
200 **
201 **      Parameters:
202 **              sd -- socket descriptor
203 **              timeout -- maximum time to wait (currently unused)
204 **              cmd -- single character command to write
205 **              buf -- buffer with further data
206 **              len -- length of buffer (without cmd!)
207 **
208 **      Returns:
209 **              MI_SUCCESS/MI_FAILURE
210 */
211
212 /*
213 **  we don't care much about the timeout here, it's very long anyway
214 **  FD_SETSIZE is checked when socket is created.
215 **  XXX l == 0 ?
216 */
217
218 #define MI_WR(data)     \
219         while (sl > 0)                                                  \
220         {                                                               \
221                 FD_WR_INIT(sd, wrs);                                    \
222                 ret = FD_WR_READY(sd, wrs, timeout);                    \
223                 if (ret == 0)                                           \
224                         return MI_FAILURE;                              \
225                 if (ret < 0)                                            \
226                 {                                                       \
227                         if (errno == EINTR)                             \
228                                 continue;                               \
229                         else                                            \
230                                 return MI_FAILURE;                      \
231                 }                                                       \
232                 l = MI_SOCK_WRITE(sd, (void *) ((data) + i), sl);       \
233                 if (l < 0)                                              \
234                 {                                                       \
235                         if (errno == EINTR)                             \
236                                 continue;                               \
237                         else                                            \
238                                 return MI_FAILURE;                      \
239                 }                                                       \
240                 i += l;                                                 \
241                 sl -= l;                                                \
242         }
243
244 int
245 mi_wr_cmd(sd, timeout, cmd, buf, len)
246         socket_t sd;
247         struct timeval *timeout;
248         int cmd;
249         char *buf;
250         size_t len;
251 {
252         size_t sl, i;
253         ssize_t l;
254         mi_int32 nl;
255         int ret;
256         FD_WR_VAR(wrs);
257         char data[MILTER_LEN_BYTES + 1];
258
259         if (len > MILTER_CHUNK_SIZE)
260                 return MI_FAILURE;
261         nl = htonl(len + 1);    /* add 1 for the cmd char */
262         (void) memcpy(data, (void *) &nl, MILTER_LEN_BYTES);
263         data[MILTER_LEN_BYTES] = (char) cmd;
264         i = 0;
265         sl = MILTER_LEN_BYTES + 1;
266
267         /* use writev() instead to send the whole stuff at once? */
268
269         MI_WR(data);
270         if (len > 0 && buf == NULL)
271                 return MI_FAILURE;
272         if (len == 0 || buf == NULL)
273                 return MI_SUCCESS;
274         i = 0;
275         sl = len;
276         MI_WR(buf);
277         return MI_SUCCESS;
278 }