summaryrefslogtreecommitdiffstats
path: root/src/backend/utils/adt/inet_net_pton.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/utils/adt/inet_net_pton.c')
-rw-r--r--src/backend/utils/adt/inet_net_pton.c564
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;
+}