Mercurial > notdcc
comparison dcclib/ck.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 * 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 } |