aboutsummaryrefslogtreecommitdiff
path: root/src/backend/utils/adt/inet_net_pton.c
diff options
context:
space:
mode:
authorBruce Momjian <bruce@momjian.us>2003-06-24 22:21:24 +0000
committerBruce Momjian <bruce@momjian.us>2003-06-24 22:21:24 +0000
commit945543d919ced497e3fc8165b11142ddc014b9cb (patch)
tree78b5bc6d56d9d8b85040aa9a45bec4d8c2adaa70 /src/backend/utils/adt/inet_net_pton.c
parent4dab978c52700b6323919876463ae23c5d7ddf9f (diff)
downloadpostgresql-945543d919ced497e3fc8165b11142ddc014b9cb.tar.gz
postgresql-945543d919ced497e3fc8165b11142ddc014b9cb.zip
Add ipv6 address parsing support to 'inet' and 'cidr' data types.
Regression tests for IPv6 operations added. Documentation updated to document IPv6 bits. Stop treating IPv4 as an "unsigned int" and IPv6 as an array of characters. Instead, always use the array of characters so we can have one function fits all. This makes bitncmp(), addressOK(), and several other functions "just work" on both address families. add family() function which returns integer 4 or 6 for IPv4 or IPv6. (See examples below) Note that to add this new function you will need to dump/initdb/reload or find the correct magic to add the function to the postgresql function catalogs. IPv4 addresses always sort before IPv6. On disk we use AF_INET for IPv4, and AF_INET+1 for IPv6 addresses. This prevents the need for a dump and reload, but lets IPv6 parsing work on machines without AF_INET6. To select all IPv4 addresses from a table: select * from foo where family(addr) = 4 ... Order by and other bits should all work. Michael Graff
Diffstat (limited to 'src/backend/utils/adt/inet_net_pton.c')
-rw-r--r--src/backend/utils/adt/inet_net_pton.c214
1 files changed, 206 insertions, 8 deletions
diff --git a/src/backend/utils/adt/inet_net_pton.c b/src/backend/utils/adt/inet_net_pton.c
index 2df36de1e9d..4c7ca9d618a 100644
--- a/src/backend/utils/adt/inet_net_pton.c
+++ b/src/backend/utils/adt/inet_net_pton.c
@@ -16,7 +16,7 @@
*/
#if defined(LIBC_SCCS) && !defined(lint)
-static const char rcsid[] = "$Id: inet_net_pton.c,v 1.14 2002/09/02 02:47:04 momjian Exp $";
+static const char rcsid[] = "$Id: inet_net_pton.c,v 1.15 2003/06/24 22:21:22 momjian Exp $";
#endif
#include "postgres.h"
@@ -29,16 +29,14 @@ static const char rcsid[] = "$Id: inet_net_pton.c,v 1.14 2002/09/02 02:47:04 mom
#include <ctype.h>
#include <errno.h>
-#include "utils/builtins.h"
+#include "utils/inet.h"
-#ifdef SPRINTF_CHAR
-#define SPRINTF(x) strlen(sprintf/**/x)
-#else
-#define SPRINTF(x) ((size_t)sprintf x)
-#endif
+#include "utils/builtins.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
@@ -63,10 +61,14 @@ inet_net_pton(int af, const char *src, void *dst, size_t size)
{
switch (af)
{
- case AF_INET:
+ 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);
@@ -335,3 +337,199 @@ 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);
+}