/src/backend/utils/adt/inet_net_pton.c
C | 564 lines | 420 code | 46 blank | 98 comment | 172 complexity | cb52a5f12818c77143f27382453ba831 MD5 | raw file
Possible License(s): LGPL-2.0, AGPL-3.0
- /*
- * 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.
- *
- * $PostgreSQL$
- */
- #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/types.h>
- #include <sys/socket.h>
- #include <netinet/in.h>
- #include <arpa/inet.h>
- #include <assert.h>
- #include <ctype.h>
- #include "utils/builtins.h"
- #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
- * 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
- 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;
- }
- /* Firey 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(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;
- }
- /* Firey 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)
- return (0);
- *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);
- }