File: hash.c

package info (click to toggle)
inn2 2.4.5-5
  • links: PTS
  • area: main
  • in suites: lenny
  • size: 8,912 kB
  • ctags: 7,860
  • sloc: ansic: 85,104; perl: 11,427; sh: 9,863; makefile: 2,498; yacc: 1,563; python: 298; lex: 252; tcl: 7
file content (163 lines) | stat: -rw-r--r-- 4,397 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
/* This provides a generic hash function for use w/INN.  Currently
   is implemented using MD5, but should probably have a mechanism for
   choosing the hash algorithm and tagging the hash with the algorithm
   used */
#include "config.h"
#include "clibrary.h"
#include <ctype.h>

#include "inn/md5.h"
#include "libinn.h"

static HASH empty= { { 0, 0, 0, 0, 0, 0, 0, 0,
		       0, 0, 0, 0, 0, 0, 0, 0 }};

/* cipoint - where in this message-ID does it become case-insensitive?
 *
 * The RFC822 code is not quite complete.  Absolute, total, full RFC822
 * compliance requires a horrible parsing job, because of the arcane
 * quoting conventions -- abc"def"ghi is not equivalent to abc"DEF"ghi,
 * for example.  There are three or four things that might occur in the
 * domain part of a message-id that are case-sensitive.  They don't seem
 * to ever occur in real news, thank Cthulhu.  (What?  You were expecting
 * a merciful and forgiving deity to be invoked in connection with RFC822?
 * Forget it; none of them would come near it.)
 *
 * Returns: pointer into s, or NULL for "nowhere"
 */
static const char *
cipoint(const char *s, size_t size)
{
    char *p;
    static const char post[] = "postmaster";
    static int plen = sizeof(post) - 1;

    if ((p = memchr(s, '@', size))== NULL)	/* no local/domain split */
	return NULL;				/* assume all local */
    if ((p - (s + 1) == plen) && !strncasecmp(post, s+1, plen)) {
	/* crazy -- "postmaster" is case-insensitive */
	return s;
    }
    return p;
}

HASH
Hash(const void *value, const size_t len)
{
    struct md5_context context;
    HASH hash;

    md5_init(&context);
    md5_update(&context, value, len);
    md5_final(&context);
    memcpy(&hash,
	   &context.digest,
	   (sizeof(hash) < sizeof(context.digest)) ? sizeof(hash) : sizeof(context.digest));
    return hash;
}

HASH
HashMessageID(const char *MessageID)
{
    char                *new = NULL;
    const char          *cip, *p = NULL;
    char                *q;
    int                 len;
    HASH                hash;

    len = strlen(MessageID);
    cip = cipoint(MessageID, len);
    if (cip != NULL) {
        for (p = cip + 1; *p != '\0'; p++) {
            if (!CTYPE(islower, *p)) {
                new = xstrdup(MessageID);
                break;
            }
        }
    }
    if (new != NULL)
        for (q = new + (p - MessageID); *q != '\0'; q++)
            *q = tolower(*q);
    hash = Hash(new ? new : MessageID, len);
    if (new != NULL)
	free(new);
    return hash;
}

/*
**  Check if the hash is all zeros, and subseqently empty, see HashClear
**  for more info on this.
*/
bool
HashEmpty(const HASH h)
{
    return (memcmp(&empty, &h, sizeof(HASH)) == 0);
}

/*
**  Set the hash to all zeros.  Using all zeros as the value for empty 
**  introduces the possibility of colliding w/a value that actually hashes
**  to all zeros, but that's fairly unlikely.
*/
void
HashClear(HASH *hash)
{
    memset(hash, '\0', sizeof(HASH));
}

/*
**  Convert the binary form of the hash to a form that we can use in error
**  messages and logs.
*/
char *
HashToText(const HASH hash)
{
    static const char	hex[] = "0123456789ABCDEF";
    const char		*p;
    unsigned int	i;
    static char		hashstr[(sizeof(HASH)*2) + 1];

    for (p = hash.hash, i = 0; i < sizeof(HASH); i++, p++) {
	hashstr[i * 2] = hex[(*p & 0xF0) >> 4];
	hashstr[(i * 2) + 1] = hex[*p & 0x0F];
    }
    hashstr[(sizeof(HASH) * 2)] = '\0';
    return hashstr;
}

/*
** Converts a hex digit and converts it to a int
*/
static
int hextodec(const int c)
{
    return isdigit(c) ? (c - '0') : ((c - 'A') + 10);
}

/*
**  Convert the ASCII representation of the hash back to the canonical form
*/
HASH
TextToHash(const char *text)
{
    char                *q;
    int                 i;
    HASH                hash;

    for (q = (char *)&hash, i = 0; i != sizeof(HASH); i++) {
	q[i] = (hextodec(text[i * 2]) << 4) + hextodec(text[(i * 2) + 1]);
    }
    return hash;
}

/* This is rather gross, we compare like the last 4 bytes of the
   hash are at the beginning because dbz considers them to be the
   most significant bytes */
int HashCompare(const HASH *h1, const HASH *h2) {
    int i;
    int tocomp = sizeof(HASH) - sizeof(unsigned long);
    
    if ((i = memcmp(&h1->hash[tocomp], &h1->hash[tocomp], sizeof(unsigned long))))
	return i;
    return memcmp(h1, h2, sizeof(HASH));
}