xref: /freebsd/contrib/sendmail/libmilter/comm.c (revision f9218d3d4fd34f082473b3a021c6d4d109fb47cf)
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 }
279