/[cvs]/eggdrop1.8/src/match.c
ViewVC logotype

Annotation of /eggdrop1.8/src/match.c

Parent Directory Parent Directory | Revision Log Revision Log | View Revision Graph Revision Graph


Revision 1.3 - (hide annotations) (download) (as text)
Tue Dec 11 13:27:44 2012 UTC (6 years, 5 months ago) by thommey
Branch: MAIN
CVS Tags: HEAD
Changes since 1.2: +5 -5 lines
File MIME type: text/x-chdr
Make sure match_cidr returns NOMATCH if address families mismatch.

1 simple 1.1 /*
2     * match.c
3     * wildcard matching functions
4     * hostmask matching
5     * cidr matching
6     *
7 thommey 1.3 * $Id: match.c,v 1.2 2012/12/10 22:49:45 thommey Exp $
8 simple 1.1 *
9     * Once this code was working, I added support for % so that I could
10     * use the same code both in Eggdrop and in my IrcII client.
11     * Pleased with this, I added the option of a fourth wildcard, ~,
12     * which matches varying amounts of whitespace (at LEAST one space,
13     * though, for sanity reasons).
14     *
15     * This code would not have been possible without the prior work and
16     * suggestions of various sourced. Special thanks to Robey for
17     * all his time/help tracking down bugs and his ever-helpful advice.
18     *
19     * 04/09: Fixed the "*\*" against "*a" bug (caused an endless loop)
20     *
21     * Chris Fuller (aka Fred1@IRC & Fwitz@IRC)
22     * crf@cfox.bchs.uh.edu
23     *
24     * I hereby release this code into the public domain
25     *
26     */
27     #include "main.h"
28    
29     #define QUOTE '\\' /* quoting character (overrides wildcards) */
30     #define WILDS '*' /* matches 0 or more characters (including spaces) */
31     #define WILDP '%' /* matches 0 or more non-space characters */
32     #define WILDQ '?' /* matches ecactly one character */
33     #define WILDT '~' /* matches 1 or more spaces */
34    
35     #define NOMATCH 0
36     #define MATCH (match+sofar)
37     #define PERMATCH (match+saved+sofar)
38    
39     int cidr_support = 0;
40    
41     int casecharcmp(unsigned char a, unsigned char b)
42     {
43     return (rfc_toupper(a) - rfc_toupper(b));
44     }
45    
46     int charcmp(unsigned char a, unsigned char b)
47     {
48     return (a - b);
49     }
50    
51     /* Wildcard-matches mask m to n. Uses the comparison function cmp1. If chgpoint
52     * isn't NULL, it points at the first character in n where we start using cmp2.
53     */
54     int _wild_match_per(register unsigned char *m, register unsigned char *n,
55     int (*cmp1)(unsigned char, unsigned char),
56     int (*cmp2)(unsigned char, unsigned char),
57     unsigned char *chgpoint)
58     {
59     unsigned char *ma = m, *lsm = 0, *lsn = 0, *lpm = 0, *lpn = 0;
60     int match = 1, saved = 0, space;
61     register unsigned int sofar = 0;
62    
63     /* null strings should never match */
64     if ((m == 0) || (n == 0) || (!*n) || (!cmp1))
65     return NOMATCH;
66    
67     if (!cmp2) /* Don't change cmpfunc if it's not valid */
68     chgpoint = NULL;
69    
70     while (*n) {
71     if (*m == WILDT) { /* Match >=1 space */
72     space = 0; /* Don't need any spaces */
73     do {
74     m++;
75     space++;
76     } /* Tally 1 more space ... */
77     while ((*m == WILDT) || (*m == ' ')); /* for each space or ~ */
78     sofar += space; /* Each counts as exact */
79     while (*n == ' ') {
80     n++;
81     space--;
82     } /* Do we have enough? */
83     if (space <= 0)
84     continue; /* Had enough spaces! */
85     }
86     /* Do the fallback */
87     else {
88     switch (*m) {
89     case 0:
90     do
91     m--; /* Search backwards */
92     while ((m > ma) && (*m == '?')); /* For first non-? char */
93     if ((m > ma) ? ((*m == '*') && (m[-1] != QUOTE)) : (*m == '*'))
94     return PERMATCH; /* nonquoted * = match */
95     break;
96     case WILDP:
97     while (*(++m) == WILDP); /* Zap redundant %s */
98     if (*m != WILDS) { /* Don't both if next=* */
99     if (*n != ' ') { /* WILDS can't match ' ' */
100     lpm = m;
101     lpn = n; /* Save '%' fallback spot */
102     saved += sofar;
103     sofar = 0; /* And save tally count */
104     }
105     continue; /* Done with '%' */
106     }
107     /* FALL THROUGH */
108     case WILDS:
109     do
110     m++; /* Zap redundant wilds */
111     while ((*m == WILDS) || (*m == WILDP));
112     lsm = m;
113     lsn = n;
114     lpm = 0; /* Save '*' fallback spot */
115     match += (saved + sofar); /* Save tally count */
116     saved = sofar = 0;
117     continue; /* Done with '*' */
118     case WILDQ:
119     m++;
120     n++;
121     continue; /* Match one char */
122     case QUOTE:
123     m++; /* Handle quoting */
124     }
125     if (((!chgpoint || n < chgpoint) && !(*cmp1)(*m, *n)) ||
126     (chgpoint && n >= chgpoint && !(*cmp2)(*m, *n))) { /* If matching */
127     m++;
128     n++;
129     sofar++;
130     continue; /* Tally the match */
131     }
132     #ifdef WILDT
133     }
134     #endif
135     if (lpm) { /* Try to fallback on '%' */
136     n = ++lpn;
137     m = lpm;
138     sofar = 0; /* Restore position */
139     if ((*n | 32) == 32)
140     lpm = 0; /* Can't match 0 or ' ' */
141     continue; /* Next char, please */
142     }
143     if (lsm) { /* Try to fallback on '*' */
144     n = ++lsn;
145     m = lsm; /* Restore position */
146     saved = sofar = 0;
147     continue; /* Next char, please */
148     }
149     return NOMATCH; /* No fallbacks=No match */
150     }
151     while ((*m == WILDS) || (*m == WILDP))
152     m++; /* Zap leftover %s & *s */
153     return (*m) ? NOMATCH : PERMATCH; /* End of both = match */
154     }
155    
156     /* Generic string matching, use addr_match() for hostmasks! */
157     int _wild_match(register unsigned char *m, register unsigned char *n)
158     {
159     unsigned char *ma = m, *na = n, *lsm = 0, *lsn = 0;
160     int match = 1;
161     register int sofar = 0;
162    
163     /* null strings should never match */
164     if ((ma == 0) || (na == 0) || (!*ma) || (!*na))
165     return NOMATCH;
166     /* find the end of each string */
167     while (*(++m));
168     m--;
169     while (*(++n));
170     n--;
171    
172     while (n >= na) {
173     /* If the mask runs out of chars before the string, fall back on
174     * a wildcard or fail. */
175     if (m < ma) {
176     if (lsm) {
177     n = --lsn;
178     m = lsm;
179     if (n < na)
180     lsm = 0;
181     sofar = 0;
182     }
183     else
184     return NOMATCH;
185     }
186    
187     switch (*m) {
188     case WILDS: /* Matches anything */
189     do
190     m--; /* Zap redundant wilds */
191     while ((m >= ma) && (*m == WILDS));
192     lsm = m;
193     lsn = n;
194     match += sofar;
195     sofar = 0; /* Update fallback pos */
196     if (m < ma)
197     return MATCH;
198     continue; /* Next char, please */
199     case WILDQ:
200     m--;
201     n--;
202     continue; /* '?' always matches */
203     }
204     if (toupper(*m) == toupper(*n)) { /* If matching char */
205     m--;
206     n--;
207     sofar++; /* Tally the match */
208     continue; /* Next char, please */
209     }
210     if (lsm) { /* To to fallback on '*' */
211     n = --lsn;
212     m = lsm;
213     if (n < na)
214     lsm = 0; /* Rewind to saved pos */
215     sofar = 0;
216     continue; /* Next char, please */
217     }
218     return NOMATCH; /* No fallback=No match */
219     }
220     while ((m >= ma) && (*m == WILDS))
221     m--; /* Zap leftover %s & *s */
222     return (m >= ma) ? NOMATCH : MATCH; /* Start of both = match */
223     }
224    
225     /* cidr and RFC1459 compatible host matching
226     * Returns: 1 if the address in n matches the hostmask in m.
227     * If cmp != 0, m and n will be compared as masks. Returns 1
228     * if m is broader, 0 otherwise.
229     * If user != 0, the masks are eggdrop user hosts and should
230     * be matched regardless of the cidr_support variable.
231     * This is required as userhost matching shouldn't depend on
232     * server support of cidr.
233     */
234     int addr_match(char *m, char *n, int user, int cmp)
235     {
236     char *p, *q, *r = 0, *s = 0;
237     char mu[UHOSTLEN], nu[UHOSTLEN];
238    
239     /* copy the strings into our own buffers
240     and convert to rfc uppercase */
241     for (p = mu; *m && (p - mu < UHOSTLEN - 1); m++) {
242     if (*m == '@')
243     r = p;
244     *p++ = rfc_toupper(*m);
245     }
246     for (q = nu; *n && (q - nu < UHOSTLEN - 1); n++) {
247     if (*n == '@')
248     s = q;
249     *q++ = rfc_toupper(*n);
250     }
251     *p = *q = 0;
252     if ((!user && !cidr_support) || !r || !s)
253     return wild_match(mu, nu) ? 1 : NOMATCH;
254    
255     *r++ = *s++ = 0;
256     if (!wild_match(mu, nu))
257     return NOMATCH; /* nick!ident parts don't match */
258     if (!*r && !*s)
259     return 1; /* end of nonempty strings */
260    
261     /* check for CIDR notation and perform
262     generic string matching if not found */
263     if (!(p = strrchr(r, '/')) || !str_isdigit(p + 1))
264     return wild_match(r, s) ? 1 : NOMATCH;
265     /* if the two strings are both cidr masks,
266     use the broader prefix */
267     if (cmp && (q = strrchr(s, '/')) && str_isdigit(q + 1)) {
268     if (atoi(p + 1) > atoi(q + 1))
269     return NOMATCH;
270     *q = 0;
271     }
272     *p = 0;
273     /* looks like a cidr mask */
274     return cidr_match(r, s, atoi(p + 1));
275     }
276    
277     /* Checks for overlapping masks
278     * Returns: > 0 if the two masks in m and n overlap, 0 otherwise.
279     */
280     int mask_match(char *m, char *n)
281     {
282     int prefix;
283     char *p, *q, *r = 0, *s = 0;
284     char mu[UHOSTLEN], nu[UHOSTLEN];
285    
286     for (p = mu; *m && (p - mu < UHOSTLEN - 1); m++) {
287     if (*m == '@')
288     r = p;
289     *p++ = rfc_toupper(*m);
290     }
291     for (q = nu; *n && (q - nu < UHOSTLEN - 1); n++) {
292     if (*n == '@')
293     s = q;
294     *q++ = rfc_toupper(*n);
295     }
296     *p = *q = 0;
297     if (!cidr_support || !r || !s)
298     return (wild_match(mu, nu) || wild_match(nu, mu));
299    
300     *r++ = *s++ = 0;
301     if (!wild_match(mu, nu) && !wild_match(nu, mu))
302     return 0;
303    
304     if (!*r && !*s)
305     return 1;
306     p = strrchr(r, '/');
307     q = strrchr(s, '/');
308     if ((!p || !str_isdigit(p + 1)) && (!q || !str_isdigit(q + 1)))
309     return (wild_match(r, s) || wild_match(s, r));
310    
311     if (p) {
312     *p = 0;
313     prefix = atoi(p + 1);
314     } else
315     prefix = (strchr(r, ':') ? 128 : 32);
316     if (q) {
317     *q = 0;
318     if (atoi(q + 1) < prefix)
319     prefix = atoi(q + 1);
320     }
321     return cidr_match(r, s, prefix);
322     }
323    
324     /* Performs bitwise comparison of two IP addresses stored in presentation
325     * (string) format. IPs are first internally converted to binary form.
326     * Returns: 1 if the first count bits are equal, 0 otherwise.
327     */
328     int cidr_match(char *m, char *n, int count)
329     {
330     #ifdef IPV6
331     int c, af = AF_INET;
332     u_8bit_t block[16], addr[16];
333    
334     if (strchr(m, ':') || strchr(n, ':')) {
335     af = AF_INET6;
336     if (count > 128)
337     return NOMATCH;
338     } else if (count > 32)
339     return NOMATCH;
340     if (inet_pton(af, m, &block) != 1 ||
341     inet_pton(af, n, &addr) != 1)
342     return NOMATCH;
343 thommey 1.3 if (count < 1)
344     return 1;
345 simple 1.1 for (c = 0; c < (count / 8); c++)
346     if (block[c] != addr[c])
347     return NOMATCH;
348     if (!(count % 8))
349     return 1;
350     count = 8 - (count % 8);
351     return ((block[c] >> count) == (addr[c] >> count));
352    
353     #else
354     IP block, addr;
355    
356 thommey 1.2 if (count > 32)
357 simple 1.1 return NOMATCH;
358     block = ntohl(inet_addr(m));
359     addr = ntohl(inet_addr(n));
360     if (block == INADDR_NONE || addr == INADDR_NONE)
361     return NOMATCH;
362 thommey 1.3 if (count < 1)
363     return 1;
364 simple 1.1 count = 32 - count;
365     return ((block >> count) == (addr >> count));
366     #endif
367     }
368    
369     /* Inline for cron_match (obviously).
370     * Matches a single field of a crontab expression.
371     */
372     inline int cron_matchfld(char *mask, int match)
373     {
374     int skip = 0, f, t;
375     char *p, *q;
376    
377     for (p = mask; mask && *mask; mask = p) {
378     /* loop through a list of values, if such is given */
379     if ((p = strchr(mask, ',')))
380     *p++ = 0;
381     /* check for the step operator */
382     if ((q = strchr(mask, '/'))) {
383     if (q == mask)
384     continue;
385     *q++ = 0;
386     skip = atoi(q);
387     }
388     if (!strcmp(mask, "*") && (!skip || !(match % skip)))
389     return 1;
390     /* ranges, e.g 10-20 */
391     if (strchr(mask, '-')) {
392     if (sscanf(mask, "%d-%d", &f, &t) != 2)
393     continue;
394     if (t < f) {
395     if (match <= t)
396     match += 60;
397     t += 60;
398     }
399     if ((match >= f && match <= t) &&
400     (!skip || !((match - f) % skip)))
401     return 1;
402     }
403     /* no operator found, should be exact match */
404     f = strtol(mask, &q, 10);
405     if ((q > mask) &&
406     (skip ? !((match - f) % skip) : (match == f)))
407     return 1;
408     }
409     return 0;
410     }
411    
412     /* Check if the current time matches a crontab-like specification.
413     *
414     * mask contains a cron-style series of time fields. The following
415     * crontab operators are supported: ranges '-', asterisks '*',
416     * lists ',' and steps '/'.
417     * match must have 5 space separated integers representing in order
418     * the current minute, hour, day of month, month and weekday.
419     * It should look like this: "53 17 01 03 06", which means
420     * Sunday 01 March, 17:53.
421     */
422     int cron_match(const char *mask, const char *match)
423     {
424     int d = 0, i, m = 1, t[5];
425     char *p, *q, *buf;
426    
427     if (!mask[0])
428     return 0;
429     if (sscanf(match, "%d %d %d %d %d",
430     &t[0], &t[1], &t[2], &t[3], &t[4]) < 5)
431     return 0;
432     buf = nmalloc(strlen(mask) + 1);
433     strcpy(buf, mask);
434     for (p = buf, i = 0; *p && i < 5; i++) {
435     q = newsplit(&p);
436     if (!strcmp(q, "*"))
437     continue;
438     m = (cron_matchfld(q, t[i]) ||
439     (i == 4 && !t[i] && cron_matchfld(q, 7)));
440     if (i == 2)
441     d = m;
442     else if (!m || (i == 3 && d))
443     break;
444     }
445     nfree(buf);
446     return m;
447     }

webmaster@eggheads.org
ViewVC Help
Powered by ViewVC 1.1.23