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 }