diff options
author | Bruce Momjian <bruce@momjian.us> | 2003-06-24 22:21:24 +0000 |
---|---|---|
committer | Bruce Momjian <bruce@momjian.us> | 2003-06-24 22:21:24 +0000 |
commit | 945543d919ced497e3fc8165b11142ddc014b9cb (patch) | |
tree | 78b5bc6d56d9d8b85040aa9a45bec4d8c2adaa70 /src/backend/utils/adt/network.c | |
parent | 4dab978c52700b6323919876463ae23c5d7ddf9f (diff) | |
download | postgresql-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/network.c')
-rw-r--r-- | src/backend/utils/adt/network.c | 608 |
1 files changed, 335 insertions, 273 deletions
diff --git a/src/backend/utils/adt/network.c b/src/backend/utils/adt/network.c index 3bc645c988b..213bf5d2f66 100644 --- a/src/backend/utils/adt/network.c +++ b/src/backend/utils/adt/network.c @@ -1,9 +1,7 @@ /* - * PostgreSQL type definitions for the INET type. This - * is for IP V4 CIDR notation, but prepared for V6: just - * add the necessary bits where the comments indicate. + * PostgreSQL type definitions for the INET and CIDR types. * - * $Header: /cvsroot/pgsql/src/backend/utils/adt/network.c,v 1.41 2003/05/13 18:03:07 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/adt/network.c,v 1.42 2003/06/24 22:21:22 momjian Exp $ * * Jon Postel RIP 16 Oct 1998 */ @@ -23,16 +21,14 @@ static Datum text_network(text *src, int type); static int32 network_cmp_internal(inet *a1, inet *a2); -static int v4bitncmp(unsigned long a1, unsigned long a2, int bits); -static bool v4addressOK(unsigned long a1, int bits); +static int bitncmp(void *l, void *r, int n); +static bool addressOK(unsigned char *a, int bits, int family); +static int ip_addrsize(inet *inetptr); /* - * Access macros. Add IPV6 support. + * Access macros. */ -#define ip_addrsize(inetptr) \ - (((inet_struct *)VARDATA(inetptr))->family == AF_INET ? 4 : -1) - #define ip_family(inetptr) \ (((inet_struct *)VARDATA(inetptr))->family) @@ -42,42 +38,68 @@ static bool v4addressOK(unsigned long a1, int bits); #define ip_type(inetptr) \ (((inet_struct *)VARDATA(inetptr))->type) -#define ip_v4addr(inetptr) \ - (((inet_struct *)VARDATA(inetptr))->addr.ipv4_addr) +#define ip_addr(inetptr) \ + (((inet_struct *)VARDATA(inetptr))->ip_addr) + +#define ip_maxbits(inetptr) \ + (ip_family(inetptr) == PGSQL_AF_INET ? 32 : 128) + +/* + * Now, as a function! + * Return the number of bytes of storage needed for this data type. + */ +static int +ip_addrsize(inet *inetptr) +{ + switch (ip_family(inetptr)) { + case PGSQL_AF_INET: + return 4; + case PGSQL_AF_INET6: + return 16; + default: + return -1; + } +} /* Common input routine */ static inet * network_in(char *src, int type) { - int bits; + int bits; inet *dst; - /* make sure any unused bits in a CIDR value are zeroed */ dst = (inet *) palloc0(VARHDRSZ + sizeof(inet_struct)); - /* First, try for an IP V4 address: */ - ip_family(dst) = AF_INET; - bits = inet_net_pton(ip_family(dst), src, &ip_v4addr(dst), - type ? ip_addrsize(dst) : -1); - if ((bits < 0) || (bits > 32)) - { - /* Go for an IPV6 address here, before faulting out: */ - elog(ERROR, "invalid %s value '%s'", - type ? "CIDR" : "INET", src); - } - /* - * Error check: CIDR values must not have any bits set beyond the - * masklen. XXX this code is not IPV6 ready. + * First, check to see if this is an IPv6 or IPv4 address. IPv6 + * addresses will have a : somewhere in them (several, in fact) so + * if there is one present, assume it's V6, otherwise assume it's V4. */ - if (type) + + if (strchr(src, ':') != NULL) { + ip_family(dst) = PGSQL_AF_INET6; + } else { + ip_family(dst) = PGSQL_AF_INET; + } + + bits = inet_net_pton(ip_family(dst), src, ip_addr(dst), + type ? ip_addrsize(dst) : -1); + if ((bits < 0) || (bits > ip_maxbits(dst))) + elog(ERROR, "invalid %s value '%s'", + type ? "CIDR" : "INET", src); + + /* + * Error check: CIDR values must not have any bits set beyond + * the masklen. + */ + if (type) { - if (!v4addressOK(ip_v4addr(dst), bits)) + if (!addressOK(ip_addr(dst), bits, ip_family(dst))) elog(ERROR, "invalid CIDR value '%s': has bits set to right of mask", src); } VARATT_SIZEP(dst) = VARHDRSZ - + ((char *) &ip_v4addr(dst) - (char *) VARDATA(dst)) + + ((char *) ip_addr(dst) - (char *) VARDATA(dst)) + ip_addrsize(dst); ip_bits(dst) = bits; ip_type(dst) = type; @@ -110,32 +132,20 @@ Datum inet_out(PG_FUNCTION_ARGS) { inet *src = PG_GETARG_INET_P(0); - char tmp[sizeof("255.255.255.255/32")]; + char tmp[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255/128")]; char *dst; int len; - if (ip_family(src) == AF_INET) + dst = inet_net_ntop(ip_family(src), ip_addr(src), ip_bits(src), + tmp, sizeof(tmp)); + if (dst == NULL) + elog(ERROR, "unable to print address (%s)", strerror(errno)); + /* For CIDR, add /n if not present */ + if (ip_type(src) && strchr(tmp, '/') == NULL) { - /* It's an IP V4 address: */ - - /* - * Use inet style for both inet and cidr, since we don't want - * abbreviated CIDR style here. - */ - dst = inet_net_ntop(AF_INET, &ip_v4addr(src), ip_bits(src), - tmp, sizeof(tmp)); - if (dst == NULL) - elog(ERROR, "unable to print address (%s)", strerror(errno)); - /* For CIDR, add /n if not present */ - if (ip_type(src) && strchr(tmp, '/') == NULL) - { - len = strlen(tmp); - snprintf(tmp + len, sizeof(tmp) - len, "/%u", ip_bits(src)); - } + len = strlen(tmp); + snprintf(tmp + len, sizeof(tmp) - len, "/%u", ip_bits(src)); } - else - /* Go for an IPV6 address here, before faulting out: */ - elog(ERROR, "unknown address family (%d)", ip_family(src)); PG_RETURN_CSTRING(pstrdup(tmp)); } @@ -172,7 +182,7 @@ inet_recv(PG_FUNCTION_ARGS) if (ip_family(addr) != AF_INET) elog(ERROR, "Invalid family in external inet"); bits = pq_getmsgbyte(buf); - if (bits < 0 || bits > 32) + if (bits < 0 || bits > ip_maxbits(addr)) elog(ERROR, "Invalid bits in external inet"); ip_bits(addr) = bits; ip_type(addr) = pq_getmsgbyte(buf); @@ -183,10 +193,10 @@ inet_recv(PG_FUNCTION_ARGS) elog(ERROR, "Invalid length in external inet"); VARATT_SIZEP(addr) = VARHDRSZ - + ((char *) &ip_v4addr(addr) - (char *) VARDATA(addr)) + + ((char *)ip_addr(addr) - (char *) VARDATA(addr)) + ip_addrsize(addr); - addrptr = (char *) &ip_v4addr(addr); + addrptr = (char *)ip_addr(addr); for (i = 0; i < nb; i++) addrptr[i] = pq_getmsgbyte(buf); @@ -196,7 +206,7 @@ inet_recv(PG_FUNCTION_ARGS) */ if (ip_type(addr)) { - if (!v4addressOK(ip_v4addr(addr), bits)) + if (!addressOK(ip_addr(addr), bits, ip_family(addr))) elog(ERROR, "invalid external CIDR value: has bits set to right of mask"); } @@ -230,7 +240,7 @@ inet_send(PG_FUNCTION_ARGS) if (nb < 0) nb = 0; pq_sendbyte(&buf, nb); - addrptr = (char *) &ip_v4addr(addr); + addrptr = (char *)ip_addr(addr); for (i = 0; i < nb; i++) pq_sendbyte(&buf, addrptr[i]); PG_RETURN_BYTEA_P(pq_endtypsend(&buf)); @@ -257,6 +267,7 @@ text_network(text *src, int type) PG_RETURN_INET_P(network_in(str, type)); } + Datum text_cidr(PG_FUNCTION_ARGS) { @@ -276,8 +287,11 @@ inet_set_masklen(PG_FUNCTION_ARGS) int bits = PG_GETARG_INT32(1); inet *dst; - if ((bits < 0) || (bits > 32)) /* no support for v6 yet */ - elog(ERROR, "set_masklen - invalid value '%d'", bits); + if ( bits == -1 ) + bits = ip_maxbits(src); + + if ((bits < 0) || (bits > ip_maxbits(src))) + elog(ERROR, "set_masklen - invalid value '%d'", bits); /* clone the original data */ dst = (inet *) palloc(VARHDRSZ + sizeof(inet_struct)); @@ -302,26 +316,21 @@ inet_set_masklen(PG_FUNCTION_ARGS) static int32 network_cmp_internal(inet *a1, inet *a2) { - if (ip_family(a1) == AF_INET && ip_family(a2) == AF_INET) + if (ip_family(a1) == ip_family(a2)) { int order; - order = v4bitncmp(ip_v4addr(a1), ip_v4addr(a2), - Min(ip_bits(a1), ip_bits(a2))); + order = bitncmp(ip_addr(a1), ip_addr(a2), + Min(ip_bits(a1), ip_bits(a2))); if (order != 0) return order; order = ((int) ip_bits(a1)) - ((int) ip_bits(a2)); if (order != 0) return order; - return v4bitncmp(ip_v4addr(a1), ip_v4addr(a2), 32); - } - else - { - /* Go for an IPV6 address here, before faulting out: */ - elog(ERROR, "cannot compare address families %d and %d", - ip_family(a1), ip_family(a2)); - return 0; /* keep compiler quiet */ + return bitncmp(ip_addr(a1), ip_addr(a2), ip_maxbits(a1)); } + + return ip_family(a1) - ip_family(a2); } Datum @@ -399,18 +408,13 @@ network_sub(PG_FUNCTION_ARGS) inet *a1 = PG_GETARG_INET_P(0); inet *a2 = PG_GETARG_INET_P(1); - if ((ip_family(a1) == AF_INET) && (ip_family(a2) == AF_INET)) + if (ip_family(a1) == ip_family(a2)) { PG_RETURN_BOOL(ip_bits(a1) > ip_bits(a2) - && v4bitncmp(ip_v4addr(a1), ip_v4addr(a2), ip_bits(a2)) == 0); - } - else - { - /* Go for an IPV6 address here, before faulting out: */ - elog(ERROR, "cannot compare address families %d and %d", - ip_family(a1), ip_family(a2)); - PG_RETURN_BOOL(false); + && bitncmp(ip_addr(a1), ip_addr(a2), ip_bits(a2)) == 0); } + + PG_RETURN_BOOL(false); } Datum @@ -419,18 +423,13 @@ network_subeq(PG_FUNCTION_ARGS) inet *a1 = PG_GETARG_INET_P(0); inet *a2 = PG_GETARG_INET_P(1); - if ((ip_family(a1) == AF_INET) && (ip_family(a2) == AF_INET)) + if (ip_family(a1) == ip_family(a2)) { PG_RETURN_BOOL(ip_bits(a1) >= ip_bits(a2) - && v4bitncmp(ip_v4addr(a1), ip_v4addr(a2), ip_bits(a2)) == 0); - } - else - { - /* Go for an IPV6 address here, before faulting out: */ - elog(ERROR, "cannot compare address families %d and %d", - ip_family(a1), ip_family(a2)); - PG_RETURN_BOOL(false); + && bitncmp(ip_addr(a1), ip_addr(a2), ip_bits(a2)) == 0); } + + PG_RETURN_BOOL(false); } Datum @@ -439,18 +438,13 @@ network_sup(PG_FUNCTION_ARGS) inet *a1 = PG_GETARG_INET_P(0); inet *a2 = PG_GETARG_INET_P(1); - if ((ip_family(a1) == AF_INET) && (ip_family(a2) == AF_INET)) + if (ip_family(a1) == ip_family(a2)) { PG_RETURN_BOOL(ip_bits(a1) < ip_bits(a2) - && v4bitncmp(ip_v4addr(a1), ip_v4addr(a2), ip_bits(a1)) == 0); - } - else - { - /* Go for an IPV6 address here, before faulting out: */ - elog(ERROR, "cannot compare address families %d and %d", - ip_family(a1), ip_family(a2)); - PG_RETURN_BOOL(false); + && bitncmp(ip_addr(a1), ip_addr(a2), ip_bits(a1)) == 0); } + + PG_RETURN_BOOL(false); } Datum @@ -459,18 +453,13 @@ network_supeq(PG_FUNCTION_ARGS) inet *a1 = PG_GETARG_INET_P(0); inet *a2 = PG_GETARG_INET_P(1); - if ((ip_family(a1) == AF_INET) && (ip_family(a2) == AF_INET)) + if (ip_family(a1) == ip_family(a2)) { PG_RETURN_BOOL(ip_bits(a1) <= ip_bits(a2) - && v4bitncmp(ip_v4addr(a1), ip_v4addr(a2), ip_bits(a1)) == 0); - } - else - { - /* Go for an IPV6 address here, before faulting out: */ - elog(ERROR, "cannot compare address families %d and %d", - ip_family(a1), ip_family(a2)); - PG_RETURN_BOOL(false); + && bitncmp(ip_addr(a1), ip_addr(a2), ip_bits(a1)) == 0); } + + PG_RETURN_BOOL(false); } /* @@ -482,19 +471,13 @@ network_host(PG_FUNCTION_ARGS) inet *ip = PG_GETARG_INET_P(0); text *ret; int len; - char *ptr, - tmp[sizeof("255.255.255.255/32")]; + char *ptr; + char tmp[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255/128")]; - if (ip_family(ip) == AF_INET) - { - /* It's an IP V4 address: */ - /* force display of 32 bits, regardless of masklen... */ - if (inet_net_ntop(AF_INET, &ip_v4addr(ip), 32, tmp, sizeof(tmp)) == NULL) - elog(ERROR, "unable to print host (%s)", strerror(errno)); - } - else - /* Go for an IPV6 address here, before faulting out: */ - elog(ERROR, "unknown address family (%d)", ip_family(ip)); + /* force display of max bits, regardless of masklen... */ + if (inet_net_ntop(ip_family(ip), ip_addr(ip), ip_maxbits(ip), + tmp, sizeof(tmp)) == NULL) + elog(ERROR, "unable to print host (%s)", strerror(errno)); /* Suppress /n if present (shouldn't happen now) */ if ((ptr = strchr(tmp, '/')) != NULL) @@ -514,24 +497,17 @@ network_show(PG_FUNCTION_ARGS) inet *ip = PG_GETARG_INET_P(0); text *ret; int len; - char tmp[sizeof("255.255.255.255/32")]; + char tmp[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255/128")]; - if (ip_family(ip) == AF_INET) + if (inet_net_ntop(ip_family(ip), ip_addr(ip), ip_maxbits(ip), + tmp, sizeof(tmp)) == NULL) + elog(ERROR, "unable to print host (%s)", strerror(errno)); + /* Add /n if not present (which it won't be) */ + if (strchr(tmp, '/') == NULL) { - /* It's an IP V4 address: */ - /* force display of 32 bits, regardless of masklen... */ - if (inet_net_ntop(AF_INET, &ip_v4addr(ip), 32, tmp, sizeof(tmp)) == NULL) - elog(ERROR, "unable to print host (%s)", strerror(errno)); - /* Add /n if not present (which it won't be) */ - if (strchr(tmp, '/') == NULL) - { - len = strlen(tmp); - snprintf(tmp + len, sizeof(tmp) - len, "/%u", ip_bits(ip)); - } + len = strlen(tmp); + snprintf(tmp + len, sizeof(tmp) - len, "/%u", ip_bits(ip)); } - else - /* Go for an IPV6 address here, before faulting out: */ - elog(ERROR, "unknown address family (%d)", ip_family(ip)); /* Return string as a text datum */ len = strlen(tmp); @@ -548,24 +524,18 @@ network_abbrev(PG_FUNCTION_ARGS) text *ret; char *dst; int len; - char tmp[sizeof("255.255.255.255/32")]; + char tmp[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255/128")]; - if (ip_family(ip) == AF_INET) - { - /* It's an IP V4 address: */ - if (ip_type(ip)) - dst = inet_cidr_ntop(AF_INET, &ip_v4addr(ip), ip_bits(ip), - tmp, sizeof(tmp)); - else - dst = inet_net_ntop(AF_INET, &ip_v4addr(ip), ip_bits(ip), - tmp, sizeof(tmp)); - - if (dst == NULL) - elog(ERROR, "unable to print address (%s)", strerror(errno)); - } + if (ip_type(ip)) + dst = inet_cidr_ntop(ip_family(ip), ip_addr(ip), + ip_bits(ip), tmp, sizeof(tmp)); else - /* Go for an IPV6 address here, before faulting out: */ - elog(ERROR, "unknown address family (%d)", ip_family(ip)); + dst = inet_net_ntop(ip_family(ip), ip_addr(ip), + ip_bits(ip), tmp, sizeof(tmp)); + + if (dst == NULL) + elog(ERROR, "unable to print address (%s)", + strerror(errno)); /* Return string as a text datum */ len = strlen(tmp); @@ -584,39 +554,66 @@ network_masklen(PG_FUNCTION_ARGS) } Datum +network_family(PG_FUNCTION_ARGS) +{ + inet *ip = PG_GETARG_INET_P(0); + + switch (ip_family(ip)) { + case PGSQL_AF_INET: + PG_RETURN_INT32(4); + break; + case PGSQL_AF_INET6: + PG_RETURN_INT32(6); + break; + default: + PG_RETURN_INT32(0); + break; + } +} + +Datum network_broadcast(PG_FUNCTION_ARGS) { inet *ip = PG_GETARG_INET_P(0); inet *dst; + int byte; + int bits; + int maxbytes; + unsigned char mask; + unsigned char *a, *b; /* make sure any unused bits are zeroed */ dst = (inet *) palloc0(VARHDRSZ + sizeof(inet_struct)); - if (ip_family(ip) == AF_INET) - { - /* It's an IP V4 address: */ - unsigned long mask = 0xffffffff; - - /* - * Shifting by 32 or more bits does not yield portable results, so - * don't try it. - */ - if (ip_bits(ip) < 32) - mask >>= ip_bits(ip); - else - mask = 0; - - ip_v4addr(dst) = htonl(ntohl(ip_v4addr(ip)) | mask); + if (ip_family(ip) == PGSQL_AF_INET) { + maxbytes = 4; + } else { + maxbytes = 16; } - else - /* Go for an IPV6 address here, before faulting out: */ - elog(ERROR, "unknown address family (%d)", ip_family(ip)); + + bits = ip_bits(ip); + a = ip_addr(ip); + b = ip_addr(dst); + + for (byte = 0 ; byte < maxbytes ; byte++) { + if (bits >= 8) { + mask = 0x00; + bits -= 8; + } else if (bits == 0) { + mask = 0xff; + } else { + mask = 0xff >> bits; + bits = 0; + } + + b[byte] = a[byte] | mask; + } ip_family(dst) = ip_family(ip); ip_bits(dst) = ip_bits(ip); ip_type(dst) = 0; VARATT_SIZEP(dst) = VARHDRSZ - + ((char *) &ip_v4addr(dst) - (char *) VARDATA(dst)) + + ((char *) ip_addr(dst) - (char *) VARDATA(dst)) + ip_addrsize(dst); PG_RETURN_INET_P(dst); @@ -627,35 +624,44 @@ network_network(PG_FUNCTION_ARGS) { inet *ip = PG_GETARG_INET_P(0); inet *dst; + int byte; + int bits; + int maxbytes; + unsigned char mask; + unsigned char *a, *b; /* make sure any unused bits are zeroed */ dst = (inet *) palloc0(VARHDRSZ + sizeof(inet_struct)); - if (ip_family(ip) == AF_INET) - { - /* It's an IP V4 address: */ - unsigned long mask = 0xffffffff; - - /* - * Shifting by 32 or more bits does not yield portable results, so - * don't try it. - */ - if (ip_bits(ip) > 0) - mask <<= (32 - ip_bits(ip)); - else - mask = 0; - - ip_v4addr(dst) = htonl(ntohl(ip_v4addr(ip)) & mask); + if (ip_family(ip) == PGSQL_AF_INET) { + maxbytes = 4; + } else { + maxbytes = 16; } - else - /* Go for an IPV6 address here, before faulting out: */ - elog(ERROR, "unknown address family (%d)", ip_family(ip)); + + bits = ip_bits(ip); + a = ip_addr(ip); + b = ip_addr(dst); + + byte = 0; + while (bits) { + if (bits >= 8) { + mask = 0xff; + bits -= 8; + } else { + mask = 0xff << (8 - bits); + bits = 0; + } + + b[byte] = a[byte] & mask; + byte++; + } ip_family(dst) = ip_family(ip); ip_bits(dst) = ip_bits(ip); ip_type(dst) = 1; VARATT_SIZEP(dst) = VARHDRSZ - + ((char *) &ip_v4addr(dst) - (char *) VARDATA(dst)) + + ((char *) ip_addr(dst) - (char *) VARDATA(dst)) + ip_addrsize(dst); PG_RETURN_INET_P(dst); @@ -666,36 +672,43 @@ network_netmask(PG_FUNCTION_ARGS) { inet *ip = PG_GETARG_INET_P(0); inet *dst; + int byte; + int bits; + int maxbytes; + unsigned char mask; + unsigned char *b; /* make sure any unused bits are zeroed */ dst = (inet *) palloc0(VARHDRSZ + sizeof(inet_struct)); - if (ip_family(ip) == AF_INET) - { - /* It's an IP V4 address: */ - unsigned long mask = 0xffffffff; + if (ip_family(ip) == PGSQL_AF_INET) { + maxbytes = 4; + } else { + maxbytes = 16; + } - /* - * Shifting by 32 or more bits does not yield portable results, so - * don't try it. - */ - if (ip_bits(ip) > 0) - mask <<= (32 - ip_bits(ip)); - else - mask = 0; + bits = ip_bits(ip); + b = ip_addr(dst); + + byte = 0; + while (bits) { + if (bits >= 8) { + mask = 0xff; + bits -= 8; + } else { + mask = 0xff << (8 - bits); + bits = 0; + } - ip_v4addr(dst) = htonl(mask); - - ip_bits(dst) = 32; - } - else - /* Go for an IPV6 address here, before faulting out: */ - elog(ERROR, "unknown address family (%d)", ip_family(ip)); + b[byte] = mask; + byte++; + } ip_family(dst) = ip_family(ip); + ip_bits(dst) = ip_bits(ip); ip_type(dst) = 0; VARATT_SIZEP(dst) = VARHDRSZ - + ((char *) &ip_v4addr(dst) - (char *) VARDATA(dst)) + + ((char *)ip_addr(dst) - (char *) VARDATA(dst)) + ip_addrsize(dst); PG_RETURN_INET_P(dst); @@ -706,36 +719,43 @@ network_hostmask(PG_FUNCTION_ARGS) { inet *ip = PG_GETARG_INET_P(0); inet *dst; + int byte; + int bits; + int maxbytes; + unsigned char mask; + unsigned char *b; /* make sure any unused bits are zeroed */ dst = (inet *) palloc0(VARHDRSZ + sizeof(inet_struct)); - if (ip_family(ip) == AF_INET) - { - /* It's an IP V4 address: */ - unsigned long mask = 0xffffffff; - - /* - * Only shift if the mask len is < 32 bits .. - */ - - if (ip_bits(ip) < 32) - mask >>= ip_bits(ip); - else - mask = 0; + if (ip_family(ip) == PGSQL_AF_INET) { + maxbytes = 4; + } else { + maxbytes = 16; + } - ip_v4addr(dst) = htonl(mask); + bits = ip_maxbits(ip) - ip_bits(ip); + b = ip_addr(dst); + + byte = maxbytes - 1; + while (bits) { + if (bits >= 8) { + mask = 0xff; + bits -= 8; + } else { + mask = 0xff >> (8 - bits); + bits = 0; + } - ip_bits(dst) = 32; - } - else - /* Go for an IPV6 address here, before faulting out: */ - elog(ERROR, "unknown address family (%d)", ip_family(ip)); + b[byte] = mask; + byte--; + } ip_family(dst) = ip_family(ip); + ip_bits(dst) = ip_bits(ip); ip_type(dst) = 0; VARATT_SIZEP(dst) = VARHDRSZ - + ((char *) &ip_v4addr(dst) - (char *) VARDATA(dst)) + + ((char *)ip_addr(dst) - (char *) VARDATA(dst)) + ip_addrsize(dst); PG_RETURN_INET_P(dst); @@ -760,12 +780,26 @@ convert_network_to_scalar(Datum value, Oid typid) case CIDROID: { inet *ip = DatumGetInetP(value); - - if (ip_family(ip) == AF_INET) - return (double) ip_v4addr(ip); + int len; + double res; + int i; + + /* + * Note that we don't use the full address + * here. + */ + if (ip_family(ip) == PGSQL_AF_INET) + len = 4; else - /* Go for an IPV6 address here, before faulting out: */ - elog(ERROR, "unknown address family (%d)", ip_family(ip)); + len = 5; + + res = ip_family(ip); + for (i = 0 ; i < len ; i++) { + res *= 256; + res += ip_addr(ip)[i]; + } + return res; + break; } case MACADDROID: @@ -788,53 +822,80 @@ convert_network_to_scalar(Datum value, Oid typid) return 0; } - /* - * Bitwise comparison for V4 addresses. Add V6 implementation! + * int + * bitncmp(l, r, n) + * compare bit masks l and r, for n bits. + * return: + * -1, 1, or 0 in the libc tradition. + * note: + * network byte order assumed. this means 192.5.5.240/28 has + * 0x11110000 in its fourth octet. + * author: + * Paul Vixie (ISC), June 1996 */ - static int -v4bitncmp(unsigned long a1, unsigned long a2, int bits) -{ - unsigned long mask; - - /* - * Shifting by 32 or more bits does not yield portable results, so - * don't try it. - */ - if (bits > 0) - mask = (0xFFFFFFFFL << (32 - bits)) & 0xFFFFFFFFL; - else - mask = 0; - a1 = ntohl(a1); - a2 = ntohl(a2); - if ((a1 & mask) < (a2 & mask)) - return (-1); - else if ((a1 & mask) > (a2 & mask)) - return (1); +bitncmp(void *l, void *r, int n) +{ + u_int lb, rb; + int x, b; + + b = n / 8; + x = memcmp(l, r, b); + if (x) + return (x); + + lb = ((const u_char *)l)[b]; + rb = ((const u_char *)r)[b]; + for (b = n % 8; b > 0; b--) { + if ((lb & 0x80) != (rb & 0x80)) { + if (lb & 0x80) + return (1); + return (-1); + } + lb <<= 1; + rb <<= 1; + } return (0); } -/* - * Returns true if given address fits fully within the specified bit width. - */ static bool -v4addressOK(unsigned long a1, int bits) -{ - unsigned long mask; +addressOK(unsigned char *a, int bits, int family) +{ + int byte; + int nbits; + int maxbits; + int maxbytes; + unsigned char mask; + + if (family == PGSQL_AF_INET) { + maxbits = 32; + maxbytes = 4; + } else { + maxbits = 128; + maxbytes = 16; + } +#if 0 + assert(bits <= maxbits); +#endif + + if (bits == maxbits) + return 1; + + byte = (bits + 7) / 8; + nbits = bits % 8; + mask = 0xff; + if (bits != 0) + mask >>= nbits; + + while (byte < maxbytes) { + if ((a[byte] & mask) != 0) + return 0; + mask = 0xff; + byte++; + } - /* - * Shifting by 32 or more bits does not yield portable results, so - * don't try it. - */ - if (bits > 0) - mask = (0xFFFFFFFFL << (32 - bits)) & 0xFFFFFFFFL; - else - mask = 0; - a1 = ntohl(a1); - if ((a1 & mask) == a1) - return true; - return false; + return 1; } @@ -852,15 +913,16 @@ network_scan_first(Datum in) /* * return "last" IP on a given network. It's the broadcast address, - * however, masklen has to be set to 32, since + * however, masklen has to be set to its max btis, since * 192.168.0.255/24 is considered less than 192.168.0.255/32 * - * NB: this is not IPv6 ready ... + * inet_set_masklen() hacked to max out the masklength to 128 for IPv6 + * and 32 for IPv4 when given '-1' as argument. */ Datum network_scan_last(Datum in) { return DirectFunctionCall2(inet_set_masklen, DirectFunctionCall1(network_broadcast, in), - Int32GetDatum(32)); + Int32GetDatum(-1)); } |