/* * General purpose functions. * * Copyright 2000-2007 Willy Tarreau * * 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 * 2 of the License, or (at your option) any later version. * */ #include #include #include #include #include #include #include #include #include #include #include /* enough to store 10 integers of : * 2^64-1 = 18446744073709551615 or * -2^63 = -9223372036854775808 * * The HTML version needs room for adding the 25 characters * '' around digits at positions 3N+1 in order * to add spacing at up to 6 positions : 18 446 744 073 709 551 615 */ char itoa_str[10][171]; /* * copies at most chars from to . Last char is always * set to 0, unless is 0. The number of chars copied is returned * (excluding the terminating zero). * This code has been optimized for size and speed : on x86, it's 45 bytes * long, uses only registers, and consumes only 4 cycles per char. */ int strlcpy2(char *dst, const char *src, int size) { char *orig = dst; if (size) { while (--size && (*dst = *src)) { src++; dst++; } *dst = 0; } return dst - orig; } /* * This function simply returns a locally allocated string containing * the ascii representation for number 'n' in decimal. */ const char *ultoa_r(unsigned long n, char *buffer, int size) { char *pos; pos = buffer + size - 1; *pos-- = '\0'; do { *pos-- = '0' + n % 10; n /= 10; } while (n && pos >= buffer); return pos + 1; } /* * This function simply returns a locally allocated string containing * the ascii representation for number 'n' in decimal, formatted for * HTML output with tags to create visual grouping by 3 digits. The * output needs to support at least 171 characters. */ const char *ulltoh_r(unsigned long long n, char *buffer, int size) { char *start; int digit = 0; start = buffer + size; *--start = '\0'; do { if (digit == 3 && start >= buffer + 7) memcpy(start -= 7, "", 7); if (start >= buffer + 1) { *--start = '0' + n % 10; n /= 10; } if (digit == 3 && start >= buffer + 18) memcpy(start -= 18, "", 18); if (digit++ == 3) digit = 1; } while (n && start > buffer); return start; } /* * This function simply returns a locally allocated string containing the ascii * representation for number 'n' in decimal, unless n is 0 in which case it * returns the alternate string (or an empty string if the alternate string is * NULL). It use is intended for limits reported in reports, where it's * desirable not to display anything if there is no limit. Warning! it shares * the same vector as ultoa_r(). */ const char *limit_r(unsigned long n, char *buffer, int size, const char *alt) { return (n) ? ultoa_r(n, buffer, size) : (alt ? alt : ""); } /* * converts to a struct sockaddr_un* which is locally allocated. * The format is "/path", where "/path" is a path to a UNIX domain socket. */ struct sockaddr_un *str2sun(const char *str) { static struct sockaddr_un su; int strsz; /* length included null */ memset(&su, 0, sizeof(su)); strsz = strlen(str) + 1; if (strsz > sizeof(su.sun_path)) { Alert("Socket path '%s' too long (max %d)\n", str, sizeof(su.sun_path) - 1); } else { su.sun_family = AF_UNIX; memcpy(su.sun_path, str, strsz); } return &su; } /* * Returns non-zero if character is a hex digit (0-9, a-f, A-F), else zero. * * It looks like this one would be a good candidate for inlining, but this is * not interesting because it around 35 bytes long and often called multiple * times within the same function. */ int ishex(char s) { s -= '0'; if ((unsigned char)s <= 9) return 1; s -= 'A' - '0'; if ((unsigned char)s <= 5) return 1; s -= 'a' - 'A'; if ((unsigned char)s <= 5) return 1; return 0; } /* * Checks for invalid characters. Valid chars are [A-Za-z0-9_:.-]. If an * invalid character is found, a pointer to it is returned. If everything is * fine, NULL is returned. */ const char *invalid_char(const char *name) { if (!*name) return name; while (*name) { if (!isalnum((int)*name) && *name != '.' && *name != ':' && *name != '_' && *name != '-') return name; name++; } return NULL; } /* * Checks for invalid characters. Valid chars are [A-Za-z0-9_.-]. * If an invalid character is found, a pointer to it is returned. * If everything is fine, NULL is returned. */ const char *invalid_domainchar(const char *name) { if (!*name) return name; while (*name) { if (!isalnum((int)*name) && *name != '.' && *name != '_' && *name != '-') return name; name++; } return NULL; } /* * converts to a struct sockaddr_in* which is locally allocated. * The format is "addr:port", where "addr" can be a dotted IPv4 address, * a host name, or empty or "*" to indicate INADDR_ANY. */ struct sockaddr_in *str2sa(char *str) { static struct sockaddr_in sa; char *c; int port; memset(&sa, 0, sizeof(sa)); str = strdup(str); if (str == NULL) goto out_nofree; if ((c = strrchr(str,':')) != NULL) { *c++ = '\0'; port = atol(c); } else port = 0; if (*str == '*' || *str == '\0') { /* INADDR_ANY */ sa.sin_addr.s_addr = INADDR_ANY; } else if (!inet_pton(AF_INET, str, &sa.sin_addr)) { struct hostent *he; if ((he = gethostbyname(str)) == NULL) { Alert("Invalid server name: '%s'\n", str); } else sa.sin_addr = *(struct in_addr *) *(he->h_addr_list); } sa.sin_port = htons(port); sa.sin_family = AF_INET; free(str); out_nofree: return &sa; } /* * converts to two struct in_addr* which must be pre-allocated. * The format is "addr[/mask]", where "addr" cannot be empty, and mask * is optionnal and either in the dotted or CIDR notation. * Note: "addr" can also be a hostname. Returns 1 if OK, 0 if error. */ int str2net(const char *str, struct in_addr *addr, struct in_addr *mask) { __label__ out_free, out_err; char *c, *s; int ret_val; unsigned long len; s = strdup(str); if (!s) return 0; memset(mask, 0, sizeof(*mask)); memset(addr, 0, sizeof(*addr)); if ((c = strrchr(s, '/')) != NULL) { *c++ = '\0'; /* c points to the mask */ if (strchr(c, '.') != NULL) { /* dotted notation */ if (!inet_pton(AF_INET, c, mask)) goto out_err; } else { /* mask length */ char *err; len = strtol(c, &err, 10); if (!*c || (err && *err) || (unsigned)len > 32) goto out_err; if (len) mask->s_addr = htonl(~0UL << (32 - len)); else mask->s_addr = 0; } } else { mask->s_addr = ~0U; } if (!inet_pton(AF_INET, s, addr)) { struct hostent *he; if ((he = gethostbyname(s)) == NULL) { goto out_err; } else *addr = *(struct in_addr *) *(he->h_addr_list); } ret_val = 1; out_free: free(s); return ret_val; out_err: ret_val = 0; goto out_free; } /* * Parse IP address found in url. */ static int url2ip(const char *addr, struct in_addr *dst) { int saw_digit, octets, ch; u_char tmp[4], *tp; const char *cp = addr; saw_digit = 0; octets = 0; *(tp = tmp) = 0; while (*addr) { unsigned char digit = (ch = *addr++) - '0'; if (digit > 9 && ch != '.') break; if (digit <= 9) { u_int new = *tp * 10 + digit; if (new > 255) return 0; *tp = new; if (!saw_digit) { if (++octets > 4) return 0; saw_digit = 1; } } else if (ch == '.' && saw_digit) { if (octets == 4) return 0; *++tp = 0; saw_digit = 0; } else return 0; } if (octets < 4) return 0; memcpy(&dst->s_addr, tmp, 4); return addr-cp-1; } /* * Resolve destination server from URL. Convert to a sockaddr_in*. */ int url2sa(const char *url, int ulen, struct sockaddr_in *addr) { const char *curr = url, *cp = url; int ret, url_code = 0; unsigned int http_code = 0; /* Cleanup the room */ addr->sin_family = AF_INET; addr->sin_addr.s_addr = 0; addr->sin_port = 0; /* Firstly, try to find :// pattern */ while (curr < url+ulen && url_code != 0x3a2f2f) { url_code = ((url_code & 0xffff) << 8); url_code += (unsigned char)*curr++; } /* Secondly, if :// pattern is found, verify parsed stuff * before pattern is matching our http pattern. * If so parse ip address and port in uri. * * WARNING: Current code doesn't support dynamic async dns resolver. */ if (url_code == 0x3a2f2f) { while (cp < curr - 3) http_code = (http_code << 8) + *cp++; http_code |= 0x20202020; /* Turn everything to lower case */ /* HTTP url matching */ if (http_code == 0x68747470) { /* We are looking for IP address. If you want to parse and * resolve hostname found in url, you can use str2sa(), but * be warned this can slow down global daemon performances * while handling lagging dns responses. */ ret = url2ip(curr, &addr->sin_addr); if (!ret) return -1; curr += ret; addr->sin_port = (*curr == ':') ? str2uic(++curr) : 80; addr->sin_port = htons(addr->sin_port); } return 0; } return -1; } /* will try to encode the string replacing all characters tagged in * with the hexadecimal representation of their ASCII-code (2 digits) * prefixed by , and will store the result between (included) * and (excluded), and will always terminate the string with a '\0' * before . The position of the '\0' is returned if the conversion * completes. If bytes are missing between and , then the * conversion will be incomplete and truncated. If <= , the '\0' * cannot even be stored so we return without writing the 0. * The input string must also be zero-terminated. */ const char hextab[16] = "0123456789ABCDEF"; char *encode_string(char *start, char *stop, const char escape, const fd_set *map, const char *string) { if (start < stop) { stop--; /* reserve one byte for the final '\0' */ while (start < stop && *string != '\0') { if (!FD_ISSET((unsigned char)(*string), map)) *start++ = *string; else { if (start + 3 >= stop) break; *start++ = escape; *start++ = hextab[(*string >> 4) & 15]; *start++ = hextab[*string & 15]; } string++; } *start = '\0'; } return start; } unsigned int str2ui(const char *s) { return __str2ui(s); } unsigned int str2uic(const char *s) { return __str2uic(s); } unsigned int strl2ui(const char *s, int len) { return __strl2ui(s, len); } unsigned int strl2uic(const char *s, int len) { return __strl2uic(s, len); } /* This one is 7 times faster than strtol() on athlon with checks. * It returns the value of the number composed of all valid digits read, * and can process negative numbers too. */ int strl2ic(const char *s, int len) { int i = 0; int j, k; if (len > 0) { if (*s != '-') { /* positive number */ while (len-- > 0) { j = (*s++) - '0'; k = i * 10; if (j > 9) break; i = k + j; } } else { /* negative number */ s++; while (--len > 0) { j = (*s++) - '0'; k = i * 10; if (j > 9) break; i = k - j; } } } return i; } /* This function reads exactly chars from and converts them to a * signed integer which it stores into . It accurately detects any error * (truncated string, invalid chars, overflows). It is meant to be used in * applications designed for hostile environments. It returns zero when the * number has successfully been converted, non-zero otherwise. When an error * is returned, the value is left untouched. It is yet 5 to 40 times * faster than strtol(). */ int strl2irc(const char *s, int len, int *ret) { int i = 0; int j; if (!len) return 1; if (*s != '-') { /* positive number */ while (len-- > 0) { j = (*s++) - '0'; if (j > 9) return 1; /* invalid char */ if (i > INT_MAX / 10) return 1; /* check for multiply overflow */ i = i * 10; if (i + j < i) return 1; /* check for addition overflow */ i = i + j; } } else { /* negative number */ s++; while (--len > 0) { j = (*s++) - '0'; if (j > 9) return 1; /* invalid char */ if (i < INT_MIN / 10) return 1; /* check for multiply overflow */ i = i * 10; if (i - j > i) return 1; /* check for subtract overflow */ i = i - j; } } *ret = i; return 0; } /* This function reads exactly chars from and converts them to a * signed integer which it stores into . It accurately detects any error * (truncated string, invalid chars, overflows). It is meant to be used in * applications designed for hostile environments. It returns zero when the * number has successfully been converted, non-zero otherwise. When an error * is returned, the value is left untouched. It is about 3 times slower * than str2irc(). */ #ifndef LLONG_MAX #define LLONG_MAX 9223372036854775807LL #define LLONG_MIN (-LLONG_MAX - 1LL) #endif int strl2llrc(const char *s, int len, long long *ret) { long long i = 0; int j; if (!len) return 1; if (*s != '-') { /* positive number */ while (len-- > 0) { j = (*s++) - '0'; if (j > 9) return 1; /* invalid char */ if (i > LLONG_MAX / 10LL) return 1; /* check for multiply overflow */ i = i * 10LL; if (i + j < i) return 1; /* check for addition overflow */ i = i + j; } } else { /* negative number */ s++; while (--len > 0) { j = (*s++) - '0'; if (j > 9) return 1; /* invalid char */ if (i < LLONG_MIN / 10LL) return 1; /* check for multiply overflow */ i = i * 10LL; if (i - j > i) return 1; /* check for subtract overflow */ i = i - j; } } *ret = i; return 0; } /* This function parses a time value optionally followed by a unit suffix among * "d", "h", "m", "s", "ms" or "us". It converts the value into the unit * expected by the caller. The computation does its best to avoid overflows. * The value is returned in if everything is fine, and a NULL is returned * by the function. In case of error, a pointer to the error is returned and * is left untouched. Values are automatically rounded up when needed. */ const char *parse_time_err(const char *text, unsigned *ret, unsigned unit_flags) { unsigned imult, idiv; unsigned omult, odiv; unsigned value; omult = odiv = 1; switch (unit_flags & TIME_UNIT_MASK) { case TIME_UNIT_US: omult = 1000000; break; case TIME_UNIT_MS: omult = 1000; break; case TIME_UNIT_S: break; case TIME_UNIT_MIN: odiv = 60; break; case TIME_UNIT_HOUR: odiv = 3600; break; case TIME_UNIT_DAY: odiv = 86400; break; default: break; } value = 0; while (1) { unsigned int j; j = *text - '0'; if (j > 9) break; text++; value *= 10; value += j; } imult = idiv = 1; switch (*text) { case '\0': /* no unit = default unit */ imult = omult = idiv = odiv = 1; break; case 's': /* second = unscaled unit */ break; case 'u': /* microsecond : "us" */ if (text[1] == 's') { idiv = 1000000; text++; } break; case 'm': /* millisecond : "ms" or minute: "m" */ if (text[1] == 's') { idiv = 1000; text++; } else imult = 60; break; case 'h': /* hour : "h" */ imult = 3600; break; case 'd': /* day : "d" */ imult = 86400; break; default: return text; break; } if (omult % idiv == 0) { omult /= idiv; idiv = 1; } if (idiv % omult == 0) { idiv /= omult; omult = 1; } if (imult % odiv == 0) { imult /= odiv; odiv = 1; } if (odiv % imult == 0) { odiv /= imult; imult = 1; } value = (value * (imult * omult) + (idiv * odiv - 1)) / (idiv * odiv); *ret = value; return NULL; } /* * Local variables: * c-indent-level: 8 * c-basic-offset: 8 * End: */