diff options
Diffstat (limited to 'src/backend/utils/adt/inet_net_pton.c')
-rw-r--r-- | src/backend/utils/adt/inet_net_pton.c | 564 |
1 files changed, 564 insertions, 0 deletions
diff --git a/src/backend/utils/adt/inet_net_pton.c b/src/backend/utils/adt/inet_net_pton.c new file mode 100644 index 0000000..d3221a1 --- /dev/null +++ b/src/backend/utils/adt/inet_net_pton.c @@ -0,0 +1,564 @@ +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1996,1999 by Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT + * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * src/backend/utils/adt/inet_net_pton.c + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static const char rcsid[] = "Id: inet_net_pton.c,v 1.4.2.3 2004/03/17 00:40:11 marka Exp $"; +#endif + +#include "postgres.h" + +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <assert.h> +#include <ctype.h> + +#include "utils/builtins.h" /* pgrminclude ignore */ /* needed on some + * platforms */ +#include "utils/inet.h" + + +static int inet_net_pton_ipv4(const char *src, u_char *dst); +static int inet_cidr_pton_ipv4(const char *src, u_char *dst, size_t size); +static int inet_net_pton_ipv6(const char *src, u_char *dst); +static int inet_cidr_pton_ipv6(const char *src, u_char *dst, size_t size); + + +/* + * int + * pg_inet_net_pton(af, src, dst, size) + * convert network number from presentation to network format. + * accepts hex octets, hex strings, decimal octets, and /CIDR. + * "size" is in bytes and describes "dst". + * return: + * number of bits, either imputed classfully or specified with /CIDR, + * or -1 if some failure occurred (check errno). ENOENT means it was + * not a valid network specification. + * author: + * Paul Vixie (ISC), June 1996 + * + * Changes: + * I added the inet_cidr_pton function (also from Paul) and changed + * the names to reflect their current use. + * + */ +int +pg_inet_net_pton(int af, const char *src, void *dst, size_t size) +{ + switch (af) + { + case PGSQL_AF_INET: + return size == -1 ? + inet_net_pton_ipv4(src, dst) : + inet_cidr_pton_ipv4(src, dst, size); + case PGSQL_AF_INET6: + return size == -1 ? + inet_net_pton_ipv6(src, dst) : + inet_cidr_pton_ipv6(src, dst, size); + default: + errno = EAFNOSUPPORT; + return -1; + } +} + +/* + * static int + * inet_cidr_pton_ipv4(src, dst, size) + * convert IPv4 network number from presentation to network format. + * accepts hex octets, hex strings, decimal octets, and /CIDR. + * "size" is in bytes and describes "dst". + * return: + * number of bits, either imputed classfully or specified with /CIDR, + * or -1 if some failure occurred (check errno). ENOENT means it was + * not an IPv4 network specification. + * note: + * network byte order assumed. this means 192.5.5.240/28 has + * 0b11110000 in its fourth octet. + * author: + * Paul Vixie (ISC), June 1996 + */ +static int +inet_cidr_pton_ipv4(const char *src, u_char *dst, size_t size) +{ + static const char xdigits[] = "0123456789abcdef"; + static const char digits[] = "0123456789"; + int n, + ch, + tmp = 0, + dirty, + bits; + const u_char *odst = dst; + + ch = *src++; + if (ch == '0' && (src[0] == 'x' || src[0] == 'X') + && isxdigit((unsigned char) src[1])) + { + /* Hexadecimal: Eat nybble string. */ + if (size <= 0U) + goto emsgsize; + dirty = 0; + src++; /* skip x or X. */ + while ((ch = *src++) != '\0' && isxdigit((unsigned char) ch)) + { + if (isupper((unsigned char) ch)) + ch = tolower((unsigned char) ch); + n = strchr(xdigits, ch) - xdigits; + assert(n >= 0 && n <= 15); + if (dirty == 0) + tmp = n; + else + tmp = (tmp << 4) | n; + if (++dirty == 2) + { + if (size-- <= 0U) + goto emsgsize; + *dst++ = (u_char) tmp; + dirty = 0; + } + } + if (dirty) + { /* Odd trailing nybble? */ + if (size-- <= 0U) + goto emsgsize; + *dst++ = (u_char) (tmp << 4); + } + } + else if (isdigit((unsigned char) ch)) + { + /* Decimal: eat dotted digit string. */ + for (;;) + { + tmp = 0; + do + { + n = strchr(digits, ch) - digits; + assert(n >= 0 && n <= 9); + tmp *= 10; + tmp += n; + if (tmp > 255) + goto enoent; + } while ((ch = *src++) != '\0' && + isdigit((unsigned char) ch)); + if (size-- <= 0U) + goto emsgsize; + *dst++ = (u_char) tmp; + if (ch == '\0' || ch == '/') + break; + if (ch != '.') + goto enoent; + ch = *src++; + if (!isdigit((unsigned char) ch)) + goto enoent; + } + } + else + goto enoent; + + bits = -1; + if (ch == '/' && isdigit((unsigned char) src[0]) && dst > odst) + { + /* CIDR width specifier. Nothing can follow it. */ + ch = *src++; /* Skip over the /. */ + bits = 0; + do + { + n = strchr(digits, ch) - digits; + assert(n >= 0 && n <= 9); + bits *= 10; + bits += n; + } while ((ch = *src++) != '\0' && isdigit((unsigned char) ch)); + if (ch != '\0') + goto enoent; + if (bits > 32) + goto emsgsize; + } + + /* Fiery death and destruction unless we prefetched EOS. */ + if (ch != '\0') + goto enoent; + + /* If nothing was written to the destination, we found no address. */ + if (dst == odst) + goto enoent; + /* If no CIDR spec was given, infer width from net class. */ + if (bits == -1) + { + if (*odst >= 240) /* Class E */ + bits = 32; + else if (*odst >= 224) /* Class D */ + bits = 8; + else if (*odst >= 192) /* Class C */ + bits = 24; + else if (*odst >= 128) /* Class B */ + bits = 16; + else + /* Class A */ + bits = 8; + /* If imputed mask is narrower than specified octets, widen. */ + if (bits < ((dst - odst) * 8)) + bits = (dst - odst) * 8; + + /* + * If there are no additional bits specified for a class D address + * adjust bits to 4. + */ + if (bits == 8 && *odst == 224) + bits = 4; + } + /* Extend network to cover the actual mask. */ + while (bits > ((dst - odst) * 8)) + { + if (size-- <= 0U) + goto emsgsize; + *dst++ = '\0'; + } + return bits; + +enoent: + errno = ENOENT; + return -1; + +emsgsize: + errno = EMSGSIZE; + return -1; +} + +/* + * int + * inet_net_pton_ipv4(af, src, dst, *bits) + * convert network address from presentation to network format. + * accepts inet_pton()'s input for this "af" plus trailing "/CIDR". + * "dst" is assumed large enough for its "af". "bits" is set to the + * /CIDR prefix length, which can have defaults (like /32 for IPv4). + * return: + * -1 if an error occurred (inspect errno; ENOENT means bad format). + * 0 if successful conversion occurred. + * note: + * 192.5.5.1/28 has a nonzero host part, which means it isn't a network + * as called for by inet_cidr_pton() but it can be a host address with + * an included netmask. + * author: + * Paul Vixie (ISC), October 1998 + */ +static int +inet_net_pton_ipv4(const char *src, u_char *dst) +{ + static const char digits[] = "0123456789"; + const u_char *odst = dst; + int n, + ch, + tmp, + bits; + size_t size = 4; + + /* Get the mantissa. */ + while (ch = *src++, isdigit((unsigned char) ch)) + { + tmp = 0; + do + { + n = strchr(digits, ch) - digits; + assert(n >= 0 && n <= 9); + tmp *= 10; + tmp += n; + if (tmp > 255) + goto enoent; + } while ((ch = *src++) != '\0' && isdigit((unsigned char) ch)); + if (size-- == 0) + goto emsgsize; + *dst++ = (u_char) tmp; + if (ch == '\0' || ch == '/') + break; + if (ch != '.') + goto enoent; + } + + /* Get the prefix length if any. */ + bits = -1; + if (ch == '/' && isdigit((unsigned char) src[0]) && dst > odst) + { + /* CIDR width specifier. Nothing can follow it. */ + ch = *src++; /* Skip over the /. */ + bits = 0; + do + { + n = strchr(digits, ch) - digits; + assert(n >= 0 && n <= 9); + bits *= 10; + bits += n; + } while ((ch = *src++) != '\0' && isdigit((unsigned char) ch)); + if (ch != '\0') + goto enoent; + if (bits > 32) + goto emsgsize; + } + + /* Fiery death and destruction unless we prefetched EOS. */ + if (ch != '\0') + goto enoent; + + /* Prefix length can default to /32 only if all four octets spec'd. */ + if (bits == -1) + { + if (dst - odst == 4) + bits = 32; + else + goto enoent; + } + + /* If nothing was written to the destination, we found no address. */ + if (dst == odst) + goto enoent; + + /* If prefix length overspecifies mantissa, life is bad. */ + if ((bits / 8) > (dst - odst)) + goto enoent; + + /* Extend address to four octets. */ + while (size-- > 0) + *dst++ = 0; + + return bits; + +enoent: + errno = ENOENT; + return -1; + +emsgsize: + errno = EMSGSIZE; + return -1; +} + +static int +getbits(const char *src, int *bitsp) +{ + static const char digits[] = "0123456789"; + int n; + int val; + char ch; + + val = 0; + n = 0; + while ((ch = *src++) != '\0') + { + const char *pch; + + pch = strchr(digits, ch); + if (pch != NULL) + { + if (n++ != 0 && val == 0) /* no leading zeros */ + return 0; + val *= 10; + val += (pch - digits); + if (val > 128) /* range */ + return 0; + continue; + } + return 0; + } + if (n == 0) + return 0; + *bitsp = val; + return 1; +} + +static int +getv4(const char *src, u_char *dst, int *bitsp) +{ + static const char digits[] = "0123456789"; + u_char *odst = dst; + int n; + u_int val; + char ch; + + val = 0; + n = 0; + while ((ch = *src++) != '\0') + { + const char *pch; + + pch = strchr(digits, ch); + if (pch != NULL) + { + if (n++ != 0 && val == 0) /* no leading zeros */ + return 0; + val *= 10; + val += (pch - digits); + if (val > 255) /* range */ + return 0; + continue; + } + if (ch == '.' || ch == '/') + { + if (dst - odst > 3) /* too many octets? */ + return 0; + *dst++ = val; + if (ch == '/') + return getbits(src, bitsp); + val = 0; + n = 0; + continue; + } + return 0; + } + if (n == 0) + return 0; + if (dst - odst > 3) /* too many octets? */ + return 0; + *dst++ = val; + return 1; +} + +static int +inet_net_pton_ipv6(const char *src, u_char *dst) +{ + return inet_cidr_pton_ipv6(src, dst, 16); +} + +#define NS_IN6ADDRSZ 16 +#define NS_INT16SZ 2 +#define NS_INADDRSZ 4 + +static int +inet_cidr_pton_ipv6(const char *src, u_char *dst, size_t size) +{ + static const char xdigits_l[] = "0123456789abcdef", + xdigits_u[] = "0123456789ABCDEF"; + u_char tmp[NS_IN6ADDRSZ], + *tp, + *endp, + *colonp; + const char *xdigits, + *curtok; + int ch, + saw_xdigit; + u_int val; + int digits; + int bits; + + if (size < NS_IN6ADDRSZ) + goto emsgsize; + + memset((tp = tmp), '\0', NS_IN6ADDRSZ); + endp = tp + NS_IN6ADDRSZ; + colonp = NULL; + /* Leading :: requires some special handling. */ + if (*src == ':') + if (*++src != ':') + goto enoent; + curtok = src; + saw_xdigit = 0; + val = 0; + digits = 0; + bits = -1; + while ((ch = *src++) != '\0') + { + const char *pch; + + if ((pch = strchr((xdigits = xdigits_l), ch)) == NULL) + pch = strchr((xdigits = xdigits_u), ch); + if (pch != NULL) + { + val <<= 4; + val |= (pch - xdigits); + if (++digits > 4) + goto enoent; + saw_xdigit = 1; + continue; + } + if (ch == ':') + { + curtok = src; + if (!saw_xdigit) + { + if (colonp) + goto enoent; + colonp = tp; + continue; + } + else if (*src == '\0') + goto enoent; + if (tp + NS_INT16SZ > endp) + goto enoent; + *tp++ = (u_char) (val >> 8) & 0xff; + *tp++ = (u_char) val & 0xff; + saw_xdigit = 0; + digits = 0; + val = 0; + continue; + } + if (ch == '.' && ((tp + NS_INADDRSZ) <= endp) && + getv4(curtok, tp, &bits) > 0) + { + tp += NS_INADDRSZ; + saw_xdigit = 0; + break; /* '\0' was seen by inet_pton4(). */ + } + if (ch == '/' && getbits(src, &bits) > 0) + break; + goto enoent; + } + if (saw_xdigit) + { + if (tp + NS_INT16SZ > endp) + goto enoent; + *tp++ = (u_char) (val >> 8) & 0xff; + *tp++ = (u_char) val & 0xff; + } + if (bits == -1) + bits = 128; + + endp = tmp + 16; + + if (colonp != NULL) + { + /* + * Since some memmove()'s erroneously fail to handle overlapping + * regions, we'll do the shift by hand. + */ + const int n = tp - colonp; + int i; + + if (tp == endp) + goto enoent; + for (i = 1; i <= n; i++) + { + endp[-i] = colonp[n - i]; + colonp[n - i] = 0; + } + tp = endp; + } + if (tp != endp) + goto enoent; + + /* + * Copy out the result. + */ + memcpy(dst, tmp, NS_IN6ADDRSZ); + + return bits; + +enoent: + errno = ENOENT; + return -1; + +emsgsize: + errno = EMSGSIZE; + return -1; +} |