summaryrefslogtreecommitdiff
path: root/src/backend/utils/adt/network.c
diff options
context:
space:
mode:
authorTom Lane2006-01-26 02:35:51 +0000
committerTom Lane2006-01-26 02:35:51 +0000
commit8d8bf127605d0a87b22bb07e630a075506ca5e1e (patch)
tree3ad06ce423081f23df826753da0dc7f998e58822 /src/backend/utils/adt/network.c
parent5997386a0a38f3ded28ce6eb2c2b4f110b377e46 (diff)
Clean up the INET-vs-CIDR situation. Get rid of the internal is_cidr flag
and rely exclusively on the SQL type system to tell the difference between the types. Prevent creation of invalid CIDR values via casting from INET or set_masklen() --- both of these operations now silently zero any bits to the right of the netmask. Remove duplicate CIDR comparison operators, letting the type rely on the INET operators instead.
Diffstat (limited to 'src/backend/utils/adt/network.c')
-rw-r--r--src/backend/utils/adt/network.c275
1 files changed, 196 insertions, 79 deletions
diff --git a/src/backend/utils/adt/network.c b/src/backend/utils/adt/network.c
index 567a7cc2ee5..c827dab6967 100644
--- a/src/backend/utils/adt/network.c
+++ b/src/backend/utils/adt/network.c
@@ -1,7 +1,7 @@
/*
* PostgreSQL type definitions for the INET and CIDR types.
*
- * $PostgreSQL: pgsql/src/backend/utils/adt/network.c,v 1.60 2006/01/23 21:49:39 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/adt/network.c,v 1.61 2006/01/26 02:35:49 tgl Exp $
*
* Jon Postel RIP 16 Oct 1998
*/
@@ -22,7 +22,7 @@
#include "utils/inet.h"
-static Datum text_network(text *src, bool is_cidr);
+static inet *text_network(text *src, bool is_cidr);
static int32 network_cmp_internal(inet *a1, inet *a2);
static int bitncmp(void *l, void *r, int n);
static bool addressOK(unsigned char *a, int bits, int family);
@@ -38,9 +38,6 @@ static int ip_addrsize(inet *inetptr);
#define ip_bits(inetptr) \
(((inet_struct *)VARDATA(inetptr))->bits)
-#define ip_is_cidr(inetptr) \
- (((inet_struct *)VARDATA(inetptr))->is_cidr)
-
#define ip_addr(inetptr) \
(((inet_struct *)VARDATA(inetptr))->ipaddr)
@@ -64,7 +61,9 @@ ip_addrsize(inet *inetptr)
}
}
-/* Common input routine */
+/*
+ * Common INET/CIDR input routine
+ */
static inet *
network_in(char *src, bool is_cidr)
{
@@ -109,37 +108,33 @@ network_in(char *src, bool is_cidr)
+ ((char *) ip_addr(dst) - (char *) VARDATA(dst))
+ ip_addrsize(dst);
ip_bits(dst) = bits;
- ip_is_cidr(dst) = is_cidr;
return dst;
}
-/* INET address reader. */
Datum
inet_in(PG_FUNCTION_ARGS)
{
char *src = PG_GETARG_CSTRING(0);
- PG_RETURN_INET_P(network_in(src, 0));
+ PG_RETURN_INET_P(network_in(src, false));
}
-/* CIDR address reader. */
Datum
cidr_in(PG_FUNCTION_ARGS)
{
char *src = PG_GETARG_CSTRING(0);
- PG_RETURN_INET_P(network_in(src, 1));
+ PG_RETURN_INET_P(network_in(src, true));
}
/*
- * INET address output function.
+ * Common INET/CIDR output routine
*/
-Datum
-inet_out(PG_FUNCTION_ARGS)
+static char *
+network_out(inet *src, bool is_cidr)
{
- inet *src = PG_GETARG_INET_P(0);
char tmp[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255/128")];
char *dst;
int len;
@@ -152,34 +147,45 @@ inet_out(PG_FUNCTION_ARGS)
errmsg("could not format inet value: %m")));
/* For CIDR, add /n if not present */
- if (ip_is_cidr(src) && strchr(tmp, '/') == NULL)
+ if (is_cidr && strchr(tmp, '/') == NULL)
{
len = strlen(tmp);
snprintf(tmp + len, sizeof(tmp) - len, "/%u", ip_bits(src));
}
- PG_RETURN_CSTRING(pstrdup(tmp));
+ return pstrdup(tmp);
}
+Datum
+inet_out(PG_FUNCTION_ARGS)
+{
+ inet *src = PG_GETARG_INET_P(0);
+
+ PG_RETURN_CSTRING(network_out(src, false));
+}
-/* share code with INET case */
Datum
cidr_out(PG_FUNCTION_ARGS)
{
- return inet_out(fcinfo);
+ inet *src = PG_GETARG_INET_P(0);
+
+ PG_RETURN_CSTRING(network_out(src, true));
}
/*
- * inet_recv - converts external binary format to inet
+ * network_recv - converts external binary format to inet
*
* The external representation is (one byte apiece for)
* family, bits, is_cidr, address length, address in network byte order.
+ *
+ * Presence of is_cidr is largely for historical reasons, though it might
+ * allow some code-sharing on the client side. We send it correctly on
+ * output, but ignore the value on input.
*/
-Datum
-inet_recv(PG_FUNCTION_ARGS)
+static inet *
+network_recv(StringInfo buf, bool is_cidr)
{
- StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
inet *addr;
char *addrptr;
int bits;
@@ -194,23 +200,25 @@ inet_recv(PG_FUNCTION_ARGS)
ip_family(addr) != PGSQL_AF_INET6)
ereport(ERROR,
(errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
- errmsg("invalid address family in external \"inet\" value")));
+ /* translator: %s is inet or cidr */
+ errmsg("invalid address family in external \"%s\" value",
+ is_cidr ? "cidr" : "inet")));
bits = pq_getmsgbyte(buf);
if (bits < 0 || bits > ip_maxbits(addr))
ereport(ERROR,
(errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
- errmsg("invalid bits in external \"inet\" value")));
+ /* translator: %s is inet or cidr */
+ errmsg("invalid bits in external \"%s\" value",
+ is_cidr ? "cidr" : "inet")));
ip_bits(addr) = bits;
- ip_is_cidr(addr) = pq_getmsgbyte(buf);
- if (ip_is_cidr(addr) != false && ip_is_cidr(addr) != true)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
- errmsg("invalid type in external \"inet\" value")));
+ i = pq_getmsgbyte(buf); /* ignore is_cidr */
nb = pq_getmsgbyte(buf);
if (nb != ip_addrsize(addr))
ereport(ERROR,
(errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
- errmsg("invalid length in external \"inet\" value")));
+ /* translator: %s is inet or cidr */
+ errmsg("invalid length in external \"%s\" value",
+ is_cidr ? "cidr" : "inet")));
VARATT_SIZEP(addr) = VARHDRSZ
+ ((char *) ip_addr(addr) - (char *) VARDATA(addr))
+ ip_addrsize(addr);
@@ -222,7 +230,7 @@ inet_recv(PG_FUNCTION_ARGS)
/*
* Error check: CIDR values must not have any bits set beyond the masklen.
*/
- if (ip_is_cidr(addr))
+ if (is_cidr)
{
if (!addressOK(ip_addr(addr), bits, ip_family(addr)))
ereport(ERROR,
@@ -231,23 +239,32 @@ inet_recv(PG_FUNCTION_ARGS)
errdetail("Value has bits set to right of mask.")));
}
- PG_RETURN_INET_P(addr);
+ return addr;
+}
+
+Datum
+inet_recv(PG_FUNCTION_ARGS)
+{
+ StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
+
+ PG_RETURN_INET_P(network_recv(buf, false));
}
-/* share code with INET case */
Datum
cidr_recv(PG_FUNCTION_ARGS)
{
- return inet_recv(fcinfo);
+ StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
+
+ PG_RETURN_INET_P(network_recv(buf, true));
}
+
/*
- * inet_send - converts inet to binary format
+ * network_send - converts inet to binary format
*/
-Datum
-inet_send(PG_FUNCTION_ARGS)
+static bytea *
+network_send(inet *addr, bool is_cidr)
{
- inet *addr = PG_GETARG_INET_P(0);
StringInfoData buf;
char *addrptr;
int nb,
@@ -256,7 +273,7 @@ inet_send(PG_FUNCTION_ARGS)
pq_begintypsend(&buf);
pq_sendbyte(&buf, ip_family(addr));
pq_sendbyte(&buf, ip_bits(addr));
- pq_sendbyte(&buf, ip_is_cidr(addr));
+ pq_sendbyte(&buf, is_cidr);
nb = ip_addrsize(addr);
if (nb < 0)
nb = 0;
@@ -264,41 +281,93 @@ inet_send(PG_FUNCTION_ARGS)
addrptr = (char *) ip_addr(addr);
for (i = 0; i < nb; i++)
pq_sendbyte(&buf, addrptr[i]);
- PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
+ return pq_endtypsend(&buf);
+}
+
+Datum
+inet_send(PG_FUNCTION_ARGS)
+{
+ inet *addr = PG_GETARG_INET_P(0);
+
+ PG_RETURN_BYTEA_P(network_send(addr, false));
}
-/* share code with INET case */
Datum
cidr_send(PG_FUNCTION_ARGS)
{
- return inet_send(fcinfo);
+ inet *addr = PG_GETARG_INET_P(0);
+
+ PG_RETURN_BYTEA_P(network_send(addr, true));
}
-static Datum
+static inet *
text_network(text *src, bool is_cidr)
{
int len = VARSIZE(src) - VARHDRSZ;
-
char *str = palloc(len + 1);
memcpy(str, VARDATA(src), len);
- *(str + len) = '\0';
+ str[len] = '\0';
- PG_RETURN_INET_P(network_in(str, is_cidr));
+ return network_in(str, is_cidr);
}
+Datum
+text_inet(PG_FUNCTION_ARGS)
+{
+ text *src = PG_GETARG_TEXT_P(0);
+
+ PG_RETURN_INET_P(text_network(src, false));
+}
Datum
text_cidr(PG_FUNCTION_ARGS)
{
- return text_network(PG_GETARG_TEXT_P(0), 1);
+ text *src = PG_GETARG_TEXT_P(0);
+
+ PG_RETURN_INET_P(text_network(src, true));
}
+
Datum
-text_inet(PG_FUNCTION_ARGS)
+inet_to_cidr(PG_FUNCTION_ARGS)
{
- return text_network(PG_GETARG_TEXT_P(0), 0);
+ inet *src = PG_GETARG_INET_P(0);
+ inet *dst;
+ int bits;
+ int byte;
+ int nbits;
+ int maxbytes;
+
+ bits = ip_bits(src);
+
+ /* safety check */
+ if ((bits < 0) || (bits > ip_maxbits(src)))
+ elog(ERROR, "invalid inet bit length: %d", bits);
+
+ /* clone the original data */
+ dst = (inet *) palloc(VARSIZE(src));
+ memcpy(dst, src, VARSIZE(src));
+
+ /* zero out any bits to the right of the netmask */
+ byte = bits / 8;
+ nbits = bits % 8;
+ /* clear the first byte, this might be a partial byte */
+ if (nbits != 0)
+ {
+ ip_addr(dst)[byte] &= ~(0xFF >> nbits);
+ byte++;
+ }
+ /* clear remaining bytes */
+ maxbytes = ip_addrsize(dst);
+ while (byte < maxbytes)
+ {
+ ip_addr(dst)[byte] = 0;
+ byte++;
+ }
+
+ PG_RETURN_INET_P(dst);
}
Datum
@@ -325,6 +394,50 @@ inet_set_masklen(PG_FUNCTION_ARGS)
PG_RETURN_INET_P(dst);
}
+Datum
+cidr_set_masklen(PG_FUNCTION_ARGS)
+{
+ inet *src = PG_GETARG_INET_P(0);
+ int bits = PG_GETARG_INT32(1);
+ inet *dst;
+ int byte;
+ int nbits;
+ int maxbytes;
+
+ if (bits == -1)
+ bits = ip_maxbits(src);
+
+ if ((bits < 0) || (bits > ip_maxbits(src)))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("invalid mask length: %d", bits)));
+
+ /* clone the original data */
+ dst = (inet *) palloc(VARSIZE(src));
+ memcpy(dst, src, VARSIZE(src));
+
+ ip_bits(dst) = bits;
+
+ /* zero out any bits to the right of the new netmask */
+ byte = bits / 8;
+ nbits = bits % 8;
+ /* clear the first byte, this might be a partial byte */
+ if (nbits != 0)
+ {
+ ip_addr(dst)[byte] &= ~(0xFF >> nbits);
+ byte++;
+ }
+ /* clear remaining bytes */
+ maxbytes = ip_addrsize(dst);
+ while (byte < maxbytes)
+ {
+ ip_addr(dst)[byte] = 0;
+ byte++;
+ }
+
+ PG_RETURN_INET_P(dst);
+}
+
/*
* Basic comparison function for sorting and inet/cidr comparisons.
*
@@ -424,23 +537,15 @@ network_ne(PG_FUNCTION_ARGS)
/*
* Support function for hash indexes on inet/cidr.
- *
- * Since network_cmp considers only ip_family, ip_bits, and ip_addr, only
- * these fields may be used in the hash; in particular don't use is_cidr.
*/
Datum
hashinet(PG_FUNCTION_ARGS)
{
inet *addr = PG_GETARG_INET_P(0);
int addrsize = ip_addrsize(addr);
- unsigned char key[sizeof(inet_struct)];
-
- Assert(addrsize + 2 <= sizeof(key));
- key[0] = ip_family(addr);
- key[1] = ip_bits(addr);
- memcpy(key + 2, ip_addr(addr), addrsize);
- return hash_any(key, addrsize + 2);
+ /* XXX this assumes there are no pad bytes in the data structure */
+ return hash_any(VARDATA(addr), addrsize + 2);
}
/*
@@ -567,7 +672,7 @@ network_show(PG_FUNCTION_ARGS)
}
Datum
-network_abbrev(PG_FUNCTION_ARGS)
+inet_abbrev(PG_FUNCTION_ARGS)
{
inet *ip = PG_GETARG_INET_P(0);
text *ret;
@@ -575,12 +680,8 @@ network_abbrev(PG_FUNCTION_ARGS)
int len;
char tmp[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255/128")];
- if (ip_is_cidr(ip))
- dst = inet_cidr_ntop(ip_family(ip), ip_addr(ip),
- ip_bits(ip), tmp, sizeof(tmp));
- else
- dst = inet_net_ntop(ip_family(ip), ip_addr(ip),
- ip_bits(ip), tmp, sizeof(tmp));
+ dst = inet_net_ntop(ip_family(ip), ip_addr(ip),
+ ip_bits(ip), tmp, sizeof(tmp));
if (dst == NULL)
ereport(ERROR,
@@ -596,6 +697,31 @@ network_abbrev(PG_FUNCTION_ARGS)
}
Datum
+cidr_abbrev(PG_FUNCTION_ARGS)
+{
+ inet *ip = PG_GETARG_INET_P(0);
+ text *ret;
+ char *dst;
+ int len;
+ char tmp[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255/128")];
+
+ dst = inet_cidr_ntop(ip_family(ip), ip_addr(ip),
+ ip_bits(ip), tmp, sizeof(tmp));
+
+ if (dst == NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
+ errmsg("could not format cidr value: %m")));
+
+ /* Return string as a text datum */
+ len = strlen(tmp);
+ ret = (text *) palloc(len + VARHDRSZ);
+ VARATT_SIZEP(ret) = len + VARHDRSZ;
+ memcpy(VARDATA(ret), tmp, len);
+ PG_RETURN_TEXT_P(ret);
+}
+
+Datum
network_masklen(PG_FUNCTION_ARGS)
{
inet *ip = PG_GETARG_INET_P(0);
@@ -666,7 +792,6 @@ network_broadcast(PG_FUNCTION_ARGS)
ip_family(dst) = ip_family(ip);
ip_bits(dst) = ip_bits(ip);
- ip_is_cidr(dst) = false;
VARATT_SIZEP(dst) = VARHDRSZ
+ ((char *) ip_addr(dst) - (char *) VARDATA(dst))
+ ip_addrsize(dst);
@@ -712,7 +837,6 @@ network_network(PG_FUNCTION_ARGS)
ip_family(dst) = ip_family(ip);
ip_bits(dst) = ip_bits(ip);
- ip_is_cidr(dst) = true;
VARATT_SIZEP(dst) = VARHDRSZ
+ ((char *) ip_addr(dst) - (char *) VARDATA(dst))
+ ip_addrsize(dst);
@@ -756,7 +880,6 @@ network_netmask(PG_FUNCTION_ARGS)
ip_family(dst) = ip_family(ip);
ip_bits(dst) = ip_maxbits(ip);
- ip_is_cidr(dst) = false;
VARATT_SIZEP(dst) = VARHDRSZ
+ ((char *) ip_addr(dst) - (char *) VARDATA(dst))
+ ip_addrsize(dst);
@@ -806,7 +929,6 @@ network_hostmask(PG_FUNCTION_ARGS)
ip_family(dst) = ip_family(ip);
ip_bits(dst) = ip_maxbits(ip);
- ip_is_cidr(dst) = false;
VARATT_SIZEP(dst) = VARHDRSZ
+ ((char *) ip_addr(dst) - (char *) VARDATA(dst))
+ ip_addrsize(dst);
@@ -818,11 +940,6 @@ network_hostmask(PG_FUNCTION_ARGS)
* Convert a value of a network datatype to an approximate scalar value.
* This is used for estimating selectivities of inequality operators
* involving network types.
- *
- * Currently, inet/cidr values are simply converted to the IPv4 address;
- * this will need more thought when IPv6 is supported too. MAC addresses
- * are converted to their numeric equivalent as well (OK since we have a
- * double to play in).
*/
double
convert_network_to_scalar(Datum value, Oid typid)
@@ -838,7 +955,7 @@ convert_network_to_scalar(Datum value, Oid typid)
int i;
/*
- * Note that we don't use the full address here.
+ * Note that we don't use the full address for IPv6.
*/
if (ip_family(ip) == PGSQL_AF_INET)
len = 4;
@@ -1020,7 +1137,7 @@ inet_client_addr(PG_FUNCTION_ARGS)
if (ret)
PG_RETURN_NULL();
- PG_RETURN_INET_P(network_in(remote_host, 0));
+ PG_RETURN_INET_P(network_in(remote_host, false));
}
@@ -1094,7 +1211,7 @@ inet_server_addr(PG_FUNCTION_ARGS)
if (ret)
PG_RETURN_NULL();
- PG_RETURN_INET_P(network_in(local_host, 0));
+ PG_RETURN_INET_P(network_in(local_host, false));
}