X-Git-Url: http://git.pk910.de/?p=ircu2.10.12-pk.git;a=blobdiff_plain;f=ircd%2Fircd_string.c;fp=ircd%2Fircd_string.c;h=ed0349ea45fc20b1d8700cc80c355b82317c0603;hp=0000000000000000000000000000000000000000;hb=0400a5a6479398d82526785c18c0df8bc8b92dce;hpb=d17e10da972ce5776c60b4c317267c6abe0e1ead diff --git a/ircd/ircd_string.c b/ircd/ircd_string.c new file mode 100644 index 0000000..ed0349e --- /dev/null +++ b/ircd/ircd_string.c @@ -0,0 +1,661 @@ +/* + * IRC - Internet Relay Chat, ircd/ircd_string.c + * Copyright (C) 1999 Thomas Helvey + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 1, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +/** @file + * @brief Implementation of string operations. + * @version $Id$ + */ +#include "config.h" + +#include "ircd_string.h" +#include "ircd_defs.h" +#include "ircd_chattr.h" +#include "ircd_log.h" +#include "res.h" + +/* #include -- Now using assert in ircd_log.h */ +#include +#include +#include + +/* + * include the character attribute tables here + */ +#include "chattr.tab.c" + +/** Check whether \a str contains wildcard characters. + * @param[in] str String that might contain wildcards. + * @return Non-zero if \a str contains naked (non-escaped) wildcards, + * zero if there are none or if they are all escaped. + */ +int string_has_wildcards(const char* str) +{ + assert(0 != str); + for ( ; *str; ++str) { + if ('\\' == *str) { + if ('\0' == *++str) + break; + } + else if ('*' == *str || '?' == *str) + return 1; + } + return 0; +} + +/** Split a string on certain delimiters. + * This is a reentrant version of normal strtok(). The first call for + * a particular input string must use a non-NULL \a str; *save will be + * initialized based on that. Later calls must use a NULL \a str; + * *save will be updated. + * @param[in,out] save Pointer to a position indicator. + * @param[in] str Pointer to the input string, or NULL to continue. + * @param[in] fs String that lists token delimiters. + * @return Next token in input string, or NULL if no tokens remain. + */ +char* ircd_strtok(char **save, char *str, char *fs) +{ + char *pos = *save; /* keep last position across calls */ + char *tmp; + + if (str) + pos = str; /* new string scan */ + + while (pos && *pos && strchr(fs, *pos) != NULL) + pos++; /* skip leading separators */ + + if (!pos || !*pos) + return (pos = *save = NULL); /* string contains only sep's */ + + tmp = pos; /* now, keep position of the token */ + + while (*pos && strchr(fs, *pos) == NULL) + pos++; /* skip content of the token */ + + if (*pos) + *pos++ = '\0'; /* remove first sep after the token */ + else + pos = NULL; /* end of string */ + + *save = pos; + return (tmp); +} + +/** Rewrite a comma-delimited list of items to remove duplicates. + * @param[in,out] buffer Comma-delimited list. + * @return The input buffer \a buffer. + */ +char* canonize(char* buffer) +{ + static char cbuf[BUFSIZE]; + char* s; + char* t; + char* cp = cbuf; + int l = 0; + char* p = NULL; + char* p2; + + *cp = '\0'; + + for (s = ircd_strtok(&p, buffer, ","); s; s = ircd_strtok(&p, NULL, ",")) + { + if (l) + { + p2 = NULL; + for (t = ircd_strtok(&p2, cbuf, ","); t; t = ircd_strtok(&p2, NULL, ",")) + if (0 == ircd_strcmp(s, t)) + break; + else if (p2) + p2[-1] = ','; + } + else + t = NULL; + if (!t) + { + if (l) + *(cp - 1) = ','; + else + l = 1; + strcpy(cp, s); + if (p) + cp += (p - s); + } + else if (p2) + p2[-1] = ','; + } + return cbuf; +} + +/** Copy one string to another, not to exceed a certain length. + * @param[in] s1 Output buffer. + * @param[in] s2 Source buffer. + * @param[in] n Maximum number of bytes to write, plus one. + * @return The original input buffer \a s1. + */ +char* ircd_strncpy(char* s1, const char* s2, size_t n) +{ + char* endp = s1 + n; + char* s = s1; + + assert(0 != s1); + assert(0 != s2); + + while (s < endp && (*s++ = *s2++)) + ; + if (s == endp) + *s = '\0'; + return s1; +} + + +#ifndef FORCEINLINE +NTL_HDR_strChattr { NTL_SRC_strChattr } +NTL_HDR_strCasediff { NTL_SRC_strCasediff } +#endif /* !FORCEINLINE */ + +/* + * Other functions visible externally + */ + +/** Case insensitive string comparison. + * @param[in] a First string to compare. + * @param[in] b Second string to compare. + * @return Less than, equal to, or greater than zero if \a a is lexicographically less than, equal to, or greater than \a b. + */ +int ircd_strcmp(const char *a, const char *b) +{ + const char* ra = a; + const char* rb = b; + while (ToLower(*ra) == ToLower(*rb)) { + if (!*ra++) + return 0; + else + ++rb; + } + return (ToLower(*ra) - ToLower(*rb)); +} + +/** Case insensitive comparison of the starts of two strings. + * @param[in] a First string to compare. + * @param[in] b Second string to compare. + * @param[in] n Maximum number of characters to compare. + * @return Less than, equal to, or greater than zero if \a a is + * lexicographically less than, equal to, or greater than \a b. + */ +int ircd_strncmp(const char *a, const char *b, size_t n) +{ + const char* ra = a; + const char* rb = b; + int left = n; + if (!left--) + return 0; + while (ToLower(*ra) == ToLower(*rb)) { + if (!*ra++ || !left--) + return 0; + else + ++rb; + } + return (ToLower(*ra) - ToLower(*rb)); +} + +/** Fill a vector of distinct names from a delimited input list. + * Empty tokens (when \a token occurs at the start or end of \a list, + * or when \a token occurs adjacent to itself) are ignored. When + * \a size tokens have been written to \a vector, the rest of the + * string is ignored. + * Unlike token_vector(), if a token repeats an earlier token, it is + * skipped. + * @param[in,out] names Input buffer. + * @param[in] token Delimiter used to split \a list. + * @param[out] vector Output vector. + * @param[in] size Maximum number of elements to put in \a vector. + * @return Number of elements written to \a vector. + */ +int unique_name_vector(char* names, char token, char** vector, int size) +{ + int i; + int count = 0; + char* start = names; + char* end; + + assert(0 != names); + assert(0 != vector); + assert(0 < size); + + /* + * ignore spurious tokens + */ + while (token == *start) + ++start; + + for (end = strchr(start, token); end; end = strchr(start, token)) { + *end++ = '\0'; + /* + * ignore spurious tokens + */ + while (token == *end) + ++end; + for (i = 0; i < count; ++i) { + if (0 == ircd_strcmp(vector[i], start)) + break; + } + if (i == count) { + vector[count++] = start; + if (count == size) + return count; + } + start = end; + } + if (*start) { + for (i = 0; i < count; ++i) + if (0 == ircd_strcmp(vector[i], start)) + return count; + vector[count++] = start; + } + return count; +} + +/** Fill a vector of tokens from a delimited input list. + * Empty tokens (when \a token occurs at the start or end of \a list, + * or when \a token occurs adjacent to itself) are ignored. When + * \a size tokens have been written to \a vector, the rest of the + * string is ignored. + * @param[in,out] names Input buffer. + * @param[in] token Delimiter used to split \a list. + * @param[out] vector Output vector. + * @param[in] size Maximum number of elements to put in \a vector. + * @return Number of elements written to \a vector. + */ +int token_vector(char* names, char token, char** vector, int size) +{ + int count = 0; + char* start = names; + char* end; + + assert(0 != names); + assert(0 != vector); + assert(1 < size); + + vector[count++] = start; + for (end = strchr(start, token); end; end = strchr(start, token)) { + *end++ = '\0'; + start = end; + if (*start) { + vector[count++] = start; + if (count < size) + continue; + } + break; + } + return count; +} + +/** Copy all or part of the hostname in a string to another string. + * If \a userhost contains an '\@', the remaining portion is used; + * otherwise, the whole \a userhost is used. + * @param[out] buf Output buffer. + * @param[in] userhost user\@hostname or hostname string. + * @param[in] len Maximum number of bytes to write to \a host. + * @return The output buffer \a buf. + */ +char* host_from_uh(char* buf, const char* userhost, size_t len) +{ + const char* s; + + assert(0 != buf); + assert(0 != userhost); + + if ((s = strchr(userhost, '@'))) + ++s; + else + s = userhost; + ircd_strncpy(buf, s, len); + buf[len] = '\0'; + return buf; +} + +/* + * this new faster inet_ntoa was ripped from: + * From: Thomas Helvey + */ +/** Array of text strings for dotted quads. */ +static const char* IpQuadTab[] = +{ + "0", "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", "164", "165", "166", "167", "168", "169", + "170", "171", "172", "173", "174", "175", "176", "177", "178", "179", + "180", "181", "182", "183", "184", "185", "186", "187", "188", "189", + "190", "191", "192", "193", "194", "195", "196", "197", "198", "199", + "200", "201", "202", "203", "204", "205", "206", "207", "208", "209", + "210", "211", "212", "213", "214", "215", "216", "217", "218", "219", + "220", "221", "222", "223", "224", "225", "226", "227", "228", "229", + "230", "231", "232", "233", "234", "235", "236", "237", "238", "239", + "240", "241", "242", "243", "244", "245", "246", "247", "248", "249", + "250", "251", "252", "253", "254", "255" +}; + +/** Convert an IP address to printable ASCII form. + * This is generally deprecated in favor of ircd_ntoa_r(). + * @param[in] in Address to convert. + * @return Pointer to a static buffer containing the readable form. + */ +const char* ircd_ntoa(const struct irc_in_addr* in) +{ + static char buf[SOCKIPLEN]; + return ircd_ntoa_r(buf, in); +} + +/** Convert an IP address to printable ASCII form. + * @param[out] buf Output buffer to write to. + * @param[in] in Address to format. + * @return Pointer to the output buffer \a buf. + */ +const char* ircd_ntoa_r(char* buf, const struct irc_in_addr* in) +{ + assert(buf != NULL); + assert(in != NULL); + + if (irc_in_addr_is_ipv4(in)) { + unsigned int pos, len; + unsigned char *pch; + + pch = (unsigned char*)&in->in6_16[6]; + len = strlen(IpQuadTab[*pch]); + memcpy(buf, IpQuadTab[*pch++], len); + pos = len; + buf[pos++] = '.'; + len = strlen(IpQuadTab[*pch]); + memcpy(buf+pos, IpQuadTab[*pch++], len); + pos += len; + buf[pos++] = '.'; + len = strlen(IpQuadTab[*pch]); + memcpy(buf+pos, IpQuadTab[*pch++], len); + pos += len; + buf[pos++] = '.'; + len = strlen(IpQuadTab[*pch]); + memcpy(buf+pos, IpQuadTab[*pch++], len); + buf[pos + len] = '\0'; + return buf; + } else { + static const char hexdigits[] = "0123456789abcdef"; + unsigned int pos, part, max_start, max_zeros, curr_zeros, ii; + + /* Find longest run of zeros. */ + for (max_start = ii = 1, max_zeros = curr_zeros = 0; ii < 8; ++ii) { + if (!in->in6_16[ii]) + curr_zeros++; + else if (curr_zeros > max_zeros) { + max_start = ii - curr_zeros; + max_zeros = curr_zeros; + curr_zeros = 0; + } + } + if (curr_zeros > max_zeros) { + max_start = ii - curr_zeros; + max_zeros = curr_zeros; + } + + /* Print out address. */ +/** Append \a CH to the output buffer. */ +#define APPEND(CH) do { buf[pos++] = (CH); } while (0) + for (pos = ii = 0; (ii < 8); ++ii) { + if ((max_zeros > 0) && (ii == max_start)) { + APPEND(':'); + ii += max_zeros - 1; + continue; + } + part = ntohs(in->in6_16[ii]); + if (part >= 0x1000) + APPEND(hexdigits[part >> 12]); + if (part >= 0x100) + APPEND(hexdigits[(part >> 8) & 15]); + if (part >= 0x10) + APPEND(hexdigits[(part >> 4) & 15]); + APPEND(hexdigits[part & 15]); + if (ii < 7) + APPEND(':'); + } +#undef APPEND + + /* Nul terminate and return number of characters used. */ + buf[pos++] = '\0'; + return buf; + } +} + +/** Attempt to parse an IPv4 address into a network-endian form. + * @param[in] input Input string. + * @param[out] output Network-endian representation of the address. + * @param[out] pbits Number of bits found in pbits. + * @return Number of characters used from \a input, or 0 if the parse failed. + */ +static unsigned int +ircd_aton_ip4(const char *input, unsigned int *output, unsigned char *pbits) +{ + unsigned int dots = 0, pos = 0, part = 0, ip = 0, bits; + + /* Intentionally no support for bizarre IPv4 formats (plain + * integers, octal or hex components) -- only vanilla dotted + * decimal quads. + */ + if (input[0] == '.') + return 0; + bits = 32; + while (1) switch (input[pos]) { + case '\0': + if (dots < 3) + return 0; + out: + ip |= part << (24 - 8 * dots); + *output = htonl(ip); + if (pbits) + *pbits = bits; + return pos; + case '.': + if (++dots > 3) + return 0; + if (input[++pos] == '.') + return 0; + ip |= part << (32 - 8 * dots); + part = 0; + if (input[pos] == '*') { + while (input[++pos] == '*' || input[pos] == '.') ; + if (input[pos] != '\0') + return 0; + if (pbits) + *pbits = dots * 8; + *output = htonl(ip); + return pos; + } + break; + case '/': + if (!pbits || !IsDigit(input[pos + 1])) + return 0; + for (bits = 0; IsDigit(input[++pos]); ) + bits = bits * 10 + input[pos] - '0'; + if (bits > 32) + return 0; + goto out; + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + part = part * 10 + input[pos++] - '0'; + if (part > 255) + return 0; + break; + default: + return 0; + } +} + +/** Parse a numeric IPv4 or IPv6 address into an irc_in_addr. + * @param[in] input Input buffer. + * @param[out] ip Receives parsed IP address. + * @param[out] pbits If non-NULL, receives number of bits specified in address mask. + * @return Number of characters used from \a input, or 0 if the + * address was unparseable or malformed. + */ +int +ipmask_parse(const char *input, struct irc_in_addr *ip, unsigned char *pbits) +{ + char *colon; + char *dot; + + assert(ip); + assert(input); + memset(ip, 0, sizeof(*ip)); + colon = strchr(input, ':'); + dot = strchr(input, '.'); + + if (colon && (!dot || (dot > colon))) { + unsigned int part = 0, pos = 0, ii = 0, colon = 8; + const char *part_start = NULL; + + /* Parse IPv6, possibly like ::127.0.0.1. + * This is pretty straightforward; the only trick is borrowed + * from Paul Vixie (BIND): when it sees a "::" continue as if + * it were a single ":", but note where it happened, and fill + * with zeros afterward. + */ + if (input[pos] == ':') { + if ((input[pos+1] != ':') || (input[pos+2] == ':')) + return 0; + colon = 0; + pos += 2; + part_start = input + pos; + } + while (ii < 8) switch (input[pos]) { + unsigned char chval; + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + chval = input[pos] - '0'; + use_chval: + part = (part << 4) | chval; + if (part > 0xffff) + return 0; + pos++; + break; + case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': + chval = input[pos] - 'A' + 10; + goto use_chval; + case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': + chval = input[pos] - 'a' + 10; + goto use_chval; + case ':': + part_start = input + ++pos; + if (input[pos] == '.') + return 0; + ip->in6_16[ii++] = htons(part); + part = 0; + if (input[pos] == ':') { + if (colon < 8) + return 0; + if (ii == 8) + return 0; + colon = ii; + pos++; + } + break; + case '.': { + uint32_t ip4; + unsigned int len; + len = ircd_aton_ip4(part_start, &ip4, pbits); + if (!len || (ii > 6)) + return 0; + ip->in6_16[ii++] = htons(ntohl(ip4) >> 16); + ip->in6_16[ii++] = htons(ntohl(ip4) & 65535); + if (pbits) + *pbits += 96; + pos = part_start + len - input; + goto finish; + } + case '/': + if (!pbits || !IsDigit(input[pos + 1])) + return 0; + ip->in6_16[ii++] = htons(part); + for (part = 0; IsDigit(input[++pos]); ) + part = part * 10 + input[pos] - '0'; + if (part > 128) + return 0; + *pbits = part; + goto finish; + case '*': + while (input[++pos] == '*' || input[pos] == ':') ; + if (input[pos] != '\0' || colon < 8) + return 0; + if (part && ii < 8) + ip->in6_16[ii++] = htons(part); + if (pbits) + *pbits = ii * 16; + return pos; + case '\0': + ip->in6_16[ii++] = htons(part); + if (colon == 8 && ii < 8) + return 0; + if (pbits) + *pbits = 128; + goto finish; + default: + return 0; + } + if (input[pos] != '\0') + return 0; + finish: + if (colon < 8) { + unsigned int jj; + /* Shift stuff after "::" up and fill middle with zeros. */ + for (jj = 0; jj < ii - colon; jj++) + ip->in6_16[7 - jj] = ip->in6_16[ii - jj - 1]; + for (jj = 0; jj < 8 - ii; jj++) + ip->in6_16[colon + jj] = 0; + } + return pos; + } else if (dot || strchr(input, '/')) { + unsigned int addr; + int len = ircd_aton_ip4(input, &addr, pbits); + if (len) { + ip->in6_16[5] = htons(65535); + ip->in6_16[6] = htons(ntohl(addr) >> 16); + ip->in6_16[7] = htons(ntohl(addr) & 65535); + if (pbits) + *pbits += 96; + } + return len; + } else if (input[0] == '*') { + unsigned int pos = 0; + while (input[++pos] == '*') ; + if (input[pos] != '\0') + return 0; + if (pbits) + *pbits = 0; + return pos; + } else return 0; /* parse failed */ +}