/* Distributed Checksum Clearinghouse * * compute simple body checksum * * --C-LICENSE-- * $Revision: 1.62 $ */ #include "dcc_ck.h" static void ck_body0(GOT_CKS *cks, const char *bp, u_int bp_len) { # define BUF_LEN 1024 char buf[BUF_LEN+5]; u_char flen; int blen; char c; if (cks->sums[DCC_CK_BODY].type != DCC_CK_BODY) return; flen = cks->ctx_body.flen; blen = 0; for (;;) { if (bp_len == 0) { if (blen != 0) { cks->ctx_body.total += blen; MD5Update(&cks->ctx_body.md5, (void *)buf, blen); } cks->ctx_body.flen = flen; return; } --bp_len; c = *bp++; /* Ignore the '>' in the sequence "\n>From" because * it is sometimes added for old UNIX MUAs. * As a side effect, ignore '\n' */ if (flen != 0) { if (c == "\n>From"[flen]) { if (++flen >= 6) { memcpy(&buf[blen], "From", 4); if ((blen += 4) >= BUF_LEN) { cks->ctx_body.total += blen; MD5Update(&cks->ctx_body.md5, (void *)buf, blen); blen = 0; } flen = 0; } continue; } if (--flen != 0) { memcpy(&buf[blen], ">From", flen); if ((blen += flen) >= BUF_LEN) { cks->ctx_body.total += blen; MD5Update(&cks->ctx_body.md5, (void *)buf, blen); blen = 0; } flen = 0; } } if (c == '\n') { flen = 1; continue; } /* Ignore whitespace to avoid being confused by * varying line endings added and removed by * various MUAs and MTAs. * As a side effect, ignore entirely blank messages. */ if (c == ' ' || c == '\t' || c == '\r') continue; /* Ignore '=' to minimize but not entirely avoid being * confused by some some sequences that look like * quoted-printable triples but that are not. */ if (c == '=') continue; buf[blen] = c; if (++blen >= BUF_LEN) { cks->ctx_body.total += blen; MD5Update(&cks->ctx_body.md5, (void *)buf, blen); blen = 0; } } } static void ck_body0_fin(GOT_CKS *cks) { /* always generate the MD5 checksum so that grey listing has it */ MD5Final(cks->sums[DCC_CK_BODY].sum.b, &cks->ctx_body.md5); if (cks->sums[DCC_CK_BODY].type != DCC_CK_BODY) return; if (cks->ctx_body.total < 30) { cks->sums[DCC_CK_BODY].type = DCC_CK_INVALID; return; } cks->sums[DCC_CK_BODY].rpt2srvr = 1; } static void decoders_init(GOT_CKS *cks) { cks->mime_bnd_matches = 0; cks->flags |= CKS_MIME_BOL; cks->mime_ct = CK_CT_TEXT; cks->mime_cset = c8859_1; cks->mime_ce = DCC_CK_CE_ASCII; cks->qp.state = CK_QP_IDLE; cks->b64.quantum_cnt = 0; } /* start all of the checksums */ void cks_init(GOT_CKS *cks) { GOT_SUM *g; for (g = cks->sums; g <= LAST(cks->sums); ++g) { CLR_GOT_SUM(g); } cks->flags = 0; cks->mime_nest = 0; cks->mhdr_st = CK_MHDR_ST_IDLE; cks->mp_st = CK_MP_ST_TEXT; decoders_init(cks); cks->url.doms = 0; cks->url.dom_start = 0; cks->sums[DCC_CK_BODY].type = DCC_CK_BODY; cks->ctx_body.total = 0; cks->ctx_body.flen = 1; MD5Init(&cks->ctx_body.md5); dcc_ck_fuz1_init(cks); dcc_ck_fuz2_init(cks); } /* decode quoted-printable and base64 and then compute the body checksums */ static void decode_sum(GOT_CKS *cks, const char *bp, u_int bp_len) { char tbuf[1024]; const char *tbufp; int len; /* Decode quoted-printable and base64 and make fuzzy sumes * only while in the body of a MIME entity. * Changing from the text, image, html, etc. requires a '\n' * to flush the URL and other decoders in the checksummers. * None of the checksums count whitespace. */ if (cks->mp_st != CK_MP_ST_TEXT) { if (bp_len == 0) return; #ifdef DCC_DEBUG_CKSUM if (dcc_clnt_debug == 4) write(1, bp, bp_len); #endif ck_body0(cks, bp, bp_len); dcc_ck_fuz1(cks, "\n", 1); dcc_ck_fuz2(cks, "\n", 1); return; } while (bp_len != 0) { switch (cks->mime_ce) { case DCC_CK_CE_ASCII: default: len = bp_len; tbufp = bp; bp_len = 0; break; case DCC_CK_CE_QP: tbufp = tbuf; len = ck_qp_decode(cks, &bp, &bp_len, tbuf, sizeof(tbuf)); break; case DCC_CK_CE_B64: tbufp = tbuf; len = ck_b64_decode(cks, &bp, &bp_len, tbuf, sizeof(tbuf)); break; } if (len != 0) { #ifdef DCC_DEBUG_CKSUM if (dcc_clnt_debug == 4) write(1, tbufp, len); #endif ck_body0(cks, tbufp, len); dcc_ck_fuz1(cks, tbufp, len); if (cks->mime_ct != CK_CT_BINARY) dcc_ck_fuz2(cks, tbufp, len); } } } /* compute all of the body checksums on a chunk of raw text */ void ck_body(GOT_CKS *cks, const void *bp, u_int bp_len) { CK_BND *bndp; const char *sum; /* 1st input byte not swallowed */ const char *cmp; /* 1st not parsed for MIME */ const char *cp; char c; int len, matched_len, i, j; sum = bp; cmp = sum; while (bp_len != 0) { /* if we have no multipart hassles * then pass buffer to qp/base64 decoder and quit */ if (cks->mime_nest == 0) { decode_sum(cks, sum, bp_len); return; } /* look for start of next line to start matching boundaries */ if (cks->mime_bnd_matches == 0) { cp = memchr(cmp, '\n', bp_len); if (!cp) { cp = cmp+bp_len; } else { ++cp; } /* look for a MIME entity header in the text before * the next line and possible start of a boundary */ i = cp - cmp; if (cks->mp_st == CK_MP_ST_HDRS) { if (parse_mime_hdr(cks, cmp, i, 0)) { /* blank header line ends the headers */ j = cp-sum; if (j) { decode_sum(cks, sum, j); sum = cp; } cks->mp_st = CK_MP_ST_TEXT; } } /* We found the end of a line. Reset positions to * start looking for a MIME boundary after it */ if (*(cp-1) == '\n') { cks->flags |= CKS_MIME_BOL; cks->mime_bnd_matches = cks->mime_nest; for (bndp = cks->mime_bnd; bndp <= LAST(cks->mime_bnd); ++bndp) { bndp->cmp_len = 0; } } cmp = cp; if ((bp_len -= i) == 0) break; } /* look for (rest of) one of the active MIME boundaries */ matched_len = 0; for (bndp = cks->mime_bnd; bndp < &cks->mime_bnd[cks->mime_nest]; ++bndp) { if (bndp->cmp_len == CK_BND_MISS) continue; /* already mismatched boundary */ j = bndp->bnd_len - bndp->cmp_len; len = bp_len; if (j > len) j = len; cp = cmp; if (j > 0) { if (memcmp(cp, &bndp->bnd[bndp->cmp_len], j)) { bndp->cmp_len = CK_BND_MISS; --cks->mime_bnd_matches; continue; } /* this boundary matches so far */ bndp->cmp_len += j; cp += j; if ((len -= j) <= 0) { matched_len = bp_len; continue; } /* since we did not exhaust len, we know * we matched the entire boundary */ j = 0; } /* look for 1st '-' of trailing "--" */ if (j == 0 && *cp == '-') { ++bndp->cmp_len; if (--len <= 0) { matched_len = bp_len; continue; } ++cp; j = -1; } /* look for 2nd '-' of trailing "--" */ if (j == -1) { if (*cp == '-') { ++bndp->cmp_len; if (--len <= 0) { matched_len = bp_len; continue; } ++cp; } else { bndp->cmp_len = CK_BND_MISS; --cks->mime_bnd_matches; continue; } } /* check for trailing whitespace & '\n' */ if ((c = *cp) == ' ' || c == '\t' || c == '\r') { do { ++cp; } while (--len > 0 && ((c = *cp) == ' ' || c == '\t' || c == '\r')); if (len <= 0) { matched_len = bp_len; continue; } } if (*cp != '\n') { /* mismatch after the end of the boundary */ bndp->cmp_len = CK_BND_MISS; --cks->mime_bnd_matches; continue; } /* We have found a MIME boundary. * Flush b64 & qp decoders and fuzzy checksummers */ j = cmp-sum; if (j) decode_sum(cks, sum, j); /* pass the boundary in the buffer */ matched_len = ++cp - cmp; cmp = sum = cp; /* Body checksum the boundary */ cks->mp_st = CK_MP_ST_BND; decode_sum(cks, bndp->bnd, bndp->bnd_len); if (bndp->cmp_len != bndp->bnd_len) { /* checksum trailing "--" of final boundary */ decode_sum(cks, "--", 2); /* end the current & inner entities */ cks->mp_st = CK_MP_ST_EPILOGUE; } else { /* intermediate boundaries end inner entities */ cks->mp_st = CK_MP_ST_HDRS; ++bndp; } cks->mime_nest = bndp - cks->mime_bnd; decoders_init(cks); break; } bp_len -= matched_len; } j = cmp-sum; if (j) decode_sum(cks, sum, j); } /* finish all of the body checksums */ void cks_fin(GOT_CKS *cks) { dcc_ck_fuz1(cks, "\n", 1); /* flush URL decoders & line buffers */ dcc_ck_fuz2(cks, "\n", 1); ck_body0_fin(cks); dcc_ck_fuz1_fin(cks); dcc_ck_fuz2_fin(cks); }