]> jfr.im git - irc/quakenet/snircd.git/blob - ircd/ircd_string.c
Update my e-mail address.
[irc/quakenet/snircd.git] / ircd / ircd_string.c
1 /*
2 * IRC - Internet Relay Chat, ircd/ircd_string.c
3 * Copyright (C) 1999 Thomas Helvey
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 /** @file
20 * @brief Implementation of string operations.
21 * @version $Id: ircd_string.c,v 1.24.2.2 2007/08/21 02:02:10 entrope Exp $
22 */
23 #include "config.h"
24
25 #include "ircd_string.h"
26 #include "ircd_defs.h"
27 #include "ircd_chattr.h"
28 #include "ircd_log.h"
29 #include "res.h"
30
31 /* #include <assert.h> -- Now using assert in ircd_log.h */
32 #include <string.h>
33 #include <sys/types.h>
34 #include <netinet/in.h>
35
36 /*
37 * include the character attribute tables here
38 */
39 #include "chattr.tab.c"
40
41 /** Check whether \a str contains wildcard characters.
42 * @param[in] str String that might contain wildcards.
43 * @return Non-zero if \a str contains naked (non-escaped) wildcards,
44 * zero if there are none or if they are all escaped.
45 */
46 int string_has_wildcards(const char* str)
47 {
48 assert(0 != str);
49 for ( ; *str; ++str) {
50 if ('\\' == *str) {
51 if ('\0' == *++str)
52 break;
53 }
54 else if ('*' == *str || '?' == *str)
55 return 1;
56 }
57 return 0;
58 }
59
60 /** Split a string on certain delimiters.
61 * This is a reentrant version of normal strtok(). The first call for
62 * a particular input string must use a non-NULL \a str; *save will be
63 * initialized based on that. Later calls must use a NULL \a str;
64 * *save will be updated.
65 * @param[in,out] save Pointer to a position indicator.
66 * @param[in] str Pointer to the input string, or NULL to continue.
67 * @param[in] fs String that lists token delimiters.
68 * @return Next token in input string, or NULL if no tokens remain.
69 */
70 char* ircd_strtok(char **save, char *str, char *fs)
71 {
72 char *pos = *save; /* keep last position across calls */
73 char *tmp;
74
75 if (str)
76 pos = str; /* new string scan */
77
78 while (pos && *pos && strchr(fs, *pos) != NULL)
79 pos++; /* skip leading separators */
80
81 if (!pos || !*pos)
82 return (pos = *save = NULL); /* string contains only sep's */
83
84 tmp = pos; /* now, keep position of the token */
85
86 while (*pos && strchr(fs, *pos) == NULL)
87 pos++; /* skip content of the token */
88
89 if (*pos)
90 *pos++ = '\0'; /* remove first sep after the token */
91 else
92 pos = NULL; /* end of string */
93
94 *save = pos;
95 return (tmp);
96 }
97
98 /** Rewrite a comma-delimited list of items to remove duplicates.
99 * @param[in,out] buffer Comma-delimited list.
100 * @return The input buffer \a buffer.
101 */
102 char* canonize(char* buffer)
103 {
104 static char cbuf[BUFSIZE];
105 char* s;
106 char* t;
107 char* cp = cbuf;
108 int l = 0;
109 char* p = NULL;
110 char* p2;
111
112 *cp = '\0';
113
114 for (s = ircd_strtok(&p, buffer, ","); s; s = ircd_strtok(&p, NULL, ","))
115 {
116 if (l)
117 {
118 p2 = NULL;
119 for (t = ircd_strtok(&p2, cbuf, ","); t; t = ircd_strtok(&p2, NULL, ","))
120 if (0 == ircd_strcmp(s, t))
121 break;
122 else if (p2)
123 p2[-1] = ',';
124 }
125 else
126 t = NULL;
127 if (!t)
128 {
129 if (l)
130 *(cp - 1) = ',';
131 else
132 l = 1;
133 strcpy(cp, s);
134 if (p)
135 cp += (p - s);
136 }
137 else if (p2)
138 p2[-1] = ',';
139 }
140 return cbuf;
141 }
142
143 /** Copy one string to another, not to exceed a certain length.
144 * @param[in] s1 Output buffer.
145 * @param[in] s2 Source buffer.
146 * @param[in] n Maximum number of bytes to write, plus one.
147 * @return The original input buffer \a s1.
148 */
149 char* ircd_strncpy(char* s1, const char* s2, size_t n)
150 {
151 char* endp = s1 + n;
152 char* s = s1;
153
154 assert(0 != s1);
155 assert(0 != s2);
156
157 while (s < endp && (*s++ = *s2++))
158 ;
159 if (s == endp)
160 *s = '\0';
161 return s1;
162 }
163
164
165 #ifndef FORCEINLINE
166 NTL_HDR_strChattr { NTL_SRC_strChattr }
167 NTL_HDR_strCasediff { NTL_SRC_strCasediff }
168 #endif /* !FORCEINLINE */
169
170 /*
171 * Other functions visible externally
172 */
173
174 /** Case insensitive string comparison.
175 * @param[in] a First string to compare.
176 * @param[in] b Second string to compare.
177 * @return Less than, equal to, or greater than zero if \a a is lexicographically less than, equal to, or greater than \a b.
178 */
179 int ircd_strcmp(const char *a, const char *b)
180 {
181 const char* ra = a;
182 const char* rb = b;
183 while (ToLower(*ra) == ToLower(*rb)) {
184 if (!*ra++)
185 return 0;
186 else
187 ++rb;
188 }
189 return (ToLower(*ra) - ToLower(*rb));
190 }
191
192 /** Case insensitive comparison of the starts of two strings.
193 * @param[in] a First string to compare.
194 * @param[in] b Second string to compare.
195 * @param[in] n Maximum number of characters to compare.
196 * @return Less than, equal to, or greater than zero if \a a is
197 * lexicographically less than, equal to, or greater than \a b.
198 */
199 int ircd_strncmp(const char *a, const char *b, size_t n)
200 {
201 const char* ra = a;
202 const char* rb = b;
203 int left = n;
204 if (!left--)
205 return 0;
206 while (ToLower(*ra) == ToLower(*rb)) {
207 if (!*ra++ || !left--)
208 return 0;
209 else
210 ++rb;
211 }
212 return (ToLower(*ra) - ToLower(*rb));
213 }
214
215 /** Fill a vector of distinct names from a delimited input list.
216 * Empty tokens (when \a token occurs at the start or end of \a list,
217 * or when \a token occurs adjacent to itself) are ignored. When
218 * \a size tokens have been written to \a vector, the rest of the
219 * string is ignored.
220 * Unlike token_vector(), if a token repeats an earlier token, it is
221 * skipped.
222 * @param[in,out] names Input buffer.
223 * @param[in] token Delimiter used to split \a list.
224 * @param[out] vector Output vector.
225 * @param[in] size Maximum number of elements to put in \a vector.
226 * @return Number of elements written to \a vector.
227 */
228 int unique_name_vector(char* names, char token, char** vector, int size)
229 {
230 int i;
231 int count = 0;
232 char* start = names;
233 char* end;
234
235 assert(0 != names);
236 assert(0 != vector);
237 assert(0 < size);
238
239 /*
240 * ignore spurious tokens
241 */
242 while (token == *start)
243 ++start;
244
245 for (end = strchr(start, token); end; end = strchr(start, token)) {
246 *end++ = '\0';
247 /*
248 * ignore spurious tokens
249 */
250 while (token == *end)
251 ++end;
252 for (i = 0; i < count; ++i) {
253 if (0 == ircd_strcmp(vector[i], start))
254 break;
255 }
256 if (i == count) {
257 vector[count++] = start;
258 if (count == size)
259 return count;
260 }
261 start = end;
262 }
263 if (*start) {
264 for (i = 0; i < count; ++i)
265 if (0 == ircd_strcmp(vector[i], start))
266 return count;
267 vector[count++] = start;
268 }
269 return count;
270 }
271
272 /** Fill a vector of tokens from a delimited input list.
273 * Empty tokens (when \a token occurs at the start or end of \a list,
274 * or when \a token occurs adjacent to itself) are ignored. When
275 * \a size tokens have been written to \a vector, the rest of the
276 * string is ignored.
277 * @param[in,out] names Input buffer.
278 * @param[in] token Delimiter used to split \a list.
279 * @param[out] vector Output vector.
280 * @param[in] size Maximum number of elements to put in \a vector.
281 * @return Number of elements written to \a vector.
282 */
283 int token_vector(char* names, char token, char** vector, int size)
284 {
285 int count = 0;
286 char* start = names;
287 char* end;
288
289 assert(0 != names);
290 assert(0 != vector);
291 assert(1 < size);
292
293 vector[count++] = start;
294 for (end = strchr(start, token); end; end = strchr(start, token)) {
295 *end++ = '\0';
296 start = end;
297 if (*start) {
298 vector[count++] = start;
299 if (count < size)
300 continue;
301 }
302 break;
303 }
304 return count;
305 }
306
307 /** Copy all or part of the hostname in a string to another string.
308 * If \a userhost contains an '\@', the remaining portion is used;
309 * otherwise, the whole \a userhost is used.
310 * @param[out] buf Output buffer.
311 * @param[in] userhost user\@hostname or hostname string.
312 * @param[in] len Maximum number of bytes to write to \a host.
313 * @return The output buffer \a buf.
314 */
315 char* host_from_uh(char* buf, const char* userhost, size_t len)
316 {
317 const char* s;
318
319 assert(0 != buf);
320 assert(0 != userhost);
321
322 if ((s = strchr(userhost, '@')))
323 ++s;
324 else
325 s = userhost;
326 ircd_strncpy(buf, s, len);
327 buf[len] = '\0';
328 return buf;
329 }
330
331 /*
332 * this new faster inet_ntoa was ripped from:
333 * From: Thomas Helvey <tomh@inxpress.net>
334 */
335 /** Array of text strings for dotted quads. */
336 static const char* IpQuadTab[] =
337 {
338 "0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
339 "10", "11", "12", "13", "14", "15", "16", "17", "18", "19",
340 "20", "21", "22", "23", "24", "25", "26", "27", "28", "29",
341 "30", "31", "32", "33", "34", "35", "36", "37", "38", "39",
342 "40", "41", "42", "43", "44", "45", "46", "47", "48", "49",
343 "50", "51", "52", "53", "54", "55", "56", "57", "58", "59",
344 "60", "61", "62", "63", "64", "65", "66", "67", "68", "69",
345 "70", "71", "72", "73", "74", "75", "76", "77", "78", "79",
346 "80", "81", "82", "83", "84", "85", "86", "87", "88", "89",
347 "90", "91", "92", "93", "94", "95", "96", "97", "98", "99",
348 "100", "101", "102", "103", "104", "105", "106", "107", "108", "109",
349 "110", "111", "112", "113", "114", "115", "116", "117", "118", "119",
350 "120", "121", "122", "123", "124", "125", "126", "127", "128", "129",
351 "130", "131", "132", "133", "134", "135", "136", "137", "138", "139",
352 "140", "141", "142", "143", "144", "145", "146", "147", "148", "149",
353 "150", "151", "152", "153", "154", "155", "156", "157", "158", "159",
354 "160", "161", "162", "163", "164", "165", "166", "167", "168", "169",
355 "170", "171", "172", "173", "174", "175", "176", "177", "178", "179",
356 "180", "181", "182", "183", "184", "185", "186", "187", "188", "189",
357 "190", "191", "192", "193", "194", "195", "196", "197", "198", "199",
358 "200", "201", "202", "203", "204", "205", "206", "207", "208", "209",
359 "210", "211", "212", "213", "214", "215", "216", "217", "218", "219",
360 "220", "221", "222", "223", "224", "225", "226", "227", "228", "229",
361 "230", "231", "232", "233", "234", "235", "236", "237", "238", "239",
362 "240", "241", "242", "243", "244", "245", "246", "247", "248", "249",
363 "250", "251", "252", "253", "254", "255"
364 };
365
366 /** Convert an IP address to printable ASCII form.
367 * This is generally deprecated in favor of ircd_ntoa_r().
368 * @param[in] in Address to convert.
369 * @return Pointer to a static buffer containing the readable form.
370 */
371 const char* ircd_ntoa(const struct irc_in_addr* in)
372 {
373 static char buf[SOCKIPLEN];
374 return ircd_ntoa_r(buf, in);
375 }
376
377 /** Convert an IP address to printable ASCII form.
378 * @param[out] buf Output buffer to write to.
379 * @param[in] in Address to format.
380 * @return Pointer to the output buffer \a buf.
381 */
382 const char* ircd_ntoa_r(char* buf, const struct irc_in_addr* in)
383 {
384 assert(buf != NULL);
385 assert(in != NULL);
386
387 if (irc_in_addr_is_ipv4(in)) {
388 unsigned int pos, len;
389 unsigned char *pch;
390
391 pch = (unsigned char*)&in->in6_16[6];
392 len = strlen(IpQuadTab[*pch]);
393 memcpy(buf, IpQuadTab[*pch++], len);
394 pos = len;
395 buf[pos++] = '.';
396 len = strlen(IpQuadTab[*pch]);
397 memcpy(buf+pos, IpQuadTab[*pch++], len);
398 pos += len;
399 buf[pos++] = '.';
400 len = strlen(IpQuadTab[*pch]);
401 memcpy(buf+pos, IpQuadTab[*pch++], len);
402 pos += len;
403 buf[pos++] = '.';
404 len = strlen(IpQuadTab[*pch]);
405 memcpy(buf+pos, IpQuadTab[*pch++], len);
406 buf[pos + len] = '\0';
407 return buf;
408 } else {
409 static const char hexdigits[] = "0123456789abcdef";
410 unsigned int pos, part, max_start, max_zeros, curr_zeros, ii;
411
412 /* Find longest run of zeros. */
413 for (max_start = ii = 1, max_zeros = curr_zeros = 0; ii < 8; ++ii) {
414 if (!in->in6_16[ii])
415 curr_zeros++;
416 else if (curr_zeros > max_zeros) {
417 max_start = ii - curr_zeros;
418 max_zeros = curr_zeros;
419 curr_zeros = 0;
420 }
421 }
422 if (curr_zeros > max_zeros) {
423 max_start = ii - curr_zeros;
424 max_zeros = curr_zeros;
425 }
426
427 /* Print out address. */
428 /** Append \a CH to the output buffer. */
429 #define APPEND(CH) do { buf[pos++] = (CH); } while (0)
430 for (pos = ii = 0; (ii < 8); ++ii) {
431 if ((max_zeros > 0) && (ii == max_start)) {
432 APPEND(':');
433 ii += max_zeros - 1;
434 continue;
435 }
436 part = ntohs(in->in6_16[ii]);
437 if (part >= 0x1000)
438 APPEND(hexdigits[part >> 12]);
439 if (part >= 0x100)
440 APPEND(hexdigits[(part >> 8) & 15]);
441 if (part >= 0x10)
442 APPEND(hexdigits[(part >> 4) & 15]);
443 APPEND(hexdigits[part & 15]);
444 if (ii < 7)
445 APPEND(':');
446 }
447 #undef APPEND
448
449 /* Nul terminate and return number of characters used. */
450 buf[pos++] = '\0';
451 return buf;
452 }
453 }
454
455 /** Attempt to parse an IPv4 address into a network-endian form.
456 * @param[in] input Input string.
457 * @param[out] output Network-endian representation of the address.
458 * @param[out] pbits Number of bits found in pbits.
459 * @return Number of characters used from \a input, or 0 if the parse failed.
460 */
461 static unsigned int
462 ircd_aton_ip4(const char *input, unsigned int *output, unsigned char *pbits)
463 {
464 unsigned int dots = 0, pos = 0, part = 0, ip = 0, bits;
465
466 /* Intentionally no support for bizarre IPv4 formats (plain
467 * integers, octal or hex components) -- only vanilla dotted
468 * decimal quads.
469 */
470 if (input[0] == '.')
471 return 0;
472 bits = 32;
473 while (1) switch (input[pos]) {
474 case '\0':
475 if (dots < 3)
476 return 0;
477 out:
478 ip |= part << (24 - 8 * dots);
479 *output = htonl(ip);
480 if (pbits)
481 *pbits = bits;
482 return pos;
483 case '.':
484 if (input[++pos] == '.')
485 return 0;
486 ip |= part << (24 - 8 * dots++);
487 part = 0;
488 if (input[pos] == '*') {
489 while (input[++pos] == '*' || input[pos] == '.') ;
490 if (input[pos] != '\0')
491 return 0;
492 if (pbits)
493 *pbits = dots * 8;
494 *output = htonl(ip);
495 return pos;
496 }
497 break;
498 case '/':
499 if (!pbits || !IsDigit(input[pos + 1]))
500 return 0;
501 for (bits = 0; IsDigit(input[++pos]); )
502 bits = bits * 10 + input[pos] - '0';
503 if (bits > 32)
504 return 0;
505 goto out;
506 case '0': case '1': case '2': case '3': case '4':
507 case '5': case '6': case '7': case '8': case '9':
508 part = part * 10 + input[pos++] - '0';
509 if (part > 255)
510 return 0;
511 break;
512 default:
513 return 0;
514 }
515 }
516
517 /** Parse a numeric IPv4 or IPv6 address into an irc_in_addr.
518 * @param[in] input Input buffer.
519 * @param[out] ip Receives parsed IP address.
520 * @param[out] pbits If non-NULL, receives number of bits specified in address mask.
521 * @return Number of characters used from \a input, or 0 if the
522 * address was unparseable or malformed.
523 */
524 int
525 ipmask_parse(const char *input, struct irc_in_addr *ip, unsigned char *pbits)
526 {
527 char *colon;
528 char *dot;
529
530 assert(ip);
531 assert(input);
532 memset(ip, 0, sizeof(*ip));
533 colon = strchr(input, ':');
534 dot = strchr(input, '.');
535
536 if (colon && (!dot || (dot > colon))) {
537 unsigned int part = 0, pos = 0, ii = 0, colon = 8;
538 const char *part_start = NULL;
539
540 /* Parse IPv6, possibly like ::127.0.0.1.
541 * This is pretty straightforward; the only trick is borrowed
542 * from Paul Vixie (BIND): when it sees a "::" continue as if
543 * it were a single ":", but note where it happened, and fill
544 * with zeros afterward.
545 */
546 if (input[pos] == ':') {
547 if ((input[pos+1] != ':') || (input[pos+2] == ':'))
548 return 0;
549 colon = 0;
550 pos += 2;
551 part_start = input + pos;
552 }
553 while (ii < 8) switch (input[pos]) {
554 unsigned char chval;
555 case '0': case '1': case '2': case '3': case '4':
556 case '5': case '6': case '7': case '8': case '9':
557 chval = input[pos] - '0';
558 use_chval:
559 part = (part << 4) | chval;
560 if (part > 0xffff)
561 return 0;
562 pos++;
563 break;
564 case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
565 chval = input[pos] - 'A' + 10;
566 goto use_chval;
567 case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
568 chval = input[pos] - 'a' + 10;
569 goto use_chval;
570 case ':':
571 part_start = input + ++pos;
572 if (input[pos] == '.')
573 return 0;
574 ip->in6_16[ii++] = htons(part);
575 part = 0;
576 if (input[pos] == ':') {
577 if (colon < 8)
578 return 0;
579 colon = ii;
580 pos++;
581 }
582 break;
583 case '.': {
584 uint32_t ip4;
585 unsigned int len;
586 len = ircd_aton_ip4(part_start, &ip4, pbits);
587 if (!len || (ii > 6))
588 return 0;
589 ip->in6_16[ii++] = htons(ntohl(ip4) >> 16);
590 ip->in6_16[ii++] = htons(ntohl(ip4) & 65535);
591 if (pbits)
592 *pbits += 96;
593 pos = part_start + len - input;
594 goto finish;
595 }
596 case '/':
597 if (!pbits || !IsDigit(input[pos + 1]))
598 return 0;
599 ip->in6_16[ii++] = htons(part);
600 for (part = 0; IsDigit(input[++pos]); )
601 part = part * 10 + input[pos] - '0';
602 if (part > 128)
603 return 0;
604 *pbits = part;
605 goto finish;
606 case '*':
607 while (input[++pos] == '*' || input[pos] == ':') ;
608 if (input[pos] != '\0' || colon < 8)
609 return 0;
610 if (pbits)
611 *pbits = ii * 16;
612 return pos;
613 case '\0':
614 ip->in6_16[ii++] = htons(part);
615 if (colon == 8 && ii < 8)
616 return 0;
617 if (pbits)
618 *pbits = 128;
619 goto finish;
620 default:
621 return 0;
622 }
623 finish:
624 if (colon < 8) {
625 unsigned int jj;
626 /* Shift stuff after "::" up and fill middle with zeros. */
627 for (jj = 0; jj < ii - colon; jj++)
628 ip->in6_16[7 - jj] = ip->in6_16[ii - jj - 1];
629 for (jj = 0; jj < 8 - ii; jj++)
630 ip->in6_16[colon + jj] = 0;
631 }
632 return pos;
633 } else if (dot || strchr(input, '/')) {
634 unsigned int addr;
635 int len = ircd_aton_ip4(input, &addr, pbits);
636 if (len) {
637 ip->in6_16[5] = htons(65535);
638 ip->in6_16[6] = htons(ntohl(addr) >> 16);
639 ip->in6_16[7] = htons(ntohl(addr) & 65535);
640 if (pbits)
641 *pbits += 96;
642 }
643 return len;
644 } else if (input[0] == '*') {
645 unsigned int pos = 0;
646 while (input[++pos] == '*') ;
647 if (input[pos] != '\0')
648 return 0;
649 if (pbits)
650 *pbits = 0;
651 return pos;
652 } else return 0; /* parse failed */
653 }