1 /* This provides a generic hash function for use w/INN. Currently
2 is implemented using MD5, but should probably have a mechanism for
3 choosing the hash algorithm and tagging the hash with the algorithm
12 static HASH empty= { { 0, 0, 0, 0, 0, 0, 0, 0,
13 0, 0, 0, 0, 0, 0, 0, 0 }};
15 /* cipoint - where in this message-ID does it become case-insensitive?
17 * The RFC822 code is not quite complete. Absolute, total, full RFC822
18 * compliance requires a horrible parsing job, because of the arcane
19 * quoting conventions -- abc"def"ghi is not equivalent to abc"DEF"ghi,
20 * for example. There are three or four things that might occur in the
21 * domain part of a message-id that are case-sensitive. They don't seem
22 * to ever occur in real news, thank Cthulhu. (What? You were expecting
23 * a merciful and forgiving deity to be invoked in connection with RFC822?
24 * Forget it; none of them would come near it.)
26 * Returns: pointer into s, or NULL for "nowhere"
29 cipoint(const char *s, size_t size)
32 static const char post[] = "postmaster";
33 static int plen = sizeof(post) - 1;
35 if ((p = memchr(s, '@', size))== NULL) /* no local/domain split */
36 return NULL; /* assume all local */
37 if ((p - (s + 1) == plen) && !strncasecmp(post, s+1, plen)) {
38 /* crazy -- "postmaster" is case-insensitive */
45 Hash(const void *value, const size_t len)
47 struct md5_context context;
51 md5_update(&context, value, len);
55 (sizeof(hash) < sizeof(context.digest)) ? sizeof(hash) : sizeof(context.digest));
60 HashMessageID(const char *MessageID)
63 const char *cip, *p = NULL;
68 len = strlen(MessageID);
69 cip = cipoint(MessageID, len);
71 for (p = cip + 1; *p != '\0'; p++) {
72 if (!CTYPE(islower, *p)) {
73 new = xstrdup(MessageID);
79 for (q = new + (p - MessageID); *q != '\0'; q++)
81 hash = Hash(new ? new : MessageID, len);
88 ** Check if the hash is all zeros, and subseqently empty, see HashClear
89 ** for more info on this.
92 HashEmpty(const HASH h)
94 return (memcmp(&empty, &h, sizeof(HASH)) == 0);
98 ** Set the hash to all zeros. Using all zeros as the value for empty
99 ** introduces the possibility of colliding w/a value that actually hashes
100 ** to all zeros, but that's fairly unlikely.
103 HashClear(HASH *hash)
105 memset(hash, '\0', sizeof(HASH));
109 ** Convert the binary form of the hash to a form that we can use in error
110 ** messages and logs.
113 HashToText(const HASH hash)
115 static const char hex[] = "0123456789ABCDEF";
118 static char hashstr[(sizeof(HASH)*2) + 1];
120 for (p = hash.hash, i = 0; i < sizeof(HASH); i++, p++) {
121 hashstr[i * 2] = hex[(*p & 0xF0) >> 4];
122 hashstr[(i * 2) + 1] = hex[*p & 0x0F];
124 hashstr[(sizeof(HASH) * 2)] = '\0';
129 ** Converts a hex digit and converts it to a int
132 int hextodec(const int c)
134 return isdigit(c) ? (c - '0') : ((c - 'A') + 10);
138 ** Convert the ASCII representation of the hash back to the canonical form
141 TextToHash(const char *text)
147 for (q = (char *)&hash, i = 0; i != sizeof(HASH); i++) {
148 q[i] = (hextodec(text[i * 2]) << 4) + hextodec(text[(i * 2) + 1]);
153 /* This is rather gross, we compare like the last 4 bytes of the
154 hash are at the beginning because dbz considers them to be the
155 most significant bytes */
156 int HashCompare(const HASH *h1, const HASH *h2) {
158 int tocomp = sizeof(HASH) - sizeof(unsigned long);
160 if ((i = memcmp(&h1->hash[tocomp], &h1->hash[tocomp], sizeof(unsigned long))))
162 return memcmp(h1, h2, sizeof(HASH));