0
|
1 /* Distributed Checksum Clearinghouse |
|
2 * |
|
3 * compute simple checksums |
|
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.90 $Revision$ |
|
40 */ |
|
41 |
|
42 #include "dcc_ck.h" |
|
43 #include "dcc_heap_debug.h" |
|
44 #include "dcc_xhdr.h" |
|
45 #ifndef DCC_WIN32 |
|
46 #include <arpa/inet.h> |
|
47 #endif |
|
48 |
|
49 |
|
50 /* "substitute" or locally configured checksums */ |
|
51 typedef struct { |
|
52 u_int nm_len; |
|
53 const char *nm; /* name of the checksum */ |
|
54 } DCC_SUB_CK; |
|
55 static DCC_SUB_CK sub_cks[DCC_MAX_SUB_CKS]; |
|
56 static u_int num_sub_cks; |
|
57 |
|
58 |
|
59 /* get the checksum of an IPv6 address */ |
|
60 void |
|
61 dcc_ck_ipv6(DCC_SUM sum, const struct in6_addr *addr) |
|
62 { |
|
63 MD5_CTX ctx; |
|
64 |
|
65 MD5Init(&ctx); |
|
66 MD5Update(&ctx, (void *)addr, sizeof(*addr)); |
|
67 MD5Final(sum, &ctx); |
|
68 } |
|
69 |
|
70 |
|
71 |
|
72 /* add an IP address to the set of checksums */ |
|
73 void |
|
74 dcc_get_ipv6_ck(DCC_GOT_CKS *cks, const struct in6_addr *addrp) |
|
75 { |
|
76 cks->sums[DCC_CK_IP].type = DCC_CK_IP; |
|
77 cks->sums[DCC_CK_IP].rpt2srvr = 1; |
|
78 cks->sums[DCC_CK_IP].tgts = DCC_TGTS_INVALID; |
|
79 dcc_ck_ipv6(cks->sums[DCC_CK_IP].sum, addrp); |
|
80 |
|
81 if (&cks->ip_addr != addrp) |
|
82 cks->ip_addr = *addrp; |
|
83 } |
|
84 |
|
85 |
|
86 |
|
87 void |
|
88 dcc_unget_ip_ck(DCC_GOT_CKS *cks) |
|
89 { |
|
90 memset(&cks->ip_addr, 0, sizeof(cks->ip_addr)); |
|
91 CLR_GOT_SUM(&cks->sums[DCC_CK_IP]); |
|
92 CLR_GOT_SUM(&cks->sums[DCC_CK_IP]); |
|
93 } |
|
94 |
|
95 |
|
96 |
|
97 /* Make DCC_CK_IP from a string containing an IPv4 or IPv6 address. |
|
98 * Because inet_pton() is picky, the string must be unambiguous and |
|
99 * fussy. */ |
|
100 u_char |
|
101 dcc_get_str_ip_ck(DCC_GOT_CKS *cks, /* put checksum here */ |
|
102 const char *str) /* from this IP address string */ |
|
103 { |
|
104 DCC_SOCKU su; |
|
105 |
|
106 if (!dcc_str2ip(&su, str)) |
|
107 return 0; |
|
108 |
|
109 if (su.sa.sa_family == AF_INET) { |
|
110 /* treat IPv4 addresses as IPv6 so that everyone computes |
|
111 * the same checksum */ |
|
112 dcc_ipv4toipv6(&cks->ip_addr, su.ipv4.sin_addr); |
|
113 } else { |
|
114 cks->ip_addr = su.ipv6.sin6_addr; |
|
115 } |
|
116 |
|
117 dcc_get_ipv6_ck(cks, &cks->ip_addr); |
|
118 return 1; |
|
119 } |
|
120 |
|
121 |
|
122 |
|
123 /* Compute a checksum from a string with matching but optional carets or |
|
124 * quotes, after stripping the quotes or carets. |
|
125 * Ignore case and white space */ |
|
126 void |
|
127 dcc_str2ck(DCC_SUM sum, |
|
128 const char *hdr, /* substitute header type */ |
|
129 u_int hdr_len, |
|
130 const char *str) /* string to checksum */ |
|
131 { |
|
132 MD5_CTX ctx; |
|
133 u_int len; |
|
134 char *p; |
|
135 char c, cbuf[DCC_HDR_CK_MAX]; |
|
136 |
|
137 /* ignore whitespace, [<>'",] and case |
|
138 * do not ignore [.-_] to prevent confusing hostnames */ |
|
139 p = cbuf; |
|
140 while ((c = *str++) != '\0' && p <= LAST(cbuf)) { |
|
141 if (DCC_IS_WHITE(c) |
|
142 || c == '<' || c == '>' |
|
143 || c == '\'' || c == '"' || c == ',') |
|
144 continue; |
|
145 *p++ = DCC_TO_LOWER(c); |
|
146 } |
|
147 str = cbuf; |
|
148 len = p - str; |
|
149 /* strip trailing periods, mostly for mail_host */ |
|
150 while (len >= 1 |
|
151 && *(p-1) == '.') { |
|
152 --len; |
|
153 --p; |
|
154 } |
|
155 MD5Init(&ctx); |
|
156 if (hdr) |
|
157 MD5Update(&ctx, hdr, hdr_len); |
|
158 MD5Update(&ctx, str, len); |
|
159 MD5Final(sum, &ctx); |
|
160 } |
|
161 |
|
162 |
|
163 |
|
164 /* make checksum from a string for headers and envelope */ |
|
165 u_char /* 1=ok 0=bad string */ |
|
166 dcc_get_cks(DCC_GOT_CKS *cks, /* put checksum here */ |
|
167 DCC_CK_TYPES type, |
|
168 const char *str, /* checksum of this string */ |
|
169 u_char rpt2srvr) |
|
170 { |
|
171 DCC_GOT_SUM *g; |
|
172 |
|
173 g = &cks->sums[type]; |
|
174 |
|
175 switch (type) { |
|
176 case DCC_CK_INVALID: |
|
177 case DCC_CK_IP: |
|
178 case DCC_CK_SUB: |
|
179 case DCC_CK_SRVR_ID: |
|
180 case DCC_CK_BODY: |
|
181 case DCC_CK_FUZ1: |
|
182 case DCC_CK_FUZ2: |
|
183 case DCC_CK_G_MSG_R_TOTAL: |
|
184 case DCC_CK_G_TRIPLE_R_BULK: |
|
185 dcc_logbad(EX_SOFTWARE, "invalid checksum %s", |
|
186 dcc_type2str_err(type, 0, 0, 0)); |
|
187 return 0; |
|
188 |
|
189 case DCC_CK_ENV_FROM: |
|
190 case DCC_CK_FROM: |
|
191 case DCC_CK_ENV_TO: |
|
192 case DCC_CK_RECEIVED: |
|
193 case DCC_CK_MESSAGE_ID: |
|
194 dcc_str2ck(g->sum, 0, 0, str); |
|
195 break; |
|
196 } |
|
197 |
|
198 g->type = type; |
|
199 g->rpt2srvr = rpt2srvr; |
|
200 g->tgts = DCC_TGTS_INVALID; |
|
201 return 1; |
|
202 } |
|
203 |
|
204 |
|
205 |
|
206 /* make checksum for a locally configured header */ |
|
207 u_char /* 1=done 0=failed */ |
|
208 dcc_ck_get_sub(DCC_GOT_CKS *cks, |
|
209 const char *hdr, /* header name, not '\0' terminated */ |
|
210 const char *str) /* header value if not after hdr */ |
|
211 { |
|
212 DCC_GOT_SUM *g; |
|
213 const DCC_SUB_CK *sck; |
|
214 DCC_CK_TYPES type; |
|
215 int i; |
|
216 |
|
217 /* look for the header name in the list of locally configured headers */ |
|
218 sck = &sub_cks[0]; |
|
219 for (i = num_sub_cks; ; ++sck, --i) { |
|
220 if (i <= 0) |
|
221 return 0; /* this header is not in the list */ |
|
222 if (!strncasecmp(hdr, sck->nm, sck->nm_len) |
|
223 && (hdr[sck->nm_len] == '\0' |
|
224 || hdr[sck->nm_len] == ':')) |
|
225 break; |
|
226 } |
|
227 |
|
228 /* Get the header value if the caller did not separate it. |
|
229 * The colon is present if the header field was not separated */ |
|
230 if (!str) |
|
231 str = hdr+sck->nm_len+1; |
|
232 |
|
233 /* find a free checksum slot |
|
234 * or a slot already assigned to the header */ |
|
235 type = DCC_CK_SUB; |
|
236 g = &cks->sums[type]; |
|
237 for (;;) { |
|
238 if (type >= DIM(cks->sums)) |
|
239 return 0; /* none free */ |
|
240 |
|
241 if (g->type == DCC_CK_INVALID |
|
242 && (type > DCC_CK_TYPE_LAST |
|
243 || type == DCC_CK_SUB)) |
|
244 break; /* found a free slot */ |
|
245 |
|
246 if (g->type == DCC_CK_SUB |
|
247 && g->hdr_nm == sck->nm) |
|
248 break; /* found previously assigned slot */ |
|
249 ++g; |
|
250 ++type; |
|
251 } |
|
252 |
|
253 dcc_str2ck(g->sum, sck->nm, sck->nm_len, str); |
|
254 g->type = DCC_CK_SUB; |
|
255 g->rpt2srvr = 1; |
|
256 g->tgts = DCC_TGTS_INVALID; |
|
257 g->hdr_nm = sck->nm; |
|
258 return 1; |
|
259 } |
|
260 |
|
261 |
|
262 |
|
263 /* add to the list of locally configured or substitute headers */ |
|
264 u_char |
|
265 dcc_add_sub_hdr(DCC_EMSG emsg, const char *hdr) |
|
266 { |
|
267 const char *p; |
|
268 char c, *q; |
|
269 u_int n, len; |
|
270 |
|
271 if (num_sub_cks >= DIM(sub_cks)) { |
|
272 dcc_pemsg(EX_USAGE, emsg, |
|
273 "too many substitute headers with \"%s\"", hdr); |
|
274 return 0; |
|
275 } |
|
276 |
|
277 p = hdr; |
|
278 for (;;) { |
|
279 if (*p == '\0') |
|
280 break; |
|
281 if (*p == ':' && p[1] == '\0') { |
|
282 --p; |
|
283 break; |
|
284 } |
|
285 if (*p <= ' ' || *p >= 0x7f || *p == ':') { |
|
286 dcc_pemsg(EX_USAGE, emsg, |
|
287 "illegal SMTP field name character in \"%s\"", |
|
288 hdr); |
|
289 return 0; |
|
290 } |
|
291 ++p; |
|
292 } |
|
293 |
|
294 len = p - hdr; |
|
295 if (len == 0) { |
|
296 dcc_pemsg(EX_USAGE, emsg, "illegal empty field name"); |
|
297 return 0; |
|
298 } |
|
299 |
|
300 /* ignore duplicates */ |
|
301 for (n = 0; n < num_sub_cks; ++n) { |
|
302 if (len == sub_cks[n].nm_len |
|
303 && !strncasecmp(hdr, sub_cks[n].nm, len)) |
|
304 return 1; |
|
305 } |
|
306 |
|
307 sub_cks[num_sub_cks].nm_len = len; |
|
308 q = dcc_malloc(len+1); |
|
309 sub_cks[num_sub_cks].nm = q; |
|
310 do { |
|
311 c = *hdr++; |
|
312 *q++ = DCC_TO_LOWER(c); |
|
313 } while (--len > 0); |
|
314 *q = '\0'; |
|
315 ++num_sub_cks; |
|
316 |
|
317 return 1; |
|
318 } |
|
319 |
|
320 |
|
321 |
|
322 static int |
|
323 get_received_addr(char addr_buf[INET6_ADDRSTRLEN+2], |
|
324 const char *hdr) /* *hdr == '[' before the address */ |
|
325 { |
|
326 int a_len; |
|
327 |
|
328 |
|
329 a_len = 1+strspn(hdr+1, ".:abcdefABCDEF0123456789"); |
|
330 if (a_len <= 6+1 || a_len >= INET6_ADDRSTRLEN+1) |
|
331 return 0; |
|
332 if (hdr[a_len] != ']') |
|
333 return 0; |
|
334 |
|
335 /* capture the address |
|
336 * include leading '[' in case we later need a host name */ |
|
337 memcpy(addr_buf, hdr, a_len); |
|
338 addr_buf[a_len] = '\0'; |
|
339 |
|
340 return a_len; |
|
341 } |
|
342 |
|
343 |
|
344 |
|
345 /* find IP address, client host name, and HELO string in a Received: |
|
346 * header of forms: |
|
347 * #1 Received: from helo (hostname [addr] ... |
|
348 * Received: from helo ([addr] ... |
|
349 * #2 Received: from hostname [addr] ... |
|
350 * Received: from [addr] ... |
|
351 * #3 Received: from qmailheloandhostname (addr) ... |
|
352 * #4 Received: from qmailhostname (HELO qmailhelo) (addr) ... |
|
353 * or Received: from qmailhostname (HELO qmailhelo) ([addr]) ... |
|
354 * |
|
355 * ignore these forms: |
|
356 * #5 Received: from localhost by hostname with LMTP |
|
357 * #6 Received (qmail 4824 invoked by uid 1000); 8 Nov 2005 12:13:33 -0000 |
|
358 * #7 Received: (qmail 21530 invoked from network); 29 Aug 2005 16:05:04 -0000 |
|
359 * #8 Received: (from user@localhost) by lochost (8.12.10/8.12.10/Submit) ... |
|
360 * #9 Received: by hostname (Postfix) id ... |
|
361 * |
|
362 * This should be called only with Received: headers that are known to |
|
363 * have been added by trustworthy code such as the local system |
|
364 * or an MX secondary. |
|
365 * Return 0 for unknown header, "" if IP address found, or stupid type string |
|
366 */ |
|
367 const char * |
|
368 parse_received(const char *hdr, /* the null terminated header */ |
|
369 DCC_GOT_CKS *cks, /* put address checksum here */ |
|
370 char *helo, /* optionally put HELO value here */ |
|
371 int helo_len, |
|
372 char *clnt_str, int clnt_str_len, |
|
373 char *clnt_name, int clnt_name_len) |
|
374 { |
|
375 char addr_buf[INET6_ADDRSTRLEN+2]; |
|
376 const char *h, *n; |
|
377 int h_len, n_len, a_len; |
|
378 int i; |
|
379 |
|
380 /* make the field name optional */ |
|
381 if (!CLITCMP(hdr, "Received:")) |
|
382 hdr += LITZ("Received"); |
|
383 hdr += strspn(hdr, " \t\r\n"); |
|
384 |
|
385 /* #define DCC_DEBUG_PARSE_RECEIVED */ |
|
386 #ifdef DCC_DEBUG_PARSE_RECEIVED |
|
387 printf("\n\nReceived: %s\n", hdr); |
|
388 #endif |
|
389 #define SPAN_ADDR(l,p) (*(p) >= '0' && *(p) <= '9' \ |
|
390 && ((l) = strspn((p), "0123456789.")) >= 7 \ |
|
391 && (l) < INET_ADDRSTRLEN) |
|
392 |
|
393 if (CLITCMP(hdr, "from")) { |
|
394 /* It does not match "Received: from" in #1, #2, #3, and #5 |
|
395 * Recognize #6 and #7 */ |
|
396 if (!LITCMP(hdr, "(qmail ")) { |
|
397 hdr += LITZ("(qmail "); |
|
398 i = strspn(hdr, "0123456789"); |
|
399 if (i == 0) |
|
400 return 0; |
|
401 hdr += i; |
|
402 if (!LITCMP(hdr, " invoked from network)") |
|
403 || !LITCMP(hdr, " invoked by uid ")) |
|
404 return "qmail"; |
|
405 return 0; |
|
406 } |
|
407 /* recognize #8 */ |
|
408 if (!LITCMP(hdr, "(from ")) { |
|
409 hdr += LITZ("(from "); |
|
410 hdr = strpbrk(hdr, DCC_WHITESPACE"@"); |
|
411 if (!hdr || *hdr != '@') |
|
412 return 0; |
|
413 hdr = strpbrk(hdr, DCC_WHITESPACE")"); |
|
414 if (!hdr || *hdr != ')') |
|
415 return 0; |
|
416 hdr += 1+strspn(hdr+1, DCC_WHITESPACE); |
|
417 if (LITCMP(hdr, "by ")) |
|
418 return 0; |
|
419 hdr += LITZ("by "); |
|
420 if (strstr(hdr, "/Submit")) |
|
421 return "sendmail Submit"; |
|
422 return 0; |
|
423 } |
|
424 /* recognize #9 */ |
|
425 if (!LITCMP(hdr, "by ")) { |
|
426 hdr += LITZ("by "); |
|
427 hdr = strpbrk(hdr, DCC_WHITESPACE); |
|
428 if (!hdr) |
|
429 return 0; |
|
430 ++hdr; |
|
431 if (!LITCMP(hdr, "(Postfix)")) |
|
432 return "postfix"; |
|
433 return 0; |
|
434 } |
|
435 /* unrecognized */ |
|
436 return 0; |
|
437 } |
|
438 |
|
439 hdr += LITZ("from"); |
|
440 i = strspn(hdr, DCC_WHITESPACE); |
|
441 if (i == 0) |
|
442 return 0; |
|
443 hdr += i; |
|
444 |
|
445 /* We have "Received: from " |
|
446 * get the host name or HELO value before '(' or '[' in |
|
447 * #1, #2, #3, and #5 */ |
|
448 h = hdr; |
|
449 hdr = strpbrk(hdr, DCC_WHITESPACE"(["); |
|
450 if (!hdr) |
|
451 return 0; /* unrecognized */ |
|
452 h_len = hdr - h; |
|
453 hdr += strspn(hdr, DCC_WHITESPACE); |
|
454 |
|
455 if (*hdr == '(') { |
|
456 /* look for client host name of #1 |
|
457 * or IPv4 address of #3 |
|
458 * or HELO value and IPv4 address of #4 */ |
|
459 ++hdr; |
|
460 |
|
461 if (SPAN_ADDR(a_len, hdr) |
|
462 && hdr[a_len] == ')') { |
|
463 /* we seem to have the IPv4 address of #3 */ |
|
464 n = h; |
|
465 n_len = h_len; |
|
466 addr_buf[0] = '['; |
|
467 memcpy(addr_buf+1, hdr, a_len); |
|
468 addr_buf[a_len+1] = '\0'; |
|
469 |
|
470 } else if (!LITCMP(hdr, "HELO ") |
|
471 && hdr[LITZ("HELO ")] != '[') { |
|
472 /* we have the #4 qmail HELO form when reverse DNS name |
|
473 * and helo value differ or unrecognizable */ |
|
474 n = h; |
|
475 n_len = h_len; |
|
476 h = hdr + LITZ("HELO "); |
|
477 hdr = strpbrk(h, " \t'\"()[]"); |
|
478 if (!hdr) |
|
479 return 0; |
|
480 h_len = hdr - h; |
|
481 if (!h_len |
|
482 || LITCMP(hdr, ") (")) |
|
483 return 0; |
|
484 hdr += LITZ(") ("); |
|
485 if (SPAN_ADDR(a_len, hdr) |
|
486 && hdr[a_len] == ')') { |
|
487 addr_buf[0] = '['; |
|
488 memcpy(addr_buf+1, hdr, a_len); |
|
489 addr_buf[a_len+1] = '\0'; |
|
490 } else if (hdr[0] == '[' |
|
491 && SPAN_ADDR(a_len, hdr+1) |
|
492 && hdr[1+a_len] == ']' |
|
493 && hdr[2+a_len] == ')') { |
|
494 memcpy(addr_buf, hdr, a_len+1); |
|
495 addr_buf[a_len+1] = '\0'; |
|
496 } else { |
|
497 return 0; |
|
498 } |
|
499 |
|
500 } else { |
|
501 /* it is #1 or unrecognizable */ |
|
502 n = hdr; |
|
503 hdr = strpbrk(hdr, DCC_WHITESPACE"["); |
|
504 if (!hdr) |
|
505 return 0; |
|
506 n_len = hdr - n; |
|
507 hdr += strspn(hdr, DCC_WHITESPACE); |
|
508 if (*hdr != '[') |
|
509 return 0; |
|
510 a_len = get_received_addr(addr_buf, hdr); |
|
511 if (!a_len) |
|
512 return 0; |
|
513 } |
|
514 |
|
515 } else if (*hdr == '[') { |
|
516 /* format #2; we have possibly null client name and no HELO */ |
|
517 n = h; |
|
518 n_len = h_len; |
|
519 h_len = 0; |
|
520 a_len = get_received_addr(addr_buf, hdr); |
|
521 if (!a_len) |
|
522 return 0; |
|
523 |
|
524 } else if (!CLITCMP(hdr, "by ")) { |
|
525 /* recognize #5 */ |
|
526 hdr += LITZ("by "); |
|
527 n = strchr(hdr, ' '); |
|
528 if (!n || n > hdr+DCC_MAXDOMAINLEN) |
|
529 return 0; |
|
530 if (!CLITCMP(n, " with LMTP")) |
|
531 return "LMTP"; /* stupid type string */ |
|
532 return 0; |
|
533 |
|
534 } else { |
|
535 return 0; |
|
536 } |
|
537 |
|
538 /* it looks ok so send out all the answers |
|
539 * if the IP address makes sense */ |
|
540 if (!dcc_get_str_ip_ck(cks, addr_buf+1)) |
|
541 return 0; |
|
542 |
|
543 dcc_ipv6tostr(clnt_str, clnt_str_len, &cks->ip_addr); |
|
544 |
|
545 if (clnt_name && clnt_name_len) { |
|
546 if (n_len == 0) { |
|
547 /* use address as the client host name */ |
|
548 addr_buf[a_len] = ']'; |
|
549 n_len = a_len+1; |
|
550 n = addr_buf; |
|
551 } |
|
552 if (clnt_name_len > n_len+1) |
|
553 clnt_name_len = n_len+1; |
|
554 STRLCPY(clnt_name, n, clnt_name_len); |
|
555 } |
|
556 |
|
557 if (helo && helo_len) { |
|
558 if (helo_len > h_len+1) |
|
559 helo_len = h_len+1; |
|
560 STRLCPY(helo, h, helo_len); |
|
561 } |
|
562 |
|
563 #ifdef DCC_DEBUG_PARSE_RECEIVED |
|
564 printf("helo=%s clnt_str=%s clnt_name=%s\n", |
|
565 helo, clnt_str, clnt_name); |
|
566 #endif |
|
567 |
|
568 return ""; |
|
569 |
|
570 #undef SPAN_ADDR |
|
571 } |
|
572 |
|
573 |
|
574 |
|
575 u_char /* 1=found env_From value */ |
|
576 parse_return_path(const char *hdr, char *buf, int buf_len) |
|
577 { |
|
578 int i; |
|
579 |
|
580 if (CLITCMP(hdr, "Return-Path:")) |
|
581 return 0; |
|
582 |
|
583 hdr += LITZ("Return-Path:"); |
|
584 hdr += strspn(hdr, " \t"); |
|
585 i = strlen(hdr); |
|
586 while (i > 0 && (hdr[i-1] == '\r' || hdr[i-1] == '\n')) |
|
587 --i; |
|
588 if (i >= 2 && *hdr == '<' && hdr[i-1] == '>') { |
|
589 ++hdr; |
|
590 i -= 2; |
|
591 } |
|
592 if (i >= buf_len-1) |
|
593 i = buf_len-1; |
|
594 if (i <= 0) |
|
595 return 0; |
|
596 memcpy(buf, hdr, i); |
|
597 buf[i] = '\0'; |
|
598 |
|
599 return 1; |
|
600 } |
|
601 |
|
602 |
|
603 |
|
604 u_char /* 1=found env_From value */ |
|
605 parse_unix_from(const char *hdr, char *buf, int buf_len) |
|
606 { |
|
607 const char *p; |
|
608 int i; |
|
609 |
|
610 if (strncmp(hdr, "From ", LITZ("From "))) |
|
611 return 0; |
|
612 |
|
613 hdr += LITZ("From "); |
|
614 hdr += strspn(hdr, " "); |
|
615 p = strchr(hdr, ' '); |
|
616 if (p == 0) |
|
617 return 0; |
|
618 |
|
619 i = p-hdr; |
|
620 if (i >= buf_len) |
|
621 i = buf_len-1; |
|
622 if (i <= 0) |
|
623 return 0; |
|
624 memcpy(buf, hdr, i); |
|
625 buf[i] = '\0'; |
|
626 return 1; |
|
627 } |
|
628 |
|
629 |
|
630 |
|
631 u_char |
|
632 parse_mail_host(const char *env_from, char *buf, int buf_len) |
|
633 { |
|
634 const char *p, *p2, *p3; |
|
635 int i; |
|
636 |
|
637 p = strchr(env_from, '@'); |
|
638 if (!p) |
|
639 return 0; |
|
640 p2 = strchr(++p, '>'); |
|
641 if (!p2) |
|
642 p2 = p+strlen(p); |
|
643 |
|
644 /* do not try to figure out source routes */ |
|
645 p3 = strpbrk(p, ";@,"); |
|
646 if (p3 && p3 < p2) |
|
647 return 0; |
|
648 |
|
649 i = p2-p; |
|
650 if (i >= buf_len) |
|
651 i = buf_len-1; |
|
652 if (i <= 0) |
|
653 return 0; |
|
654 memcpy(buf, p, i); |
|
655 buf[i] = '\0'; |
|
656 return 1; |
|
657 } |
|
658 |
|
659 |
|
660 |
|
661 void |
|
662 dcc_print_cks(LOG_WRITE_FNC out, void *arg, |
|
663 u_char is_spam, DCC_TGTS local_tgts, |
|
664 const DCC_GOT_CKS *cks, DCC_CKS_WTGTS wtgts, |
|
665 u_char have_wlist) /* 1=have whiteclnt result */ |
|
666 { |
|
667 char tgts_buf[16], type_buf[26], cbuf[DCC_CK2STR_LEN]; |
|
668 # define LINE_LEN 81 |
|
669 char buf[LINE_LEN*6]; |
|
670 const DCC_GOT_SUM *g; |
|
671 u_char have_server, have_thold, headed; |
|
672 DCC_TGTS tgts; |
|
673 int cklen, buflen, inx, i; |
|
674 |
|
675 /* decide which column headings are needed */ |
|
676 have_server = 0; |
|
677 have_thold = 0; |
|
678 for (g = cks->sums, inx = 0; g <= LAST(cks->sums); ++g, ++inx) { |
|
679 if (g->type == DCC_CK_INVALID) |
|
680 continue; |
|
681 if (g->tgts != DCC_TGTS_INVALID) |
|
682 have_server = 1; |
|
683 if (wtgts[inx] != 0) |
|
684 have_wlist = 1; |
|
685 if (cks->tholds_rej[g->type] != DCC_THOLD_UNSET |
|
686 && g->type != DCC_CK_REP_TOTAL) |
|
687 have_thold = 1; |
|
688 } |
|
689 if (have_wlist) |
|
690 have_thold = 0; |
|
691 |
|
692 headed = 0; |
|
693 buflen = 0; |
|
694 for (g = cks->sums, inx = 0; g <= LAST(cks->sums); ++g, ++inx) { |
|
695 if (g->type == DCC_CK_INVALID) |
|
696 continue; |
|
697 |
|
698 if (!headed) { |
|
699 headed = 1; |
|
700 dcc_tgts2str(tgts_buf, sizeof(tgts_buf)-LITZ("spam"), |
|
701 local_tgts, 0); |
|
702 if (is_spam) |
|
703 STRLCAT(tgts_buf, " spam", sizeof(tgts_buf)); |
|
704 buflen += snprintf(buf+buflen, sizeof(buf)-buflen, |
|
705 " " |
|
706 DCC_XHDR_REPORTED" %-15s checksum", |
|
707 tgts_buf); |
|
708 |
|
709 if (have_server || have_wlist || have_thold) |
|
710 buflen += snprintf(buf+buflen, |
|
711 sizeof(buf)-buflen, |
|
712 PRINT_CK_PAT_SRVR, |
|
713 have_server ? "server" : ""); |
|
714 if (have_wlist) |
|
715 buflen += snprintf(buf+buflen, |
|
716 sizeof(buf)-buflen, |
|
717 PRINT_CK_PAT_WLIST, |
|
718 "wlist"); |
|
719 else if (have_thold) |
|
720 buflen += snprintf(buf+buflen, |
|
721 sizeof(buf)-buflen, |
|
722 PRINT_CK_PAT_THOLD, |
|
723 "thold"); |
|
724 if (ISZ(buf)-buflen > 1) |
|
725 buf[buflen++] = '\n'; |
|
726 |
|
727 } else if (buflen >= ISZ(buf)-LINE_LEN) { |
|
728 out(arg, buf, buflen); |
|
729 buflen = 0; |
|
730 } |
|
731 |
|
732 cklen = snprintf(buf+buflen, sizeof(buf)-buflen, |
|
733 PRINT_CK_PAT_CK, |
|
734 dcc_type2str(type_buf, sizeof(type_buf), |
|
735 g->type, g->hdr_nm, 0, 0), |
|
736 dcc_ck2str(cbuf, sizeof(cbuf), |
|
737 g->type, g->sum, 0)); |
|
738 buflen += cklen; |
|
739 |
|
740 if (g->rpt2srvr != 0 && g->tgts != DCC_TGTS_INVALID) { |
|
741 if (buflen < ISZ(buf)) |
|
742 buflen += snprintf(buf+buflen, |
|
743 sizeof(buf)-buflen, |
|
744 PRINT_CK_PAT_SRVR, |
|
745 dcc_tgts2str(tgts_buf, |
|
746 sizeof(tgts_buf), |
|
747 g->tgts, 0)); |
|
748 } else if (wtgts[inx] != 0 |
|
749 || (have_thold |
|
750 && cks->tholds_rej[g->type]!=DCC_THOLD_UNSET)) { |
|
751 /* steal space from blank server count for |
|
752 * long substitute checksums */ |
|
753 i = (PRINT_CK_PAT_SRVR_LEN - (cklen - (PRINT_CK_TYPE_LEN |
|
754 +2 +PRINT_CK_SUM_LEN))); |
|
755 if (i > PRINT_CK_PAT_SRVR_LEN) |
|
756 i = PRINT_CK_PAT_SRVR_LEN; |
|
757 if (i > ISZ(buf)-buflen) |
|
758 i = ISZ(buf)-buflen; |
|
759 if (i < 0) |
|
760 i = 0; |
|
761 if (i > ISZ(buf)) |
|
762 i = ISZ(buf); |
|
763 while (--i >= 0) { |
|
764 buf[buflen++] = ' '; |
|
765 } |
|
766 } |
|
767 |
|
768 if (buflen >= ISZ(buf)) { |
|
769 ; |
|
770 } else if (wtgts[inx] != 0) { |
|
771 buflen += snprintf(buf+buflen, sizeof(buf)-buflen, |
|
772 PRINT_CK_PAT_WLIST, |
|
773 wtgts[inx] == 0 |
|
774 ? "" |
|
775 : dcc_tgts2str(tgts_buf, |
|
776 sizeof(tgts_buf), |
|
777 wtgts[inx], 0)); |
|
778 } else if (have_thold |
|
779 && (tgts = cks->tholds_rej[g->type] |
|
780 ) != DCC_THOLD_UNSET) { |
|
781 buflen += snprintf(buf+buflen, sizeof(buf)-buflen, |
|
782 PRINT_CK_PAT_THOLD, |
|
783 dcc_thold2str(tgts_buf, |
|
784 sizeof(tgts_buf), |
|
785 g->type, tgts)); |
|
786 } |
|
787 |
|
788 if (buflen >= ISZ(buf)-1) |
|
789 buflen = sizeof(buf)-2; |
|
790 buf[buflen] = '\n'; |
|
791 buf[++buflen] = '\0'; |
|
792 } |
|
793 |
|
794 if (buflen != 0) |
|
795 out(arg, buf, buflen); |
|
796 |
|
797 #undef LINE_LEN |
|
798 } |