0
|
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 } |