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