]> jfr.im git - solanum.git/blob - ircd/match.c
Centralise banmask matching logic
[solanum.git] / ircd / match.c
1 /************************************************************************
2 * IRC - Internet Relay Chat, src/match.c
3 * Copyright (C) 1990 Jarkko Oikarinen
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 1, or (at your option)
8 * any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 *
19 */
20 #include "stdinc.h"
21 #include "defaults.h"
22 #include "client.h"
23 #include "ircd.h"
24 #include "match.h"
25 #include "s_assert.h"
26
27 /*
28 * Compare if a given string (name) matches the given
29 * mask (which can contain wild cards: '*' - match any
30 * number of chars, '?' - match any single character.
31 *
32 * return 1, if match
33 * 0, if no match
34 *
35 * Originally by Douglas A Lewis (dalewis@acsu.buffalo.edu)
36 * Rewritten by Timothy Vogelsang (netski), net@astrolink.org
37 */
38
39 /** Check a string against a mask.
40 * This test checks using traditional IRC wildcards only: '*' means
41 * match zero or more characters of any type; '?' means match exactly
42 * one character of any type.
43 *
44 * @param[in] mask Wildcard-containing mask.
45 * @param[in] name String to check against \a mask.
46 * @return Zero if \a mask matches \a name, non-zero if no match.
47 */
48 int match(const char *mask, const char *name)
49 {
50 const char *m = mask, *n = name;
51 const char *m_tmp = mask, *n_tmp = name;
52 int star_p;
53
54 s_assert(mask != NULL);
55 s_assert(name != NULL);
56
57 for (;;)
58 {
59 switch (*m)
60 {
61 case '\0':
62 if (!*n)
63 return 1;
64 backtrack:
65 if (m_tmp == mask)
66 return 0;
67 m = m_tmp;
68 n = ++n_tmp;
69 break;
70 case '*':
71 case '?':
72 for (star_p = 0;; m++)
73 {
74 if (*m == '*')
75 star_p = 1;
76 else if (*m == '?')
77 {
78 if (!*n++)
79 goto backtrack;
80 }
81 else
82 break;
83 }
84 if (star_p)
85 {
86 if (!*m)
87 return 1;
88 else
89 {
90 m_tmp = m;
91 for (n_tmp = n; *n && irctolower(*n) != irctolower(*m); n++);
92 }
93 }
94 /* and fall through */
95 default:
96 if (!*n)
97 return (*m != '\0' ? 0 : 1);
98 if (irctolower(*m) != irctolower(*n))
99 goto backtrack;
100 m++;
101 n++;
102 break;
103 }
104 }
105 }
106
107 /** Check a mask against a mask.
108 * This test checks using traditional IRC wildcards only: '*' means
109 * match zero or more characters of any type; '?' means match exactly
110 * one character of any type.
111 * The difference between mask_match() and match() is that in mask_match()
112 * a '?' in mask does not match a '*' in name.
113 *
114 * @param[in] mask Existing wildcard-containing mask.
115 * @param[in] name New wildcard-containing mask.
116 * @return 1 if \a name is equal to or more specific than \a mask, 0 otherwise.
117 */
118 int mask_match(const char *mask, const char *name)
119 {
120 const char *m = mask, *n = name;
121 const char *m_tmp = mask, *n_tmp = name;
122 int star_p;
123
124 s_assert(mask != NULL);
125 s_assert(name != NULL);
126
127 for (;;)
128 {
129 switch (*m)
130 {
131 case '\0':
132 if (!*n)
133 return 1;
134 backtrack:
135 if (m_tmp == mask)
136 return 0;
137 m = m_tmp;
138 n = ++n_tmp;
139 break;
140 case '*':
141 case '?':
142 for (star_p = 0;; m++)
143 {
144 if (*m == '*')
145 star_p = 1;
146 else if (*m == '?')
147 {
148 /* changed for mask_match() */
149 if (*n == '*' || !*n)
150 goto backtrack;
151 n++;
152 }
153 else
154 break;
155 }
156 if (star_p)
157 {
158 if (!*m)
159 return 1;
160 else
161 {
162 m_tmp = m;
163 for (n_tmp = n; *n && irctolower(*n) != irctolower(*m); n++);
164 }
165 }
166 /* and fall through */
167 default:
168 if (!*n)
169 return (*m != '\0' ? 0 : 1);
170 if (irctolower(*m) != irctolower(*n))
171 goto backtrack;
172 m++;
173 n++;
174 break;
175 }
176 }
177 }
178
179
180 #define MATCH_MAX_CALLS 512 /* ACK! This dies when it's less that this
181 and we have long lines to parse */
182 /** Check a string against a mask.
183 * This test checks using extended wildcards: '*' means match zero
184 * or more characters of any type; '?' means match exactly one
185 * character of any type; '#' means match exactly one character that
186 * is a number; '@' means match exactly one character that is a
187 * letter; '\s' means match a space.
188 *
189 * This function supports escaping, so that a wildcard may be matched
190 * exactly.
191 *
192 * @param[in] mask Wildcard-containing mask.
193 * @param[in] name String to check against \a mask.
194 * @return Zero if \a mask matches \a name, non-zero if no match.
195 */
196 int
197 match_esc(const char *mask, const char *name)
198 {
199 const unsigned char *m = (const unsigned char *)mask;
200 const unsigned char *n = (const unsigned char *)name;
201 const unsigned char *ma = (const unsigned char *)mask;
202 const unsigned char *na = (const unsigned char *)name;
203 int wild = 0;
204 int calls = 0;
205 int quote = 0;
206 int match1 = 0;
207
208 s_assert(mask != NULL);
209 s_assert(name != NULL);
210
211 if(!mask || !name)
212 return 0;
213
214 /* if the mask is "*", it matches everything */
215 if((*m == '*') && (*(m + 1) == '\0'))
216 return 1;
217
218 while(calls++ < MATCH_MAX_CALLS)
219 {
220 if(quote)
221 quote++;
222 if(quote == 3)
223 quote = 0;
224 if(*m == '\\' && !quote)
225 {
226 m++;
227 quote = 1;
228 continue;
229 }
230 if(!quote && *m == '*')
231 {
232 /*
233 * XXX - shouldn't need to spin here, the mask should have been
234 * collapsed before match is called
235 */
236 while(*m == '*')
237 m++;
238
239 wild = 1;
240 ma = m;
241 na = n;
242
243 if(*m == '\\')
244 {
245 m++;
246 /* This means it is an invalid mask -A1kmm. */
247 if(!*m)
248 return 0;
249 quote++;
250 continue;
251 }
252 }
253
254 if(!*m)
255 {
256 if(!*n)
257 return 1;
258 if(quote)
259 return 0;
260 for(m--; (m > (const unsigned char *)mask) && (*m == '?'); m--)
261 ;
262
263 if(*m == '*' && (m > (const unsigned char *)mask))
264 return 1;
265 if(!wild)
266 return 0;
267 m = ma;
268 n = ++na;
269 }
270 else if(!*n)
271 {
272 /*
273 * XXX - shouldn't need to spin here, the mask should have been
274 * collapsed before match is called
275 */
276 if(quote)
277 return 0;
278 while(*m == '*')
279 m++;
280 return (*m == 0);
281 }
282
283 if(quote)
284 match1 = *m == 's' ? *n == ' ' : irctolower(*m) == irctolower(*n);
285 else if(*m == '?')
286 match1 = 1;
287 else if(*m == '@')
288 match1 = IsLetter(*n);
289 else if(*m == '#')
290 match1 = IsDigit(*n);
291 else
292 match1 = irctolower(*m) == irctolower(*n);
293 if(match1)
294 {
295 if(*m)
296 m++;
297 if(*n)
298 n++;
299 }
300 else
301 {
302 if(!wild)
303 return 0;
304 m = ma;
305 n = ++na;
306 }
307 }
308 return 0;
309 }
310
311 int comp_with_mask(void *addr, void *dest, unsigned int mask)
312 {
313 if (memcmp(addr, dest, mask / 8) == 0)
314 {
315 int n = mask / 8;
316 unsigned char m = (0xFF << (8 - (mask % 8)));
317 if (mask % 8 == 0 || (((unsigned char *) addr)[n] & m) == (((unsigned char *) dest)[n] & m))
318 {
319 return (1);
320 }
321 }
322 return (0);
323 }
324
325 int comp_with_mask_sock(struct sockaddr *addr, struct sockaddr *dest, unsigned int mask)
326 {
327 void *iaddr = NULL;
328 void *idest = NULL;
329
330 if (addr->sa_family == AF_INET)
331 {
332 iaddr = &((struct sockaddr_in *)(void *)addr)->sin_addr;
333 idest = &((struct sockaddr_in *)(void *)dest)->sin_addr;
334 }
335 else
336 {
337 iaddr = &((struct sockaddr_in6 *)(void *)addr)->sin6_addr;
338 idest = &((struct sockaddr_in6 *)(void *)dest)->sin6_addr;
339
340 }
341
342 return (comp_with_mask(iaddr, idest, mask));
343 }
344
345 /*
346 * match_ips()
347 *
348 * Input - cidr ip mask, address
349 */
350 int match_ips(const char *s1, const char *s2)
351 {
352 struct rb_sockaddr_storage ipaddr, maskaddr;
353 char mask[BUFSIZE];
354 char address[HOSTLEN + 1];
355 char *len;
356 void *ipptr, *maskptr;
357 int cidrlen, aftype;
358
359 rb_strlcpy(mask, s1, sizeof(mask));
360 rb_strlcpy(address, s2, sizeof(address));
361
362 len = strrchr(mask, '/');
363 if (len == NULL)
364 return 0;
365
366 *len++ = '\0';
367
368 cidrlen = atoi(len);
369 if (cidrlen <= 0)
370 return 0;
371
372 if (strchr(mask, ':') && strchr(address, ':'))
373 {
374 if (cidrlen > 128)
375 return 0;
376
377 aftype = AF_INET6;
378 ipptr = &((struct sockaddr_in6 *)&ipaddr)->sin6_addr;
379 maskptr = &((struct sockaddr_in6 *)&maskaddr)->sin6_addr;
380 }
381 else if (!strchr(mask, ':') && !strchr(address, ':'))
382 {
383 if (cidrlen > 32)
384 return 0;
385
386 aftype = AF_INET;
387 ipptr = &((struct sockaddr_in *)&ipaddr)->sin_addr;
388 maskptr = &((struct sockaddr_in *)&maskaddr)->sin_addr;
389 }
390 else
391 return 0;
392
393 if (rb_inet_pton(aftype, address, ipptr) <= 0)
394 return 0;
395 if (rb_inet_pton(aftype, mask, maskptr) <= 0)
396 return 0;
397 if (comp_with_mask(ipptr, maskptr, cidrlen))
398 return 1;
399 else
400 return 0;
401 }
402
403 /* match_cidr()
404 *
405 * Input - mask, address
406 * Ouput - 1 = Matched 0 = Did not match
407 */
408
409 int match_cidr(const char *s1, const char *s2)
410 {
411 struct rb_sockaddr_storage ipaddr, maskaddr;
412 char mask[BUFSIZE];
413 char address[NICKLEN + USERLEN + HOSTLEN + 6];
414 char *ipmask;
415 char *ip;
416 char *len;
417 void *ipptr, *maskptr;
418 int cidrlen, aftype;
419
420 rb_strlcpy(mask, s1, sizeof(mask));
421 rb_strlcpy(address, s2, sizeof(address));
422
423 ipmask = strrchr(mask, '@');
424 if (ipmask == NULL)
425 return 0;
426
427 *ipmask++ = '\0';
428
429 ip = strrchr(address, '@');
430 if (ip == NULL)
431 return 0;
432 *ip++ = '\0';
433
434
435 len = strrchr(ipmask, '/');
436 if (len == NULL)
437 return 0;
438
439 *len++ = '\0';
440
441 cidrlen = atoi(len);
442 if (cidrlen <= 0)
443 return 0;
444
445 if (strchr(ip, ':') && strchr(ipmask, ':'))
446 {
447 if (cidrlen > 128)
448 return 0;
449
450 aftype = AF_INET6;
451 ipptr = &((struct sockaddr_in6 *)&ipaddr)->sin6_addr;
452 maskptr = &((struct sockaddr_in6 *)&maskaddr)->sin6_addr;
453 }
454 else if (!strchr(ip, ':') && !strchr(ipmask, ':'))
455 {
456 if (cidrlen > 32)
457 return 0;
458
459 aftype = AF_INET;
460 ipptr = &((struct sockaddr_in *)&ipaddr)->sin_addr;
461 maskptr = &((struct sockaddr_in *)&maskaddr)->sin_addr;
462 }
463 else
464 return 0;
465
466 if (rb_inet_pton(aftype, ip, ipptr) <= 0)
467 return 0;
468 if (rb_inet_pton(aftype, ipmask, maskptr) <= 0)
469 return 0;
470 if (comp_with_mask(ipptr, maskptr, cidrlen) && match(mask, address))
471 return 1;
472 else
473 return 0;
474 }
475
476 /* collapse()
477 *
478 * collapses a string containing multiple *'s.
479 */
480 char *collapse(char *pattern)
481 {
482 char *p = pattern, *po = pattern;
483 char c;
484 int f = 0;
485
486 if (p == NULL)
487 return NULL;
488
489 while ((c = *p++))
490 {
491 if (c == '*')
492 {
493 if (!(f & 1))
494 *po++ = '*';
495 f |= 1;
496 }
497 else
498 {
499 *po++ = c;
500 f &= ~1;
501 }
502 }
503 *po++ = 0;
504
505 return pattern;
506 }
507
508 /* collapse_esc()
509 *
510 * The collapse() function with support for escaping characters
511 */
512 char *collapse_esc(char *pattern)
513 {
514 char *p = pattern, *po = pattern;
515 char c;
516 int f = 0;
517
518 if (p == NULL)
519 return NULL;
520
521 while ((c = *p++))
522 {
523 if (!(f & 2) && c == '*')
524 {
525 if (!(f & 1))
526 *po++ = '*';
527 f |= 1;
528 }
529 else if (!(f & 2) && c == '\\')
530 {
531 *po++ = '\\';
532 f |= 2;
533 }
534 else
535 {
536 *po++ = c;
537 f &= ~3;
538 }
539 }
540 *po++ = 0;
541 return pattern;
542 }
543
544 /*
545 * irccmp - case insensitive comparison of two 0 terminated strings.
546 *
547 * returns 0, if s1 equal to s2
548 * <0, if s1 lexicographically less than s2
549 * >0, if s1 lexicographically greater than s2
550 */
551 int irccmp(const char *s1, const char *s2)
552 {
553 const unsigned char *str1 = (const unsigned char *)s1;
554 const unsigned char *str2 = (const unsigned char *)s2;
555 int res;
556
557 s_assert(s1 != NULL);
558 s_assert(s2 != NULL);
559
560 while ((res = irctoupper(*str1) - irctoupper(*str2)) == 0)
561 {
562 if (*str1 == '\0')
563 return 0;
564 str1++;
565 str2++;
566 }
567 return (res);
568 }
569
570 int ircncmp(const char *s1, const char *s2, int n)
571 {
572 const unsigned char *str1 = (const unsigned char *)s1;
573 const unsigned char *str2 = (const unsigned char *)s2;
574 int res;
575 s_assert(s1 != NULL);
576 s_assert(s2 != NULL);
577
578 while ((res = irctoupper(*str1) - irctoupper(*str2)) == 0)
579 {
580 str1++;
581 str2++;
582 n--;
583 if (n == 0 || (*str1 == '\0' && *str2 == '\0'))
584 return 0;
585 }
586 return (res);
587 }
588
589 void matchset_for_client(struct Client *who, struct matchset *m)
590 {
591 unsigned hostn = 0;
592 unsigned ipn = 0;
593
594 struct sockaddr_in ip4;
595
596 sprintf(m->host[hostn++], "%s!%s@%s", who->name, who->username, who->host);
597 sprintf(m->ip[ipn++], "%s!%s@%s", who->name, who->username, who->sockhost);
598 if (who->localClient->mangledhost != NULL)
599 {
600 /* if host mangling mode enabled, also check their real host */
601 if (!strcmp(who->host, who->localClient->mangledhost))
602 {
603 sprintf(m->host[hostn++], "%s!%s@%s", who->name, who->username, who->orighost);
604 }
605 /* if host mangling mode not enabled and no other spoof,
606 * also check the mangled form of their host */
607 else if (!IsDynSpoof(who))
608 {
609 sprintf(m->host[hostn++], "%s!%s@%s", who->name, who->username, who->localClient->mangledhost);
610 }
611 }
612 if (GET_SS_FAMILY(&who->localClient->ip) == AF_INET6 &&
613 rb_ipv4_from_ipv6((const struct sockaddr_in6 *)&who->localClient->ip, &ip4))
614 {
615 int n = sprintf(m->ip[ipn++], "%s!%s@", who->name, who->username);
616 rb_inet_ntop_sock((struct sockaddr *)&ip4,
617 m->ip[ipn] + n, sizeof m->ip[ipn] - n);
618 }
619
620 for (int i = hostn; i < ARRAY_SIZE(m->host); i++)
621 {
622 m->host[i][0] = '\0';
623 }
624 for (int i = ipn; i < ARRAY_SIZE(m->ip); i++)
625 {
626 m->ip[i][0] = '\0';
627 }
628 }
629
630 bool client_matches_mask(struct Client *who, const char *mask)
631 {
632 static struct matchset ms;
633 matchset_for_client(who, &ms);
634 return matches_mask(&ms, mask);
635 }
636
637 bool matches_mask(const struct matchset *m, const char *mask)
638 {
639 for (int i = 0; i < ARRAY_SIZE(m->host); i++)
640 {
641 if (m->host[i][0] == '\0')
642 break;
643 if (match(mask, m->host[i]))
644 return true;
645 }
646 for (int i = 0; i < ARRAY_SIZE(m->ip); i++)
647 {
648 if (m->ip[i][0] == '\0')
649 break;
650 if (match(mask, m->ip[i]))
651 return true;
652 if (match_cidr(mask, m->ip[i]))
653 return true;
654 }
655 return false;
656 }
657
658 const unsigned char irctolower_tab[] = {
659 0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa,
660 0xb, 0xc, 0xd, 0xe, 0xf, 0x10, 0x11, 0x12, 0x13, 0x14,
661 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d,
662 0x1e, 0x1f,
663 ' ', '!', '"', '#', '$', '%', '&', 0x27, '(', ')',
664 '*', '+', ',', '-', '.', '/',
665 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
666 ':', ';', '<', '=', '>', '?',
667 '@', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i',
668 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's',
669 't', 'u', 'v', 'w', 'x', 'y', 'z', '{', '|', '}', '~',
670 '_',
671 '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i',
672 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's',
673 't', 'u', 'v', 'w', 'x', 'y', 'z', '{', '|', '}', '~',
674 0x7f,
675 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89,
676 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
677 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99,
678 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
679 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9,
680 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
681 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9,
682 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
683 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9,
684 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
685 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9,
686 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
687 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9,
688 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
689 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9,
690 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
691 };
692
693 const unsigned char irctoupper_tab[] = {
694 0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa,
695 0xb, 0xc, 0xd, 0xe, 0xf, 0x10, 0x11, 0x12, 0x13, 0x14,
696 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d,
697 0x1e, 0x1f,
698 ' ', '!', '"', '#', '$', '%', '&', 0x27, '(', ')',
699 '*', '+', ',', '-', '.', '/',
700 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
701 ':', ';', '<', '=', '>', '?',
702 '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I',
703 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S',
704 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[', '\\', ']', '^',
705 0x5f,
706 '`', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I',
707 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S',
708 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[', '\\', ']', '^',
709 0x7f,
710 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89,
711 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
712 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99,
713 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
714 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9,
715 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
716 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9,
717 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
718 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9,
719 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
720 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9,
721 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
722 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9,
723 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
724 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9,
725 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
726 };
727
728 /*
729 * CharAttrs table
730 *
731 * NOTE: RFC 1459 sez: anything but a ^G, comma, or space is allowed
732 * for channel names
733 */
734 unsigned int CharAttrs[] = {
735 /* 0 */ CNTRL_C,
736 /* 1 */ CNTRL_C | CHAN_C | NONEOS_C,
737 /* 2 */ CNTRL_C | CHAN_C | FCHAN_C | NONEOS_C,
738 /* 3 */ CNTRL_C | CHAN_C | FCHAN_C | NONEOS_C,
739 /* 4 */ CNTRL_C | CHAN_C | NONEOS_C,
740 /* 5 */ CNTRL_C | CHAN_C | NONEOS_C,
741 /* 6 */ CNTRL_C | CHAN_C | NONEOS_C,
742 /* 7 BEL */ CNTRL_C | NONEOS_C,
743 /* 8 \b */ CNTRL_C | CHAN_C | NONEOS_C,
744 /* 9 \t */ CNTRL_C | SPACE_C | CHAN_C | NONEOS_C,
745 /* 10 \n */ CNTRL_C | SPACE_C | CHAN_C | NONEOS_C | EOL_C,
746 /* 11 \v */ CNTRL_C | SPACE_C | CHAN_C | NONEOS_C,
747 /* 12 \f */ CNTRL_C | SPACE_C | CHAN_C | NONEOS_C,
748 /* 13 \r */ CNTRL_C | SPACE_C | CHAN_C | NONEOS_C | EOL_C,
749 /* 14 */ CNTRL_C | CHAN_C | NONEOS_C,
750 /* 15 */ CNTRL_C | CHAN_C | NONEOS_C,
751 /* 16 */ CNTRL_C | CHAN_C | NONEOS_C,
752 /* 17 */ CNTRL_C | CHAN_C | NONEOS_C,
753 /* 18 */ CNTRL_C | CHAN_C | NONEOS_C,
754 /* 19 */ CNTRL_C | CHAN_C | NONEOS_C,
755 /* 20 */ CNTRL_C | CHAN_C | NONEOS_C,
756 /* 21 */ CNTRL_C | CHAN_C | NONEOS_C,
757 /* 22 */ CNTRL_C | CHAN_C | FCHAN_C | NONEOS_C,
758 /* 23 */ CNTRL_C | CHAN_C | NONEOS_C,
759 /* 24 */ CNTRL_C | CHAN_C | NONEOS_C,
760 /* 25 */ CNTRL_C | CHAN_C | NONEOS_C,
761 /* 26 */ CNTRL_C | CHAN_C | NONEOS_C,
762 /* 27 */ CNTRL_C | CHAN_C | NONEOS_C,
763 /* 28 */ CNTRL_C | CHAN_C | NONEOS_C,
764 /* 29 */ CNTRL_C | CHAN_C | FCHAN_C | NONEOS_C,
765 /* 30 */ CNTRL_C | CHAN_C | NONEOS_C,
766 /* 31 */ CNTRL_C | CHAN_C | FCHAN_C | NONEOS_C,
767 /* SP */ PRINT_C | SPACE_C,
768 /* ! */ PRINT_C | KWILD_C | CHAN_C | NONEOS_C,
769 /* " */ PRINT_C | CHAN_C | NONEOS_C,
770 /* # */ PRINT_C | MWILD_C | CHANPFX_C | CHAN_C | NONEOS_C,
771 /* $ */ PRINT_C | CHAN_C | NONEOS_C,
772 /* % */ PRINT_C | CHAN_C | NONEOS_C,
773 /* & */ PRINT_C | CHANPFX_C | CHAN_C | NONEOS_C,
774 /* ' */ PRINT_C | CHAN_C | NONEOS_C,
775 /* ( */ PRINT_C | CHAN_C | NONEOS_C,
776 /* ) */ PRINT_C | CHAN_C | NONEOS_C,
777 /* * */ PRINT_C | KWILD_C | MWILD_C | CHAN_C | NONEOS_C,
778 /* + */ PRINT_C | CHAN_C | NONEOS_C,
779 /* , */ PRINT_C | NONEOS_C,
780 /* - */ PRINT_C | NICK_C | CHAN_C | NONEOS_C | USER_C | HOST_C,
781 /* . */ PRINT_C | KWILD_C | CHAN_C | NONEOS_C | USER_C | HOST_C | SERV_C,
782 /* / */ PRINT_C | CHAN_C | NONEOS_C | HOST_C,
783 /* 0 */ PRINT_C | DIGIT_C | NICK_C | CHAN_C | NONEOS_C | USER_C | HOST_C,
784 /* 1 */ PRINT_C | DIGIT_C | NICK_C | CHAN_C | NONEOS_C | USER_C | HOST_C,
785 /* 2 */ PRINT_C | DIGIT_C | NICK_C | CHAN_C | NONEOS_C | USER_C | HOST_C,
786 /* 3 */ PRINT_C | DIGIT_C | NICK_C | CHAN_C | NONEOS_C | USER_C | HOST_C,
787 /* 4 */ PRINT_C | DIGIT_C | NICK_C | CHAN_C | NONEOS_C | USER_C | HOST_C,
788 /* 5 */ PRINT_C | DIGIT_C | NICK_C | CHAN_C | NONEOS_C | USER_C | HOST_C,
789 /* 6 */ PRINT_C | DIGIT_C | NICK_C | CHAN_C | NONEOS_C | USER_C | HOST_C,
790 /* 7 */ PRINT_C | DIGIT_C | NICK_C | CHAN_C | NONEOS_C | USER_C | HOST_C,
791 /* 8 */ PRINT_C | DIGIT_C | NICK_C | CHAN_C | NONEOS_C | USER_C | HOST_C,
792 /* 9 */ PRINT_C | DIGIT_C | NICK_C | CHAN_C | NONEOS_C | USER_C | HOST_C,
793 /* : */ PRINT_C | CHAN_C | NONEOS_C | HOST_C,
794 /* ; */ PRINT_C | CHAN_C | NONEOS_C,
795 /* < */ PRINT_C | CHAN_C | NONEOS_C,
796 /* = */ PRINT_C | CHAN_C | NONEOS_C,
797 /* > */ PRINT_C | CHAN_C | NONEOS_C,
798 /* ? */ PRINT_C | KWILD_C | MWILD_C | CHAN_C | NONEOS_C,
799 /* @ */ PRINT_C | KWILD_C | MWILD_C | CHAN_C | NONEOS_C,
800 /* A */ PRINT_C | ALPHA_C | LET_C | NICK_C | CHAN_C | NONEOS_C | USER_C | HOST_C,
801 /* B */ PRINT_C | ALPHA_C | LET_C | NICK_C | CHAN_C | NONEOS_C | USER_C | HOST_C,
802 /* C */ PRINT_C | ALPHA_C | LET_C | NICK_C | CHAN_C | NONEOS_C | USER_C | HOST_C,
803 /* D */ PRINT_C | ALPHA_C | LET_C | NICK_C | CHAN_C | NONEOS_C | USER_C | HOST_C,
804 /* E */ PRINT_C | ALPHA_C | LET_C | NICK_C | CHAN_C | NONEOS_C | USER_C | HOST_C,
805 /* F */ PRINT_C | ALPHA_C | LET_C | NICK_C | CHAN_C | NONEOS_C | USER_C | HOST_C,
806 /* G */ PRINT_C | ALPHA_C | LET_C | NICK_C | CHAN_C | NONEOS_C | USER_C | HOST_C,
807 /* H */ PRINT_C | ALPHA_C | LET_C | NICK_C | CHAN_C | NONEOS_C | USER_C | HOST_C,
808 /* I */ PRINT_C | ALPHA_C | LET_C | NICK_C | CHAN_C | NONEOS_C | USER_C | HOST_C,
809 /* J */ PRINT_C | ALPHA_C | LET_C | NICK_C | CHAN_C | NONEOS_C | USER_C | HOST_C,
810 /* K */ PRINT_C | ALPHA_C | LET_C | NICK_C | CHAN_C | NONEOS_C | USER_C | HOST_C,
811 /* L */ PRINT_C | ALPHA_C | LET_C | NICK_C | CHAN_C | NONEOS_C | USER_C | HOST_C,
812 /* M */ PRINT_C | ALPHA_C | LET_C | NICK_C | CHAN_C | NONEOS_C | USER_C | HOST_C,
813 /* N */ PRINT_C | ALPHA_C | LET_C | NICK_C | CHAN_C | NONEOS_C | USER_C | HOST_C,
814 /* O */ PRINT_C | ALPHA_C | LET_C | NICK_C | CHAN_C | NONEOS_C | USER_C | HOST_C,
815 /* P */ PRINT_C | ALPHA_C | LET_C | NICK_C | CHAN_C | NONEOS_C | USER_C | HOST_C,
816 /* Q */ PRINT_C | ALPHA_C | LET_C | NICK_C | CHAN_C | NONEOS_C | USER_C | HOST_C,
817 /* R */ PRINT_C | ALPHA_C | LET_C | NICK_C | CHAN_C | NONEOS_C | USER_C | HOST_C,
818 /* S */ PRINT_C | ALPHA_C | LET_C | NICK_C | CHAN_C | NONEOS_C | USER_C | HOST_C,
819 /* T */ PRINT_C | ALPHA_C | LET_C | NICK_C | CHAN_C | NONEOS_C | USER_C | HOST_C,
820 /* U */ PRINT_C | ALPHA_C | LET_C | NICK_C | CHAN_C | NONEOS_C | USER_C | HOST_C,
821 /* V */ PRINT_C | ALPHA_C | LET_C | NICK_C | CHAN_C | NONEOS_C | USER_C | HOST_C,
822 /* W */ PRINT_C | ALPHA_C | LET_C | NICK_C | CHAN_C | NONEOS_C | USER_C | HOST_C,
823 /* X */ PRINT_C | ALPHA_C | LET_C | NICK_C | CHAN_C | NONEOS_C | USER_C | HOST_C,
824 /* Y */ PRINT_C | ALPHA_C | LET_C | NICK_C | CHAN_C | NONEOS_C | USER_C | HOST_C,
825 /* Z */ PRINT_C | ALPHA_C | LET_C | NICK_C | CHAN_C | NONEOS_C | USER_C | HOST_C,
826 /* [ */ PRINT_C | ALPHA_C | NICK_C | CHAN_C | NONEOS_C | USER_C,
827 /* \ */ PRINT_C | ALPHA_C | NICK_C | CHAN_C | NONEOS_C | USER_C,
828 /* ] */ PRINT_C | ALPHA_C | NICK_C | CHAN_C | NONEOS_C | USER_C,
829 /* ^ */ PRINT_C | ALPHA_C | NICK_C | CHAN_C | NONEOS_C | USER_C,
830 /* _ */ PRINT_C | NICK_C | CHAN_C | NONEOS_C | USER_C,
831 /* ` */ PRINT_C | NICK_C | CHAN_C | NONEOS_C | USER_C,
832 /* a */ PRINT_C | ALPHA_C | LET_C | NICK_C | CHAN_C | NONEOS_C | USER_C | HOST_C,
833 /* b */ PRINT_C | ALPHA_C | LET_C | NICK_C | CHAN_C | NONEOS_C | USER_C | HOST_C,
834 /* c */ PRINT_C | ALPHA_C | LET_C | NICK_C | CHAN_C | NONEOS_C | USER_C | HOST_C,
835 /* d */ PRINT_C | ALPHA_C | LET_C | NICK_C | CHAN_C | NONEOS_C | USER_C | HOST_C,
836 /* e */ PRINT_C | ALPHA_C | LET_C | NICK_C | CHAN_C | NONEOS_C | USER_C | HOST_C,
837 /* f */ PRINT_C | ALPHA_C | LET_C | NICK_C | CHAN_C | NONEOS_C | USER_C | HOST_C,
838 /* g */ PRINT_C | ALPHA_C | LET_C | NICK_C | CHAN_C | NONEOS_C | USER_C | HOST_C,
839 /* h */ PRINT_C | ALPHA_C | LET_C | NICK_C | CHAN_C | NONEOS_C | USER_C | HOST_C,
840 /* i */ PRINT_C | ALPHA_C | LET_C | NICK_C | CHAN_C | NONEOS_C | USER_C | HOST_C,
841 /* j */ PRINT_C | ALPHA_C | LET_C | NICK_C | CHAN_C | NONEOS_C | USER_C | HOST_C,
842 /* k */ PRINT_C | ALPHA_C | LET_C | NICK_C | CHAN_C | NONEOS_C | USER_C | HOST_C,
843 /* l */ PRINT_C | ALPHA_C | LET_C | NICK_C | CHAN_C | NONEOS_C | USER_C | HOST_C,
844 /* m */ PRINT_C | ALPHA_C | LET_C | NICK_C | CHAN_C | NONEOS_C | USER_C | HOST_C,
845 /* n */ PRINT_C | ALPHA_C | LET_C | NICK_C | CHAN_C | NONEOS_C | USER_C | HOST_C,
846 /* o */ PRINT_C | ALPHA_C | LET_C | NICK_C | CHAN_C | NONEOS_C | USER_C | HOST_C,
847 /* p */ PRINT_C | ALPHA_C | LET_C | NICK_C | CHAN_C | NONEOS_C | USER_C | HOST_C,
848 /* q */ PRINT_C | ALPHA_C | LET_C | NICK_C | CHAN_C | NONEOS_C | USER_C | HOST_C,
849 /* r */ PRINT_C | ALPHA_C | LET_C | NICK_C | CHAN_C | NONEOS_C | USER_C | HOST_C,
850 /* s */ PRINT_C | ALPHA_C | LET_C | NICK_C | CHAN_C | NONEOS_C | USER_C | HOST_C,
851 /* t */ PRINT_C | ALPHA_C | LET_C | NICK_C | CHAN_C | NONEOS_C | USER_C | HOST_C,
852 /* u */ PRINT_C | ALPHA_C | LET_C | NICK_C | CHAN_C | NONEOS_C | USER_C | HOST_C,
853 /* v */ PRINT_C | ALPHA_C | LET_C | NICK_C | CHAN_C | NONEOS_C | USER_C | HOST_C,
854 /* w */ PRINT_C | ALPHA_C | LET_C | NICK_C | CHAN_C | NONEOS_C | USER_C | HOST_C,
855 /* x */ PRINT_C | ALPHA_C | LET_C | NICK_C | CHAN_C | NONEOS_C | USER_C | HOST_C,
856 /* y */ PRINT_C | ALPHA_C | LET_C | NICK_C | CHAN_C | NONEOS_C | USER_C | HOST_C,
857 /* z */ PRINT_C | ALPHA_C | LET_C | NICK_C | CHAN_C | NONEOS_C | USER_C | HOST_C,
858 /* { */ PRINT_C | ALPHA_C | NICK_C | CHAN_C | NONEOS_C | USER_C,
859 /* | */ PRINT_C | ALPHA_C | NICK_C | CHAN_C | NONEOS_C | USER_C,
860 /* } */ PRINT_C | ALPHA_C | NICK_C | CHAN_C | NONEOS_C | USER_C,
861 /* ~ */ PRINT_C | ALPHA_C | CHAN_C | NONEOS_C | USER_C,
862 /* del */ CHAN_C | NONEOS_C,
863 /* 0x80 */ CHAN_C | NONEOS_C,
864 /* 0x81 */ CHAN_C | NONEOS_C,
865 /* 0x82 */ CHAN_C | NONEOS_C,
866 /* 0x83 */ CHAN_C | NONEOS_C,
867 /* 0x84 */ CHAN_C | NONEOS_C,
868 /* 0x85 */ CHAN_C | NONEOS_C,
869 /* 0x86 */ CHAN_C | NONEOS_C,
870 /* 0x87 */ CHAN_C | NONEOS_C,
871 /* 0x88 */ CHAN_C | NONEOS_C,
872 /* 0x89 */ CHAN_C | NONEOS_C,
873 /* 0x8A */ CHAN_C | NONEOS_C,
874 /* 0x8B */ CHAN_C | NONEOS_C,
875 /* 0x8C */ CHAN_C | NONEOS_C,
876 /* 0x8D */ CHAN_C | NONEOS_C,
877 /* 0x8E */ CHAN_C | NONEOS_C,
878 /* 0x8F */ CHAN_C | NONEOS_C,
879 /* 0x90 */ CHAN_C | NONEOS_C,
880 /* 0x91 */ CHAN_C | NONEOS_C,
881 /* 0x92 */ CHAN_C | NONEOS_C,
882 /* 0x93 */ CHAN_C | NONEOS_C,
883 /* 0x94 */ CHAN_C | NONEOS_C,
884 /* 0x95 */ CHAN_C | NONEOS_C,
885 /* 0x96 */ CHAN_C | NONEOS_C,
886 /* 0x97 */ CHAN_C | NONEOS_C,
887 /* 0x98 */ CHAN_C | NONEOS_C,
888 /* 0x99 */ CHAN_C | NONEOS_C,
889 /* 0x9A */ CHAN_C | NONEOS_C,
890 /* 0x9B */ CHAN_C | NONEOS_C,
891 /* 0x9C */ CHAN_C | NONEOS_C,
892 /* 0x9D */ CHAN_C | NONEOS_C,
893 /* 0x9E */ CHAN_C | NONEOS_C,
894 /* 0x9F */ CHAN_C | NONEOS_C,
895 /* 0xA0 */ CHAN_C | FCHAN_C | NONEOS_C,
896 /* 0xA1 */ CHAN_C | NONEOS_C,
897 /* 0xA2 */ CHAN_C | NONEOS_C,
898 /* 0xA3 */ CHAN_C | NONEOS_C,
899 /* 0xA4 */ CHAN_C | NONEOS_C,
900 /* 0xA5 */ CHAN_C | NONEOS_C,
901 /* 0xA6 */ CHAN_C | NONEOS_C,
902 /* 0xA7 */ CHAN_C | NONEOS_C,
903 /* 0xA8 */ CHAN_C | NONEOS_C,
904 /* 0xA9 */ CHAN_C | NONEOS_C,
905 /* 0xAA */ CHAN_C | NONEOS_C,
906 /* 0xAB */ CHAN_C | NONEOS_C,
907 /* 0xAC */ CHAN_C | NONEOS_C,
908 /* 0xAD */ CHAN_C | NONEOS_C,
909 /* 0xAE */ CHAN_C | NONEOS_C,
910 /* 0xAF */ CHAN_C | NONEOS_C,
911 /* 0xB0 */ CHAN_C | NONEOS_C,
912 /* 0xB1 */ CHAN_C | NONEOS_C,
913 /* 0xB2 */ CHAN_C | NONEOS_C,
914 /* 0xB3 */ CHAN_C | NONEOS_C,
915 /* 0xB4 */ CHAN_C | NONEOS_C,
916 /* 0xB5 */ CHAN_C | NONEOS_C,
917 /* 0xB6 */ CHAN_C | NONEOS_C,
918 /* 0xB7 */ CHAN_C | NONEOS_C,
919 /* 0xB8 */ CHAN_C | NONEOS_C,
920 /* 0xB9 */ CHAN_C | NONEOS_C,
921 /* 0xBA */ CHAN_C | NONEOS_C,
922 /* 0xBB */ CHAN_C | NONEOS_C,
923 /* 0xBC */ CHAN_C | NONEOS_C,
924 /* 0xBD */ CHAN_C | NONEOS_C,
925 /* 0xBE */ CHAN_C | NONEOS_C,
926 /* 0xBF */ CHAN_C | NONEOS_C,
927 /* 0xC0 */ CHAN_C | NONEOS_C,
928 /* 0xC1 */ CHAN_C | NONEOS_C,
929 /* 0xC2 */ CHAN_C | NONEOS_C,
930 /* 0xC3 */ CHAN_C | NONEOS_C,
931 /* 0xC4 */ CHAN_C | NONEOS_C,
932 /* 0xC5 */ CHAN_C | NONEOS_C,
933 /* 0xC6 */ CHAN_C | NONEOS_C,
934 /* 0xC7 */ CHAN_C | NONEOS_C,
935 /* 0xC8 */ CHAN_C | NONEOS_C,
936 /* 0xC9 */ CHAN_C | NONEOS_C,
937 /* 0xCA */ CHAN_C | NONEOS_C,
938 /* 0xCB */ CHAN_C | NONEOS_C,
939 /* 0xCC */ CHAN_C | NONEOS_C,
940 /* 0xCD */ CHAN_C | NONEOS_C,
941 /* 0xCE */ CHAN_C | NONEOS_C,
942 /* 0xCF */ CHAN_C | NONEOS_C,
943 /* 0xD0 */ CHAN_C | NONEOS_C,
944 /* 0xD1 */ CHAN_C | NONEOS_C,
945 /* 0xD2 */ CHAN_C | NONEOS_C,
946 /* 0xD3 */ CHAN_C | NONEOS_C,
947 /* 0xD4 */ CHAN_C | NONEOS_C,
948 /* 0xD5 */ CHAN_C | NONEOS_C,
949 /* 0xD6 */ CHAN_C | NONEOS_C,
950 /* 0xD7 */ CHAN_C | NONEOS_C,
951 /* 0xD8 */ CHAN_C | NONEOS_C,
952 /* 0xD9 */ CHAN_C | NONEOS_C,
953 /* 0xDA */ CHAN_C | NONEOS_C,
954 /* 0xDB */ CHAN_C | NONEOS_C,
955 /* 0xDC */ CHAN_C | NONEOS_C,
956 /* 0xDD */ CHAN_C | NONEOS_C,
957 /* 0xDE */ CHAN_C | NONEOS_C,
958 /* 0xDF */ CHAN_C | NONEOS_C,
959 /* 0xE0 */ CHAN_C | NONEOS_C,
960 /* 0xE1 */ CHAN_C | NONEOS_C,
961 /* 0xE2 */ CHAN_C | NONEOS_C,
962 /* 0xE3 */ CHAN_C | NONEOS_C,
963 /* 0xE4 */ CHAN_C | NONEOS_C,
964 /* 0xE5 */ CHAN_C | NONEOS_C,
965 /* 0xE6 */ CHAN_C | NONEOS_C,
966 /* 0xE7 */ CHAN_C | NONEOS_C,
967 /* 0xE8 */ CHAN_C | NONEOS_C,
968 /* 0xE9 */ CHAN_C | NONEOS_C,
969 /* 0xEA */ CHAN_C | NONEOS_C,
970 /* 0xEB */ CHAN_C | NONEOS_C,
971 /* 0xEC */ CHAN_C | NONEOS_C,
972 /* 0xED */ CHAN_C | NONEOS_C,
973 /* 0xEE */ CHAN_C | NONEOS_C,
974 /* 0xEF */ CHAN_C | NONEOS_C,
975 /* 0xF0 */ CHAN_C | NONEOS_C,
976 /* 0xF1 */ CHAN_C | NONEOS_C,
977 /* 0xF2 */ CHAN_C | NONEOS_C,
978 /* 0xF3 */ CHAN_C | NONEOS_C,
979 /* 0xF4 */ CHAN_C | NONEOS_C,
980 /* 0xF5 */ CHAN_C | NONEOS_C,
981 /* 0xF6 */ CHAN_C | NONEOS_C,
982 /* 0xF7 */ CHAN_C | NONEOS_C,
983 /* 0xF8 */ CHAN_C | NONEOS_C,
984 /* 0xF9 */ CHAN_C | NONEOS_C,
985 /* 0xFA */ CHAN_C | NONEOS_C,
986 /* 0xFB */ CHAN_C | NONEOS_C,
987 /* 0xFC */ CHAN_C | NONEOS_C,
988 /* 0xFD */ CHAN_C | NONEOS_C,
989 /* 0xFE */ CHAN_C | NONEOS_C,
990 /* 0xFF */ CHAN_C | NONEOS_C
991 };