comparison dcclib/dccif.c @ 0:c7f6b056b673

First import of vendor version
author Peter Gervai <grin@grin.hu>
date Tue, 10 Mar 2009 13:49:58 +0100
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:c7f6b056b673
1 /* Distributed Checksum Clearinghouse
2 *
3 * dccif(), a sample C interface to the DCC interface daemon, dccifd
4 *
5 * Copyright (c) 2008 by Rhyolite Software, LLC
6 *
7 * This agreement is not applicable to any entity which sells anti-spam
8 * solutions to others or provides an anti-spam solution as part of a
9 * security solution sold to other entities, or to a private network
10 * which employs the DCC or uses data provided by operation of the DCC
11 * but does not provide corresponding data to other users.
12 *
13 * Permission to use, copy, modify, and distribute this software without
14 * changes for any purpose with or without fee is hereby granted, provided
15 * that the above copyright notice and this permission notice appear in all
16 * copies and any distributed versions or copies are either unchanged
17 * or not called anything similar to "DCC" or "Distributed Checksum
18 * Clearinghouse".
19 *
20 * Parties not eligible to receive a license under this agreement can
21 * obtain a commercial license to use DCC by contacting Rhyolite Software
22 * at sales@rhyolite.com.
23 *
24 * A commercial license would be for Distributed Checksum and Reputation
25 * Clearinghouse software. That software includes additional features. This
26 * free license for Distributed ChecksumClearinghouse Software does not in any
27 * way grant permision to use Distributed Checksum and Reputation Clearinghouse
28 * software
29 *
30 * THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
31 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
32 * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
33 * BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES
34 * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
35 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
36 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
37 * SOFTWARE.
38 *
39 * Rhyolite Software DCC 1.3.103-1.31 $Revision$
40 */
41
42
43 #include "dccif.h"
44 #include "dcc_clnt.h"
45 #include "sys/uio.h"
46 #include <sys/un.h>
47 #ifndef DCC_WIN32
48 #include <arpa/inet.h>
49 #endif
50
51
52
53 static void
54 dccif_closes(int *fd1, int *fd2, int *fd3)
55 {
56 if (fd1 && *fd1 >= 0) {
57 close(*fd1);
58 *fd1 = -1;
59 }
60 if (fd2 && *fd2 >= 0) {
61 close(*fd2);
62 *fd2 = -1;
63 }
64 if (fd3 && *fd3 >= 0) {
65 close(*fd3);
66 *fd3 = -1;
67 }
68 }
69
70
71
72 static void PATTRIB(6,7)
73 dccif_pemsg(int ex_code, DCC_EMSG emsg,
74 int *fd1, int *fd2, int *fd3,
75 const char *msg, ...)
76 {
77 va_list args;
78
79 if (emsg) {
80 va_start(args, msg);
81 dcc_vpemsg(ex_code, emsg, msg, args);
82 va_end(args);
83 }
84
85 dccif_closes(fd1, fd2, fd3);
86 }
87
88
89
90 static u_char
91 dccif_sock(DCC_EMSG emsg,
92 const char *srvr_addr, /* home directory, FIFO, or host,port */
93 SOCKET *s, int* fd2, int* fd3)
94 {
95 struct stat sb;
96 char host[DCC_MAXDOMAINLEN];
97 u_int16_t port;
98 DCC_SOCKU su;
99 struct sockaddr_un sunsock;
100 const char *cp;
101 int error;
102
103 /* assume srvr_addr is a hostname,port if not a file or directory */
104 if (0 > stat(srvr_addr, &sb)) {
105 cp = dcc_parse_nm_port(emsg, srvr_addr, DCC_GET_PORT_INVALID,
106 host, sizeof(host),
107 &port, 0, 0,
108 0, 0);
109 if (!cp) {
110 dccif_closes(0, fd2, fd3);
111 return 0;
112 }
113 if (*cp != '\0') {
114 dccif_pemsg(EX_USAGE, emsg, 0, fd2, fd3,
115 "invalid IP address: %s", srvr_addr);
116 return 0;
117 }
118 if (host[0] == '\0') {
119 dccif_pemsg(EX_NOHOST, emsg, 0, fd2, fd3,
120 "missing host name in \"%s\"", srvr_addr);
121 return 0;
122 }
123 dcc_host_lock();
124 if (!dcc_get_host(host, 2, &error)) {
125 dcc_host_unlock();
126 dccif_pemsg(EX_NOHOST, emsg, 0, fd2, fd3,
127 "%s: %s",
128 srvr_addr, DCC_HSTRERROR(error));
129 return 0;
130 }
131 su = dcc_hostaddrs[0];
132 *DCC_SU_PORTP(&su) = port;
133 dcc_host_unlock();
134
135 *s = socket(su.sa.sa_family, SOCK_STREAM, 0);
136 if (*s < 0) {
137 dccif_pemsg(EX_IOERR, emsg, s, fd2, fd3,
138 "socket(AF_UNIX): %s", ERROR_STR());
139 return 0;
140 }
141
142 if (0 > connect(*s, &su.sa, DCC_SU_LEN(&su))) {
143 dccif_pemsg(EX_IOERR, emsg, s, fd2, fd3,
144 "connect(%s): %s", srvr_addr, ERROR_STR());
145 return 0;
146 }
147 return 1;
148 }
149
150 *s = socket(AF_UNIX, SOCK_STREAM, 0);
151 if (*s < 0) {
152 dccif_pemsg(EX_IOERR, emsg, s, fd2, fd3,
153 "socket(AF_UNIX): %s", ERROR_STR());
154 return 0;
155 }
156 memset(&sunsock, 0, sizeof(sunsock));
157 sunsock.sun_family = AF_UNIX;
158 if (S_ISDIR(sb.st_mode))
159 snprintf(sunsock.sun_path, sizeof(sunsock.sun_path),
160 "%s/"DCC_DCCIF_UDS, srvr_addr);
161 else
162 BUFCPY(sunsock.sun_path, srvr_addr);
163 #ifdef HAVE_SA_LEN
164 sunsock.sun_len = SUN_LEN(&sunsock);
165 #endif
166 if (0 > connect(*s, (struct sockaddr *)&sunsock, sizeof(sunsock))) {
167 dccif_pemsg(EX_IOERR, emsg, s, fd2, fd3,
168 "connect(%s): %s",
169 sunsock.sun_path, ERROR_STR());
170 return 0;
171 }
172 return 1;
173 }
174
175
176
177 static u_char
178 dccif_writev(DCC_EMSG emsg,
179 int *out_fd, int *fd2, int *fd3,
180 const struct iovec *iovs,
181 int num_iovs,
182 int wtotal)
183 {
184 int i;
185
186 i = writev(*out_fd, iovs, num_iovs);
187 if (i == wtotal)
188 return 1;
189 if (i < 0)
190 dccif_pemsg(EX_IOERR, emsg, out_fd, fd2, fd3,
191 "dccif writev(%d): %s", wtotal, ERROR_STR());
192 else
193 dccif_pemsg(EX_IOERR, emsg, out_fd, fd2, fd3,
194 "dccif writev(%d)=%d", wtotal, i);
195 return 0;
196 }
197
198
199
200 static u_char
201 dccif_write(DCC_EMSG emsg,
202 int *out_fd, int *fd2, int *fd3,
203 const void *buf, int buf_len)
204 {
205 int i;
206
207 i = write(*out_fd, buf, buf_len);
208 if (i == buf_len)
209 return 1;
210 if (i < 0)
211 dccif_pemsg(EX_IOERR, emsg, out_fd, fd2, fd3,
212 "dccif writev(%d): %s", buf_len, ERROR_STR());
213 else
214 dccif_pemsg(EX_IOERR, emsg, out_fd, fd2, fd3,
215 "dccif writev(%d)=%d",
216 buf_len, i);
217 return 0;
218 }
219
220
221
222 static int
223 dccif_read(DCC_EMSG emsg,
224 int *in_fd, int *fd2, int *fd3,
225 char *buf, int buf_len, int min_read)
226 {
227 int total, i;
228
229 total = 0;
230 for (;;) {
231 i = read(*in_fd, &buf[total], buf_len-total);
232 if (i < 0) {
233 dccif_pemsg(EX_IOERR, emsg, in_fd, fd2, fd3,
234 "dccif read(): %s", ERROR_STR());
235 return -1;
236 }
237 total += i;
238 if (total >= min_read)
239 return total;
240 if (i == 0) {
241 dccif_pemsg(EX_IOERR, emsg, in_fd, fd2, fd3,
242 "dccif read(): premature EOF");
243 return -1;
244 }
245 }
246 }
247
248
249
250 /* This function is mentioned in dccifd/dccif-test/dccif-test.c
251 * and so cannot change lightly. */
252 u_char /* 0=failed or DCCIF_RESULT_* */
253 dccif(DCC_EMSG emsg, /* put error message here */
254 int out_body_fd, /* -1 or write body to here */
255 char **out_body, /* 0 or pointer for resulting body */
256 const char *opts, /* blank separated string DCCIF_OPT_* */
257 const DCC_SOCKU *clnt_addr, /* SMTP client IPv4 or IPv6 address */
258 const char *clnt_name, /* optional SMTP client name */
259 const char *helo,
260 const char *env_from, /* envelope sender */
261 DCCIF_RCPT *rcpts, /* pruned envelope recipients */
262 int in_body_fd, /* -1 or read body from here */
263 const char *in_body, /* 0 or start of incoming body */
264 const char *srvr_addr) /* home directory, FIFO, or host,port */
265 {
266 static const char nl = '\n';
267 static const char cr = '\r';
268 int s;
269 char clnt_str[INET6_ADDRSTRLEN+1+1];
270 struct iovec iovs[50], *iovp;
271 int wtotal;
272 # define ADD_IOV(b,l) {int _l = (l); iovp->iov_base = (char *)(b); \
273 iovp->iov_len = _l; ++iovp; wtotal += _l;}
274 DCCIF_RCPT *rcpt;
275 char buf[4*1024];
276 char result;
277 int read_len, in_body_len, max_body_len, nxt;
278 int i, j;
279
280 if (!srvr_addr || *srvr_addr == '\0')
281 srvr_addr = DCC_HOMEDIR;
282
283 if (emsg)
284 *emsg = '\0';
285
286 if (!clnt_addr) {
287 clnt_str[0] = '\0';
288 } else {
289 dcc_su2str2(clnt_str, sizeof(clnt_str), clnt_addr);
290 }
291
292 if (!dccif_sock(emsg, srvr_addr, &s, &in_body_fd, &out_body_fd))
293 return 0;
294
295 /* first line of request */
296 iovp = iovs;
297 wtotal = 0;
298 if (opts)
299 ADD_IOV(opts, strlen(opts));
300 ADD_IOV(&nl, 1);
301
302 i = strlen(clnt_str);
303 clnt_str[i++] = '\r';
304 ADD_IOV(clnt_str, i);
305 if (i > 1 && clnt_name)
306 ADD_IOV(clnt_name, strlen(clnt_name));
307 ADD_IOV(&nl, 1);
308
309 if (helo)
310 ADD_IOV(helo, strlen(helo));
311 ADD_IOV(&nl, 1);
312
313 if (env_from)
314 ADD_IOV(env_from, strlen(env_from));
315 ADD_IOV(&nl, 1);
316
317 for (rcpt = rcpts; rcpt; rcpt = rcpt->next) {
318 ADD_IOV(rcpt->addr, strlen(rcpt->addr));
319 ADD_IOV(&cr, 1);
320 if (rcpt->user)
321 ADD_IOV(rcpt->user, strlen(rcpt->user));
322 ADD_IOV(&nl, 1);
323 if (iovp >= &iovs[DIM(iovs)-4-1]) {
324 if (!dccif_writev(emsg, &s, &in_body_fd, &out_body_fd,
325 iovs, iovp - iovs, wtotal))
326 return 0;
327 iovp = iovs;
328 wtotal = 0;
329 }
330 }
331 ADD_IOV(&nl, 1);
332
333 /* copy (some of) the body from the buffer to the daemon */
334 if (in_body) {
335 in_body_len = strlen(in_body);
336 ADD_IOV(in_body, in_body_len);
337 } else {
338 in_body_len = 0;
339 }
340
341 if (!dccif_writev(emsg, &s, &in_body_fd, &out_body_fd,
342 iovs, iovp - iovs, wtotal))
343 return 0;
344
345 /* copy (the rest of) the body from input file to the daemon */
346 if (in_body_fd >= 0) {
347 for (;;) {
348 i = read(in_body_fd, buf, sizeof(buf));
349 if (i <= 0) {
350 if (i == 0)
351 break;
352 dccif_pemsg(EX_IOERR, emsg,
353 &s, &in_body_fd, &out_body_fd,
354 "read(body): %s", ERROR_STR());
355 return 0;
356 }
357 if (!dccif_write(emsg, &s, &in_body_fd, &out_body_fd,
358 buf, i))
359 return 0;
360 in_body_len += i;
361 }
362 if (0 > close(in_body_fd)) {
363 dccif_pemsg(EX_IOERR, emsg,
364 &s, &in_body_fd, &out_body_fd,
365 "close(in_body_fd): %s", ERROR_STR());
366 return 0;
367 }
368 in_body_fd = -1;
369 }
370
371 /* tell the daemon it has all of the body */
372 if (0 > shutdown(s, 1)) {
373 dccif_pemsg(EX_IOERR, emsg, &s, &in_body_fd, &out_body_fd,
374 "shutdown(): %s", ERROR_STR());
375 return 0;
376 }
377
378 /* get the overall result */
379 read_len = dccif_read(emsg, &s, &in_body_fd, &out_body_fd,
380 buf, sizeof(buf), 2);
381 if (read_len < 0)
382 return 0;
383 result = buf[0];
384 if (result != DCCIF_RESULT_OK
385 && result != DCCIF_RESULT_GREY
386 && result != DCCIF_RESULT_REJECT
387 && result != DCCIF_RESULT_SOME
388 && result != DCCIF_RESULT_TEMP) {
389 dccif_pemsg(EX_SOFTWARE, emsg, &s, &in_body_fd, &out_body_fd,
390 "unrecognized dccifd result \"%.*s\"", 1, buf);
391 return 0;
392 }
393
394 /* read the vector of results from the daemon */
395 nxt = 1; /* skip '\n' after result */
396 for (rcpt = rcpts; rcpt != 0; rcpt = rcpt->next) {
397 if (++nxt >= read_len) {
398 read_len = dccif_read(emsg,
399 &s, &in_body_fd, &out_body_fd,
400 buf, sizeof(buf), 2);
401 if (read_len < 0)
402 return 0;
403 nxt = 0;
404 }
405 switch (buf[nxt]) {
406 case DCCIF_RCPT_ACCEPT:
407 case DCCIF_RCPT_REJECT:
408 case DCCIF_RCPT_GREY:
409 rcpt->ok = buf[nxt];
410 break;
411 default:
412 dccif_pemsg(EX_SOFTWARE, emsg,
413 &s, &in_body_fd, &out_body_fd,
414 "unrecognized dccifd recipient result"
415 " \"%c\" for %s",
416 buf[nxt], rcpt->addr);
417 return 0;
418 }
419 }
420 if (++nxt >= read_len) {
421 read_len = dccif_read(emsg, &s, &in_body_fd, &out_body_fd,
422 buf, 1, 1);
423 if (read_len < 0)
424 return 0;
425 nxt = 0;
426 }
427 if (buf[nxt++] != '\n') {
428 dccif_pemsg(EX_SOFTWARE, emsg, &s, &in_body_fd, &out_body_fd,
429 "unrecognized dccifd text after results: \"%c\"",
430 buf[nxt]);
431 return 0;
432 }
433
434 /* copy the body from the daemon to the output buffer for file */
435 if (out_body_fd >= 0) {
436 for (;;) {
437 j = read_len - nxt;
438 if (j <= 0) {
439 j = dccif_read(emsg, &s,
440 &in_body_fd, &out_body_fd,
441 buf, sizeof(buf), 0);
442 if (j < 0)
443 return 0;
444 if (j == 0)
445 break;
446 read_len = j;
447 nxt = 0;
448 }
449 if (!dccif_write(emsg, &out_body_fd,
450 &s, &in_body_fd,
451 &buf[nxt], j))
452 return 0;
453 read_len = 0;
454 }
455 if (0 > close(out_body_fd)) {
456 dccif_pemsg(EX_IOERR, emsg,
457 &s, &in_body_fd, &out_body_fd,
458 "close(out_body_fd): %s",
459 ERROR_STR());
460 return 0;
461 }
462 out_body_fd = -1;
463
464 } else if (out_body) {
465 char *body;
466
467 max_body_len = in_body_len + DCC_MAX_XHDR_LEN+1;
468 body = malloc(max_body_len);
469 *out_body = body;
470 j = read_len - nxt;
471 if (j > 0) {
472 if (j > max_body_len)
473 j = max_body_len;
474 memcpy(body, &buf[nxt], j);
475 body += j;
476 max_body_len -= j;
477 }
478 for (;;) {
479 if (max_body_len <= 0) {
480 dccif_pemsg(EX_SOFTWARE, emsg,
481 &s, &in_body_fd, &out_body_fd,
482 "too much body from dccifd");
483 free(*out_body);
484 return 0;
485 }
486
487 j = dccif_read(emsg, &s, &in_body_fd, &out_body_fd,
488 body, max_body_len, 0);
489 if (j < 0) {
490 free(*out_body);
491 return 0;
492 }
493 if (j == 0)
494 break;
495 body += j;
496 max_body_len -= j;
497 }
498 *body = '\0';
499 }
500
501 if (0 > close(s)) {
502 dccif_pemsg(EX_IOERR, emsg, &s, &in_body_fd, &out_body_fd,
503 "close(socket): %s", ERROR_STR());
504 return 0;
505 }
506 return result;
507 #undef ADD_IOV
508 }