/*
- * Copyright (c) 1996 by Internet Software Consortium.
+ * 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 INTERNET SOFTWARE CONSORTIUM DISCLAIMS
- * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
- * CONSORTIUM 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.
+ * 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 1998/10/12 15:56:34 momjian Exp $";
-
+static const char rcsid[] = "Id: inet_net_pton.c,v 1.4.2.3 2004/03/17 00:40:11 marka Exp $";
#endif
-#include <sys/types.h>
+#include "postgres.h"
+
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
-
#include <assert.h>
#include <ctype.h>
-#include <errno.h>
-#include <stdio.h>
-#include <string.h>
-#include <stdlib.h>
-
-#include <postgres.h>
-#include <utils/builtins.h>
-
-#ifdef SPRINTF_CHAR
-#define SPRINTF(x) strlen(sprintf/**/x)
-#else
-#define SPRINTF(x) ((size_t)sprintf x)
-#endif
-static int inet_net_pton_ipv4(const char *src, u_char *dst, size_t size);
+#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);
+
/*
- * static int
+ * 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.
* 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 AF_INET:
- return (inet_net_pton_ipv4(src, dst, size));
+ 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_net_pton_ipv4(src, dst, size)
+ * 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".
* not an IPv4 network specification.
* note:
* network byte order assumed. this means 192.5.5.240/28 has
- * 0x11110000 in its fourth octet.
+ * 0b11110000 in its fourth octet.
* author:
* Paul Vixie (ISC), June 1996
*/
static int
-inet_net_pton_ipv4(const char *src, u_char *dst, size_t size)
+inet_cidr_pton_ipv4(const char *src, u_char *dst, size_t size)
{
- static const char
- xdigits[] = "0123456789abcdef",
- digits[] = "0123456789";
+ static const char xdigits[] = "0123456789abcdef";
+ static const char digits[] = "0123456789";
int n,
ch,
- tmp,
+ tmp = 0,
dirty,
bits;
const u_char *odst = dst;
ch = *src++;
if (ch == '0' && (src[0] == 'x' || src[0] == 'X')
- && isascii(src[1]) && isxdigit(src[1]))
+ && isxdigit((unsigned char) src[1]))
{
/* Hexadecimal: Eat nybble string. */
- if (size <= 0)
+ if (size <= 0U)
goto emsgsize;
- tmp = 0;
dirty = 0;
src++; /* skip x or X. */
- while ((ch = *src++) != '\0' &&
- isascii(ch) && isxdigit(ch))
+ while ((ch = *src++) != '\0' && isxdigit((unsigned char) ch))
{
- if (isupper(ch))
- ch = tolower(ch);
+ if (isupper((unsigned char) ch))
+ ch = tolower((unsigned char) ch);
n = strchr(xdigits, ch) - xdigits;
assert(n >= 0 && n <= 15);
- tmp = (tmp << 4) | n;
- if (++dirty == 2) {
- if (size-- <= 0)
+ if (dirty == 0)
+ tmp = n;
+ else
+ tmp = (tmp << 4) | n;
+ if (++dirty == 2)
+ {
+ if (size-- <= 0U)
goto emsgsize;
*dst++ = (u_char) tmp;
- tmp = 0, dirty = 0;
+ dirty = 0;
}
}
- if (dirty) {
- if (size-- <= 0)
+ if (dirty)
+ { /* Odd trailing nybble? */
+ if (size-- <= 0U)
goto emsgsize;
- tmp <<= 4;
- *dst++ = (u_char) tmp;
+ *dst++ = (u_char) (tmp << 4);
}
}
- else if (isascii(ch) && isdigit(ch))
+ else if (isdigit((unsigned char) ch))
{
/* Decimal: eat dotted digit string. */
for (;;)
if (tmp > 255)
goto enoent;
} while ((ch = *src++) != '\0' &&
- isascii(ch) && isdigit(ch));
- if (size-- <= 0)
+ isdigit((unsigned char) ch));
+ if (size-- <= 0U)
goto emsgsize;
*dst++ = (u_char) tmp;
if (ch == '\0' || ch == '/')
if (ch != '.')
goto enoent;
ch = *src++;
- if (!isascii(ch) || !isdigit(ch))
+ if (!isdigit((unsigned char) ch))
goto enoent;
}
}
goto enoent;
bits = -1;
- if (ch == '/' && isascii(src[0]) && isdigit(src[0]) && dst > odst)
+ if (ch == '/' && isdigit((unsigned char) src[0]) && dst > odst)
{
/* CIDR width specifier. Nothing can follow it. */
ch = *src++; /* Skip over the /. */
assert(n >= 0 && n <= 9);
bits *= 10;
bits += n;
- } while ((ch = *src++) != '\0' &&
- isascii(ch) && isdigit(ch));
+ } while ((ch = *src++) != '\0' && isdigit((unsigned char) ch));
if (ch != '\0')
goto enoent;
if (bits > 32)
if (*odst >= 240) /* Class E */
bits = 32;
else if (*odst >= 224) /* Class D */
- bits = 4;
+ bits = 8;
else if (*odst >= 192) /* Class C */
bits = 24;
else if (*odst >= 128) /* Class B */
bits = 16;
else
-/* Class A */
+ /* Class A */
bits = 8;
/* If imputed mask is narrower than specified octets, widen. */
- if (bits >= 8 && bits < ((dst - odst) * 8))
+ 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-- <= 0)
+ if (size-- <= 0U)
goto emsgsize;
*dst++ = '\0';
}
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)
+ 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);
+}