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

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

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


Revision 1.3 - (show annotations) (download) (as text)
Tue Dec 11 13:27:44 2012 UTC (6 years, 3 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 /*
2 * match.c
3 * wildcard matching functions
4 * hostmask matching
5 * cidr matching
6 *
7 * $Id: match.c,v 1.2 2012/12/10 22:49:45 thommey Exp $
8 *
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 if (count < 1)
344 return 1;
345 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 if (count > 32)
357 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 if (count < 1)
363 return 1;
364 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