X-Git-Url: http://git.pk910.de/?a=blobdiff_plain;f=src%2Ftools.c;h=b10bc7be4379b5b573008142c58bb539ffe9c9c3;hb=HEAD;hp=26c83a9b393fbe38ebdb5a10aef48be20662b1b0;hpb=04846fe0848d04de677abbb0c9ff8b46c68b0807;p=srvx.git diff --git a/src/tools.c b/src/tools.c index 26c83a9..b10bc7b 100644 --- a/src/tools.c +++ b/src/tools.c @@ -50,13 +50,23 @@ static const unsigned char convert2n[256] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 52,53,54,55,56,57,58,59,60,61, 0, 0, 0, 0, 0, 0, + 52,53,54,55,56,57,58,59,60,61, 0, 0, 0, 0, 0, 0, 0, 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,62, 0,63, 0, 0, 0,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, 0, 0, 0, 0, 0 }; +static const unsigned char ctype[256] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, + 0,10,11,12,13,14,15, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0,10,11,12,13,14,15, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; + unsigned long int base64toint(const char* s, int count) { @@ -78,6 +88,273 @@ const char* inttobase64(char* buf, unsigned int v, unsigned int count) return buf; } +unsigned int +irc_ntop(char *output, unsigned int out_size, const irc_in_addr_t *addr) +{ + static const char hexdigits[] = "0123456789abcdef"; + unsigned int pos; + + assert(output); + assert(addr); + + if (irc_in_addr_is_ipv4(*addr)) { + unsigned int ip4; + + ip4 = (ntohs(addr->in6[6]) << 16) | ntohs(addr->in6[7]); + pos = snprintf(output, out_size, "%u.%u.%u.%u", (ip4 >> 24), (ip4 >> 16) & 255, (ip4 >> 8) & 255, ip4 & 255); + } else { + unsigned int part, max_start, max_zeros, curr_zeros, ii; + + /* Find longest run of zeros. */ + for (max_start = max_zeros = curr_zeros = ii = 0; ii < 8; ++ii) { + if (!addr->in6[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. */ +#define APPEND(CH) do { if (pos < out_size) output[pos] = (CH); pos++; } while (0) + for (pos = 0, ii = 0; ii < 8; ++ii) { + if ((max_zeros > 0) && (ii == max_start)) { + if (ii == 0) + APPEND(':'); + APPEND(':'); + ii += max_zeros - 1; + continue; + } + part = ntohs(addr->in6[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 + output[pos < out_size ? pos : out_size - 1] = '\0'; + } + + return pos; +} + +unsigned int +irc_ntop_mask(char *output, unsigned int out_size, const irc_in_addr_t *addr, unsigned char bits) +{ + char base_addr[IRC_NTOP_MAX_SIZE]; + int len; + + if (bits >= 128) + return irc_ntop(output, out_size, addr); + if (!irc_ntop(base_addr, sizeof(base_addr), addr)) + return 0; + len = snprintf(output, out_size, "%s/%d", base_addr, bits); + if ((unsigned int)len >= out_size) + return 0; + return len; +} + +static unsigned int +irc_pton_ip4(const char *input, unsigned char *pbits, uint32_t *output) +{ + unsigned int dots = 0, pos = 0, part = 0, ip = 0, bits = 32; + + /* Intentionally no support for bizarre IPv4 formats (plain + * integers, octal or hex components) -- only vanilla dotted + * decimal quads, optionally with trailing /nn. + */ + if (input[0] == '.') + return 0; + while (1) switch (input[pos]) { + default: + if (dots < 3) + return 0; + out: + ip |= part << (24 - 8 * dots++); + *output = htonl(ip); + if (pbits) + *pbits = bits; + return pos; + case '.': + if (input[++pos] == '.') + return 0; + ip |= part << (24 - 8 * dots++); + part = 0; + if (input[pos] == '*') { + while (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; + } +} + +unsigned int +irc_pton(irc_in_addr_t *addr, unsigned char *bits, const char *input) +{ + const char *part_start = NULL; + char *colon; + char *dot; + unsigned int part = 0, pos = 0, ii = 0, cpos = 8; + + assert(input); + memset(addr, 0, sizeof(*addr)); + colon = strchr(input, ':'); + dot = strchr(input, '.'); + + if (colon && (!dot || (dot > colon))) { + /* 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 afterwards. + */ + if (input[pos] == ':') { + if ((input[pos+1] != ':') || (input[pos+2] == ':')) + return 0; + cpos = 0; + pos += 2; + part_start = input + pos; + } + while (ii < 8) switch (input[pos]) { + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': + case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': + part = (part << 4) | (ctype[(unsigned char)input[pos++]] & 15); + if (part > 0xffff) + return 0; + break; + case ':': + part_start = input + ++pos; + if (input[pos] == '.') + return 0; + addr->in6[ii++] = htons(part); + part = 0; + if (input[pos] == ':') { + if (cpos < 8) + return 0; + cpos = ii; + } + break; + case '.': { + uint32_t ip4; + unsigned int len; + len = irc_pton_ip4(part_start, bits, &ip4); + if (!len || (ii > 6)) + return 0; + memcpy(addr->in6 + ii, &ip4, sizeof(ip4)); + if (bits) + *bits += 96; + ii += 2; + pos = part_start + len - input; + goto finish; + } + case '/': + if (!bits || !isdigit(input[pos + 1])) + return 0; + addr->in6[ii++] = htons(part); + for (part = 0; isdigit(input[++pos]); ) + part = part * 10 + input[pos] - '0'; + if (part > 128) + return 0; + *bits = part; + goto finish; + case '*': + while (input[++pos] == '*') ; + if (input[pos] != '\0' || cpos < 8) + return 0; + if (bits) + *bits = ii * 16; + return pos; + default: + addr->in6[ii++] = htons(part); + if (cpos == 8 && ii < 8) + return 0; + if (bits) + *bits = 128; + goto finish; + } + finish: + /* Shift stuff after "::" up and fill middle with zeros. */ + if (cpos < 8) { + unsigned int jj; + for (jj = 0; jj < ii - cpos; jj++) + addr->in6[7 - jj] = addr->in6[ii - jj - 1]; + for (jj = 0; jj < 8 - ii; jj++) + addr->in6[cpos + jj] = 0; + } + } else if (dot) { + uint32_t ip4; + pos = irc_pton_ip4(input, bits, &ip4); + if (pos) { +/* glibc's htons() macro is not -Wshadow-safe. */ +#undef htons + addr->in6[5] = htons(65535); + addr->in6[6] = htons(ntohl(ip4) >> 16); + addr->in6[7] = htons(ntohl(ip4) & 65535); + if (bits) + *bits += 96; + } + } else if (input[0] == '*') { + while (input[++pos] == '*') ; + if (input[pos] != '\0') + return 0; + if (bits) + *bits = 0; + } + return pos; +} + +const char *irc_ntoa(const irc_in_addr_t *addr) +{ + static char ntoa[IRC_NTOP_MAX_SIZE]; + irc_ntop(ntoa, sizeof(ntoa), addr); + return ntoa; +} + +unsigned int +irc_check_mask(const irc_in_addr_t *check, const irc_in_addr_t *mask, unsigned char bits) +{ + unsigned int ii; + + for (ii = 0; (ii < 8) && (bits > 16); bits -= 16, ++ii) + if (check->in6[ii] != mask->in6[ii]) + return 0; + if (ii < 8 && bits > 0 + && (ntohs(check->in6[ii] ^ mask->in6[ii]) >> (16 - bits))) + return 0; + return 1; +} + static char irc_tolower[256]; #undef tolower #define tolower(X) irc_tolower[(unsigned char)(X)] @@ -110,25 +387,33 @@ irccasestr(const char *haystack, const char *needle) { return NULL; } +char * +ircstrlower(char *str) { + size_t ii; + for (ii = 0; str[ii] != '\0'; ++ii) + str[ii] = tolower(str[ii]); + return str; +} + int split_line(char *line, int irc_colon, int argv_size, char *argv[]) { int argc = 0; int n; while (*line && (argc < argv_size)) { - while (*line == ' ') + while (*line == ' ') *line++ = 0; - if (*line == ':' && irc_colon && argc > 0) { - /* the rest is a single parameter */ - argv[argc++] = line + 1; - break; - } + if (*line == ':' && irc_colon && argc > 0) { + /* the rest is a single parameter */ + argv[argc++] = line + 1; + break; + } if (!*line) break; - argv[argc++] = line; - if (argc >= argv_size) + argv[argc++] = line; + if (argc >= argv_size) break; - while (*line != ' ' && *line) + while (*line != ' ' && *line) line++; } #ifdef NDEBUG @@ -155,7 +440,7 @@ int mmatch(const char *old_mask, const char *new_mask) if (*m == '*') { while (*m == '*') - m++; + m++; wild = 1; ma = m; na = n; @@ -164,25 +449,25 @@ int mmatch(const char *old_mask, const char *new_mask) if (!*m) { if (!*n) - return 0; + return 0; for (m--; (m > old_mask) && (*m == '?'); m--) - ; + ; if ((*m == '*') && (m > old_mask) && (m[-1] != '\\')) - return 0; + return 0; if (!wild) - return 1; + return 1; m = ma; /* Added to `mmatch' : Because '\?' and '\*' now is one character: */ if ((*na == '\\') && ((na[1] == '*') || (na[1] == '?'))) - ++na; + ++na; n = ++na; } else if (!*n) { while (*m == '*') - m++; + m++; return (*m != 0); } if ((*m == '\\') && ((m[1] == '*') || (m[1] == '?'))) @@ -219,23 +504,23 @@ int mmatch(const char *old_mask, const char *new_mask) * cases upfront (which took 2 hours!)). */ if ((*m == '*' && !mq) || - ((!mq || nq) && tolower(*m) == tolower(*n)) || - (*m == '?' && !mq && (*n != '*' || nq))) + ((!mq || nq) && tolower(*m) == tolower(*n)) || + (*m == '?' && !mq && (*n != '*' || nq))) { if (*m) - m++; + m++; if (*n) - n++; + n++; } else { if (!wild) - return 1; + return 1; m = ma; /* Added to `mmatch' : Because '\?' and '\*' now is one character: */ if ((*na == '\\') && ((na[1] == '*') || (na[1] == '?'))) - ++na; + ++na; n = ++na; } @@ -245,80 +530,78 @@ int mmatch(const char *old_mask, const char *new_mask) int match_ircglob(const char *text, const char *glob) { - unsigned int star_p, q_cnt; - while (1) { - switch (*glob) { - case 0: - return !*text; - case '\\': - glob++; - /* intentionally not tolower(...) so people can force - * capitalization, or we can overload \ in the future */ - if (*text++ != *glob++) - return 0; - break; - case '*': - case '?': - star_p = q_cnt = 0; - do { - if (*glob == '*') - star_p = 1; - else if (*glob == '?') - q_cnt++; - else - break; - glob++; - } while (1); - while (q_cnt) { - if (!*text++) - return 0; - q_cnt--; - } - if (star_p) { - /* if this is the last glob character, it will match any text */ - if (!*glob) - return 1; - /* Thanks to the loop above, we know that the next - * character is a normal character. So just look for - * the right character. - */ - for (; *text; text++) { - if ((tolower(*text) == tolower(*glob)) - && match_ircglob(text+1, glob+1)) { - return 1; - } - } - return 0; - } - /* if !star_p, fall through to normal character case, - * first checking to see if ?s carried us to the end */ - if (!*glob && !*text) + const char *m = glob, *n = text; + const char *m_tmp = glob, *n_tmp = text; + int star_p; + + for (;;) switch (*m) { + case '\0': + if (!*n) + return 1; + backtrack: + if (m_tmp == glob) + return 0; + m = m_tmp; + n = ++n_tmp; + if (!*n) + return 0; + break; + case '\\': + m++; + /* allow escaping to force capitalization */ + if (*m++ != *n++) + goto backtrack; + break; + case '*': case '?': + for (star_p = 0; ; m++) { + if (*m == '*') + star_p = 1; + else if (*m == '?') { + if (!*n++) + goto backtrack; + } else break; + } + if (star_p) { + if (!*m) return 1; - default: - if (!*text) - return 0; - while (*text && *glob && *glob != '*' && *glob != '?' && *glob != '\\') { - if (tolower(*text++) != tolower(*glob++)) + else if (*m == '\\') { + m_tmp = ++m; + if (!*m) return 0; - } - } + for (n_tmp = n; *n && *n != *m; n++) ; + } else { + m_tmp = m; + for (n_tmp = n; *n && tolower(*n) != tolower(*m); n++) ; + } + } + /* and fall through */ + default: + if (!*n) + return *m == '\0'; + if (tolower(*m) != tolower(*n)) + goto backtrack; + m++; + n++; + break; } } extern const char *hidden_host_suffix; int -user_matches_glob(struct userNode *user, const char *orig_glob, int include_nick) +user_matches_glob(struct userNode *user, const char *orig_glob, int flags) { + irc_in_addr_t mask; char *glob, *marker; + unsigned char mask_bits; /* Make a writable copy of the glob */ glob = alloca(strlen(orig_glob)+1); strcpy(glob, orig_glob); /* Check the nick, if it's present */ - if (include_nick) { + if (flags & MATCH_USENICK) { if (!(marker = strchr(glob, '!'))) { - log_module(MAIN_LOG, LOG_ERROR, "user_matches_glob(\"%s\", \"%s\", %d) called, and glob doesn't include a '!'", user->nick, orig_glob, include_nick); + log_module(MAIN_LOG, LOG_ERROR, "user_matches_glob(\"%s\", \"%s\", %d) called, and glob doesn't include a '!'", user->nick, orig_glob, flags); return 0; } *marker = 0; @@ -327,20 +610,17 @@ user_matches_glob(struct userNode *user, const char *orig_glob, int include_nick } /* Check the ident */ if (!(marker = strchr(glob, '@'))) { - log_module(MAIN_LOG, LOG_ERROR, "user_matches_glob(\"%s\", \"%s\", %d) called, and glob doesn't include an '@'", user->nick, orig_glob, include_nick); + log_module(MAIN_LOG, LOG_ERROR, "user_matches_glob(\"%s\", \"%s\", %d) called, and glob doesn't include an '@'", user->nick, orig_glob, flags); return 0; } *marker = 0; - if (!match_ircglob(user->ident, glob)) + if (((IsFakeIdent(user) && IsHiddenHost(user) && (flags & MATCH_VISIBLE)) || !match_ircglob(user->ident, glob)) && + !(IsFakeIdent(user) && match_ircglob(user->fakeident, glob))) return 0; glob = marker + 1; - /* If it might be an IP glob, test that. */ - if (!glob[strspn(glob, "0123456789./*?")] - && match_ircglob(inet_ntoa(user->ip), glob)) - return 1; /* Check for a fakehost match. */ if (IsFakeHost(user) && match_ircglob(user->fakehost, glob)) - return 1; + return 1; /* Check for an account match. */ if (hidden_host_suffix && user->handle_info) { char hidden_host[HOSTLEN+1]; @@ -348,6 +628,14 @@ user_matches_glob(struct userNode *user, const char *orig_glob, int include_nick if (match_ircglob(hidden_host, glob)) return 1; } + /* If only matching the visible hostnames, bail early. */ + if ((flags & MATCH_VISIBLE) && IsHiddenHost(user) + && (IsFakeHost(user) || (hidden_host_suffix && user->handle_info))) + return 0; + /* If it might be an IP glob, test that. */ + if (irc_pton(&mask, &mask_bits, glob) + && irc_check_mask(&user->ip, &mask, mask_bits)) + return 1; /* None of the above; could only be a hostname match. */ return match_ircglob(user->hostname, glob); } @@ -378,7 +666,7 @@ is_gline(const char *text) return 0; if (!*text) return 0; - while (*text && (isalnum((char)*text) || strchr(".-?*", *text))) + while (*text && (isalnum((char)*text) || strchr(".-?*:", *text))) text++; return !*text; } @@ -405,9 +693,9 @@ split_ircmask(char *text, char **nick, char **ident, char **host) *text = 0; if (ident) *ident = start; - + start = ++text; - while (*text && (isalnum((char)*text) || strchr(".-?*", *text))) + while (*text && (isalnum((char)*text) || strchr(".-?*:", *text))) text++; if (host) *host = start; @@ -430,20 +718,20 @@ sanitize_ircmask(char *input) mask = input; while(*input++ != '!') { - length++; + length++; } if(length > NICKLEN) { - mask += NICKLEN; - *mask++ = '!'; + mask += NICKLEN; + *mask++ = '!'; - /* This flag is used to indicate following parts should - be shifted. */ - flag = 1; + /* This flag is used to indicate following parts should + be shifted. */ + flag = 1; } else { - mask = input; + mask = input; } /* The ident and host must be truncated at the beginning and @@ -452,46 +740,46 @@ sanitize_ircmask(char *input) start = input; while(*input++ != '@') { - length++; + length++; } if(length > USERLEN || flag) { - if(length > USERLEN) - { - start = input - USERLEN; - *mask++ = '*'; - } - while(*start != '@') - { - *mask++ = *start++; - } - *mask++ = '@'; - - flag = 1; + if(length > USERLEN) + { + start = input - USERLEN; + *mask++ = '*'; + } + while(*start != '@') + { + *mask++ = *start++; + } + *mask++ = '@'; + + flag = 1; } else { - mask = input; + mask = input; } length = 0; start = input; while(*input++) { - length++; + length++; } if(length > HOSTLEN || flag) { - if(length > HOSTLEN) - { - start = input - HOSTLEN; - *mask++ = '*'; - } - while(*start) - { - *mask++ = *start++; - } - *mask = '\0'; + if(length > HOSTLEN) + { + start = input - HOSTLEN; + *mask++ = '*'; + } + while(*start) + { + *mask++ = *start++; + } + *mask = '\0'; } return output; @@ -521,12 +809,14 @@ ParseInterval(const char *interval) /* process the string, resetting the count if we find a unit character */ while ((c = *interval++)) { - if (isdigit((int)c)) { - partial = partial*10 + c - '0'; - } else { - seconds += TypeLength(c) * partial; - partial = 0; - } + if (isdigit((int)c)) { + partial = partial*10 + c - '0'; + } else if (strchr("yMwdhms", c)) { + seconds += TypeLength(c) * partial; + partial = 0; + } else { + return 0; + } } /* assume the last chunk is seconds (the normal case) */ return seconds + partial; @@ -560,74 +850,6 @@ ParseVolume(const char *volume) return accum + partial; } -int -parse_ipmask(const char *str, struct in_addr *addr, unsigned long *mask) -{ - int accum, pos; - unsigned long t_a, t_m; - - t_a = t_m = pos = 0; - if (addr) - addr->s_addr = htonl(t_a); - if (mask) - *mask = t_m; - while (*str) { - if (!isdigit(*str)) - return 0; - accum = 0; - do { - accum = (accum * 10) + *str++ - '0'; - } while (isdigit(*str)); - if (accum > 255) - return 0; - t_a = (t_a << 8) | accum; - t_m = (t_m << 8) | 255; - pos += 8; - if (*str == '.') { - str++; - while (*str == '*') { - str++; - if (*str == '.') { - t_a <<= 8; - t_m <<= 8; - pos += 8; - str++; - } else if (*str == 0) { - t_a <<= 32 - pos; - t_m <<= 32 - pos; - pos = 32; - goto out; - } else - return 0; - } - } else if (*str == '/') { - int start = pos; - accum = 0; - do { - accum = (accum * 10) + *str++ - '0'; - } while (isdigit(*str)); - while (pos < start+accum && pos < 32) { - t_a = (t_a << 1) | 0; - t_m = (t_m << 1) | 1; - pos++; - } - if (pos != start+accum) - return 0; - } else if (*str == 0) - break; - else - return 0; - } -out: - if (pos != 32) - return 0; - if (addr) - addr->s_addr = htonl(t_a); - if (mask) - *mask = t_m; - return 1; -} - char * unsplit_string(char *set[], unsigned int max, char *dest) { @@ -646,12 +868,12 @@ unsplit_string(char *set[], unsigned int max, char *dest) } char * -intervalString(char *output, time_t interval, struct handle_info *hi) +intervalString(char *output, unsigned long interval, struct handle_info *hi) { static const struct { const char *msg_single; const char *msg_plural; - long length; + unsigned long length; } unit[] = { { "MSG_YEAR", "MSG_YEARS", 365 * 24 * 60 * 60 }, { "MSG_WEEK", "MSG_WEEKS", 7 * 24 * 60 * 60 }, @@ -668,13 +890,13 @@ intervalString(char *output, time_t interval, struct handle_info *hi) if(!interval) { msg = language_find_message(lang, "MSG_0_SECONDS"); - return strcpy(output, msg); + return strcpy(output, msg); } for (type = 0, words = pos = 0; interval && (words < 2) && (type < ArrayLength(unit)); type++) { - if (interval < unit[type].length) + if (interval < unit[type].length) continue; count = interval / unit[type].length; interval = interval % unit[type].length;