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