postgresql/src/backend/utils/adt/network.c

1136 lines
22 KiB
C
Raw Normal View History

1998-10-22 22:40:50 +02:00
/*
* PostgreSQL type definitions for the INET and CIDR types.
1998-10-22 22:40:50 +02:00
*
2006-01-23 22:49:39 +01:00
* $PostgreSQL: pgsql/src/backend/utils/adt/network.c,v 1.60 2006/01/23 21:49:39 momjian Exp $
*
1998-10-22 22:40:50 +02:00
* Jon Postel RIP 16 Oct 1998
*/
#include "postgres.h"
#include <sys/socket.h>
1998-10-22 22:40:50 +02:00
#include <netinet/in.h>
#include <arpa/inet.h>
#include "access/hash.h"
#include "catalog/pg_type.h"
*) inet_(client|server)_(addr|port)() and necessary documentation for the four functions. > Also, please justify the temp-related changes. I was not aware that we > had any breakage there. patch-tmp-schema.txt contains the following bits: *) Changes pg_namespace_aclmask() so that the superuser is always able to create objects in the temp namespace. *) Changes pg_namespace_aclmask() so that if this is a temp namespace, objects are only allowed to be created in the temp namespace if the user has TEMP privs on the database. This encompasses all object creation, not just TEMP tables. *) InitTempTableNamespace() checks to see if the current user, not the session user, has access to create a temp namespace. The first two changes are necessary to support the third change. Now it's possible to revoke all temp table privs from non-super users and limiting all creation of temp tables/schemas via a function that's executed with elevated privs (security definer). Before this change, it was not possible to have a setuid function to create a temp table/schema if the session user had no TEMP privs. patch-area-path.txt contains: *) Can now determine the area of a closed path. patch-dfmgr.txt contains: *) Small tweak to add the library path that's being expanded. I was using $lib/foo.so and couldn't easily figure out what the error message, "invalid macro name in dynamic library path" meant without looking through the source code. With the path in there, at least I know where to start looking in my config file. Sean Chittenden
2004-05-26 20:35:51 +02:00
#include "libpq/ip.h"
#include "libpq/libpq-be.h"
2003-05-13 20:03:08 +02:00
#include "libpq/pqformat.h"
*) inet_(client|server)_(addr|port)() and necessary documentation for the four functions. > Also, please justify the temp-related changes. I was not aware that we > had any breakage there. patch-tmp-schema.txt contains the following bits: *) Changes pg_namespace_aclmask() so that the superuser is always able to create objects in the temp namespace. *) Changes pg_namespace_aclmask() so that if this is a temp namespace, objects are only allowed to be created in the temp namespace if the user has TEMP privs on the database. This encompasses all object creation, not just TEMP tables. *) InitTempTableNamespace() checks to see if the current user, not the session user, has access to create a temp namespace. The first two changes are necessary to support the third change. Now it's possible to revoke all temp table privs from non-super users and limiting all creation of temp tables/schemas via a function that's executed with elevated privs (security definer). Before this change, it was not possible to have a setuid function to create a temp table/schema if the session user had no TEMP privs. patch-area-path.txt contains: *) Can now determine the area of a closed path. patch-dfmgr.txt contains: *) Small tweak to add the library path that's being expanded. I was using $lib/foo.so and couldn't easily figure out what the error message, "invalid macro name in dynamic library path" meant without looking through the source code. With the path in there, at least I know where to start looking in my config file. Sean Chittenden
2004-05-26 20:35:51 +02:00
#include "miscadmin.h"
#include "utils/builtins.h"
#include "utils/inet.h"
2006-01-23 22:49:39 +01:00
static Datum text_network(text *src, bool is_cidr);
static int32 network_cmp_internal(inet *a1, inet *a2);
2003-08-04 02:43:34 +02:00
static int bitncmp(void *l, void *r, int n);
static bool addressOK(unsigned char *a, int bits, int family);
2003-08-04 02:43:34 +02:00
static int ip_addrsize(inet *inetptr);
1998-10-22 22:40:50 +02:00
/*
* Access macros.
1998-10-22 22:40:50 +02:00
*/
#define ip_family(inetptr) \
(((inet_struct *)VARDATA(inetptr))->family)
#define ip_bits(inetptr) \
(((inet_struct *)VARDATA(inetptr))->bits)
#define ip_is_cidr(inetptr) \
(((inet_struct *)VARDATA(inetptr))->is_cidr)
1998-10-22 22:40:50 +02:00
#define ip_addr(inetptr) \
(((inet_struct *)VARDATA(inetptr))->ipaddr)
#define ip_maxbits(inetptr) \
(ip_family(inetptr) == PGSQL_AF_INET ? 32 : 128)
/*
* Return the number of bytes of storage needed for this data type.
*/
static int
ip_addrsize(inet *inetptr)
{
2003-08-04 02:43:34 +02:00
switch (ip_family(inetptr))
{
case PGSQL_AF_INET:
return 4;
case PGSQL_AF_INET6:
return 16;
default:
return 0;
}
}
1998-10-22 22:40:50 +02:00
/* Common input routine */
static inet *
network_in(char *src, bool is_cidr)
1998-10-22 22:40:50 +02:00
{
2003-08-04 02:43:34 +02:00
int bits;
1998-10-22 22:40:50 +02:00
inet *dst;
dst = (inet *) palloc0(VARHDRSZ + sizeof(inet_struct));
1999-03-22 06:00:57 +01:00
/*
2005-10-15 04:49:52 +02:00
* 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.
*/
2003-08-02 01:22:52 +02:00
if (strchr(src, ':') != NULL)
ip_family(dst) = PGSQL_AF_INET6;
2003-08-02 01:22:52 +02:00
else
ip_family(dst) = PGSQL_AF_INET;
2003-08-04 02:43:34 +02:00
bits = inet_net_pton(ip_family(dst), src, ip_addr(dst),
is_cidr ? ip_addrsize(dst) : -1);
if ((bits < 0) || (bits > ip_maxbits(dst)))
ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
2003-08-04 02:43:34 +02:00
/* translator: first %s is inet or cidr */
errmsg("invalid input syntax for type %s: \"%s\"",
is_cidr ? "cidr" : "inet", src)));
/*
2005-10-15 04:49:52 +02:00
* Error check: CIDR values must not have any bits set beyond the masklen.
*/
if (is_cidr)
{
if (!addressOK(ip_addr(dst), bits, ip_family(dst)))
ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("invalid cidr value: \"%s\"", src),
errdetail("Value has bits set to right of mask.")));
}
1999-03-22 06:00:57 +01:00
VARATT_SIZEP(dst) = VARHDRSZ
+ ((char *) ip_addr(dst) - (char *) VARDATA(dst))
1998-10-22 22:40:50 +02:00
+ ip_addrsize(dst);
ip_bits(dst) = bits;
ip_is_cidr(dst) = is_cidr;
1998-10-22 22:40:50 +02:00
return dst;
}
/* INET address reader. */
Datum
inet_in(PG_FUNCTION_ARGS)
1998-10-22 22:40:50 +02:00
{
char *src = PG_GETARG_CSTRING(0);
PG_RETURN_INET_P(network_in(src, 0));
1998-10-22 22:40:50 +02:00
}
/* CIDR address reader. */
Datum
cidr_in(PG_FUNCTION_ARGS)
1998-10-22 22:40:50 +02:00
{
char *src = PG_GETARG_CSTRING(0);
PG_RETURN_INET_P(network_in(src, 1));
1998-10-22 22:40:50 +02:00
}
*) inet_(client|server)_(addr|port)() and necessary documentation for the four functions. > Also, please justify the temp-related changes. I was not aware that we > had any breakage there. patch-tmp-schema.txt contains the following bits: *) Changes pg_namespace_aclmask() so that the superuser is always able to create objects in the temp namespace. *) Changes pg_namespace_aclmask() so that if this is a temp namespace, objects are only allowed to be created in the temp namespace if the user has TEMP privs on the database. This encompasses all object creation, not just TEMP tables. *) InitTempTableNamespace() checks to see if the current user, not the session user, has access to create a temp namespace. The first two changes are necessary to support the third change. Now it's possible to revoke all temp table privs from non-super users and limiting all creation of temp tables/schemas via a function that's executed with elevated privs (security definer). Before this change, it was not possible to have a setuid function to create a temp table/schema if the session user had no TEMP privs. patch-area-path.txt contains: *) Can now determine the area of a closed path. patch-dfmgr.txt contains: *) Small tweak to add the library path that's being expanded. I was using $lib/foo.so and couldn't easily figure out what the error message, "invalid macro name in dynamic library path" meant without looking through the source code. With the path in there, at least I know where to start looking in my config file. Sean Chittenden
2004-05-26 20:35:51 +02:00
1998-10-22 22:40:50 +02:00
/*
* INET address output function.
*/
Datum
inet_out(PG_FUNCTION_ARGS)
1998-10-22 22:40:50 +02:00
{
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;
1998-10-22 22:40:50 +02:00
dst = inet_net_ntop(ip_family(src), ip_addr(src), ip_bits(src),
2003-08-04 02:43:34 +02:00
tmp, sizeof(tmp));
if (dst == NULL)
ereport(ERROR,
(errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
errmsg("could not format inet value: %m")));
/* For CIDR, add /n if not present */
if (ip_is_cidr(src) && strchr(tmp, '/') == NULL)
1998-10-22 22:40:50 +02:00
{
len = strlen(tmp);
snprintf(tmp + len, sizeof(tmp) - len, "/%u", ip_bits(src));
1998-10-22 22:40:50 +02:00
}
1999-03-22 06:00:57 +01:00
PG_RETURN_CSTRING(pstrdup(tmp));
1998-10-22 22:40:50 +02:00
}
/* share code with INET case */
Datum
cidr_out(PG_FUNCTION_ARGS)
1998-10-22 22:40:50 +02:00
{
return inet_out(fcinfo);
1998-10-22 22:40:50 +02:00
}
2003-05-13 20:03:08 +02:00
/*
* inet_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.
2003-05-13 20:03:08 +02:00
*/
Datum
inet_recv(PG_FUNCTION_ARGS)
{
StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
inet *addr;
char *addrptr;
int bits;
int nb,
i;
/* make sure any unused bits in a CIDR value are zeroed */
addr = (inet *) palloc0(VARHDRSZ + sizeof(inet_struct));
ip_family(addr) = pq_getmsgbyte(buf);
2003-08-02 01:22:52 +02:00
if (ip_family(addr) != PGSQL_AF_INET &&
ip_family(addr) != PGSQL_AF_INET6)
ereport(ERROR,
(errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
2005-10-15 04:49:52 +02:00
errmsg("invalid address family in external \"inet\" value")));
2003-05-13 20:03:08 +02:00
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")));
2003-05-13 20:03:08 +02:00
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")));
2003-05-13 20:03:08 +02:00
nb = pq_getmsgbyte(buf);
if (nb != ip_addrsize(addr))
ereport(ERROR,
(errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
errmsg("invalid length in external \"inet\" value")));
2003-05-13 20:03:08 +02:00
VARATT_SIZEP(addr) = VARHDRSZ
2003-08-04 02:43:34 +02:00
+ ((char *) ip_addr(addr) - (char *) VARDATA(addr))
2003-05-13 20:03:08 +02:00
+ ip_addrsize(addr);
2003-08-04 02:43:34 +02:00
addrptr = (char *) ip_addr(addr);
2003-05-13 20:03:08 +02:00
for (i = 0; i < nb; i++)
addrptr[i] = pq_getmsgbyte(buf);
/*
2005-10-15 04:49:52 +02:00
* Error check: CIDR values must not have any bits set beyond the masklen.
2003-05-13 20:03:08 +02:00
*/
if (ip_is_cidr(addr))
2003-05-13 20:03:08 +02:00
{
if (!addressOK(ip_addr(addr), bits, ip_family(addr)))
ereport(ERROR,
(errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
errmsg("invalid external \"cidr\" value"),
errdetail("Value has bits set to right of mask.")));
2003-05-13 20:03:08 +02:00
}
PG_RETURN_INET_P(addr);
}
/* share code with INET case */
Datum
cidr_recv(PG_FUNCTION_ARGS)
{
return inet_recv(fcinfo);
}
/*
* inet_send - converts inet to binary format
*/
Datum
inet_send(PG_FUNCTION_ARGS)
{
inet *addr = PG_GETARG_INET_P(0);
StringInfoData buf;
char *addrptr;
int nb,
i;
pq_begintypsend(&buf);
pq_sendbyte(&buf, ip_family(addr));
pq_sendbyte(&buf, ip_bits(addr));
pq_sendbyte(&buf, ip_is_cidr(addr));
2003-05-13 20:03:08 +02:00
nb = ip_addrsize(addr);
if (nb < 0)
nb = 0;
pq_sendbyte(&buf, nb);
2003-08-04 02:43:34 +02:00
addrptr = (char *) ip_addr(addr);
2003-05-13 20:03:08 +02:00
for (i = 0; i < nb; i++)
pq_sendbyte(&buf, addrptr[i]);
PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
}
/* share code with INET case */
Datum
cidr_send(PG_FUNCTION_ARGS)
{
return inet_send(fcinfo);
}
static Datum
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';
PG_RETURN_INET_P(network_in(str, is_cidr));
}
Datum
text_cidr(PG_FUNCTION_ARGS)
{
return text_network(PG_GETARG_TEXT_P(0), 1);
}
Datum
text_inet(PG_FUNCTION_ARGS)
{
return text_network(PG_GETARG_TEXT_P(0), 0);
}
Datum
inet_set_masklen(PG_FUNCTION_ARGS)
{
inet *src = PG_GETARG_INET_P(0);
int bits = PG_GETARG_INT32(1);
inet *dst;
2003-08-04 02:43:34 +02:00
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;
PG_RETURN_INET_P(dst);
}
1998-10-22 22:40:50 +02:00
/*
* Basic comparison function for sorting and inet/cidr comparisons.
*
* Comparison is first on the common bits of the network part, then on
* the length of the network part, and then on the whole unmasked address.
* The effect is that the network part is the major sort key, and for
* equal network parts we sort on the host part. Note this is only sane
* for CIDR if address bits to the right of the mask are guaranteed zero;
* otherwise logically-equal CIDRs might compare different.
1998-10-22 22:40:50 +02:00
*/
static int32
network_cmp_internal(inet *a1, inet *a2)
1998-10-22 22:40:50 +02:00
{
if (ip_family(a1) == ip_family(a2))
{
2001-03-22 05:01:46 +01:00
int order;
order = bitncmp(ip_addr(a1), ip_addr(a2),
2003-08-04 02:43:34 +02:00
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 bitncmp(ip_addr(a1), ip_addr(a2), ip_maxbits(a1));
}
return ip_family(a1) - ip_family(a2);
1998-10-22 22:40:50 +02:00
}
Datum
network_cmp(PG_FUNCTION_ARGS)
1998-10-22 22:40:50 +02:00
{
inet *a1 = PG_GETARG_INET_P(0);
inet *a2 = PG_GETARG_INET_P(1);
PG_RETURN_INT32(network_cmp_internal(a1, a2));
1998-10-22 22:40:50 +02:00
}
/*
* Boolean ordering tests.
*/
Datum
network_lt(PG_FUNCTION_ARGS)
1998-10-22 22:40:50 +02:00
{
inet *a1 = PG_GETARG_INET_P(0);
inet *a2 = PG_GETARG_INET_P(1);
PG_RETURN_BOOL(network_cmp_internal(a1, a2) < 0);
1998-10-22 22:40:50 +02:00
}
Datum
network_le(PG_FUNCTION_ARGS)
1998-10-22 22:40:50 +02:00
{
inet *a1 = PG_GETARG_INET_P(0);
inet *a2 = PG_GETARG_INET_P(1);
PG_RETURN_BOOL(network_cmp_internal(a1, a2) <= 0);
1998-10-22 22:40:50 +02:00
}
Datum
network_eq(PG_FUNCTION_ARGS)
1998-10-22 22:40:50 +02:00
{
inet *a1 = PG_GETARG_INET_P(0);
inet *a2 = PG_GETARG_INET_P(1);
PG_RETURN_BOOL(network_cmp_internal(a1, a2) == 0);
}
Datum
network_ge(PG_FUNCTION_ARGS)
{
inet *a1 = PG_GETARG_INET_P(0);
inet *a2 = PG_GETARG_INET_P(1);
PG_RETURN_BOOL(network_cmp_internal(a1, a2) >= 0);
}
Datum
network_gt(PG_FUNCTION_ARGS)
{
inet *a1 = PG_GETARG_INET_P(0);
inet *a2 = PG_GETARG_INET_P(1);
PG_RETURN_BOOL(network_cmp_internal(a1, a2) > 0);
1998-10-22 22:40:50 +02:00
}
Datum
network_ne(PG_FUNCTION_ARGS)
{
inet *a1 = PG_GETARG_INET_P(0);
inet *a2 = PG_GETARG_INET_P(1);
PG_RETURN_BOOL(network_cmp_internal(a1, a2) != 0);
}
/*
* 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);
}
/*
* Boolean network-inclusion tests.
*/
Datum
network_sub(PG_FUNCTION_ARGS)
1998-10-22 22:40:50 +02:00
{
inet *a1 = PG_GETARG_INET_P(0);
inet *a2 = PG_GETARG_INET_P(1);
1999-05-25 18:15:34 +02:00
if (ip_family(a1) == ip_family(a2))
1998-10-22 22:40:50 +02:00
{
PG_RETURN_BOOL(ip_bits(a1) > ip_bits(a2)
2005-10-15 04:49:52 +02:00
&& bitncmp(ip_addr(a1), ip_addr(a2), ip_bits(a2)) == 0);
1998-10-22 22:40:50 +02:00
}
PG_RETURN_BOOL(false);
1998-10-22 22:40:50 +02:00
}
Datum
network_subeq(PG_FUNCTION_ARGS)
1998-10-22 22:40:50 +02:00
{
inet *a1 = PG_GETARG_INET_P(0);
inet *a2 = PG_GETARG_INET_P(1);
1999-03-22 06:00:57 +01:00
if (ip_family(a1) == ip_family(a2))
1998-10-22 22:40:50 +02:00
{
PG_RETURN_BOOL(ip_bits(a1) >= ip_bits(a2)
2005-10-15 04:49:52 +02:00
&& bitncmp(ip_addr(a1), ip_addr(a2), ip_bits(a2)) == 0);
1998-10-22 22:40:50 +02:00
}
PG_RETURN_BOOL(false);
1998-10-22 22:40:50 +02:00
}
Datum
network_sup(PG_FUNCTION_ARGS)
1998-10-22 22:40:50 +02:00
{
inet *a1 = PG_GETARG_INET_P(0);
inet *a2 = PG_GETARG_INET_P(1);
1999-03-22 06:00:57 +01:00
if (ip_family(a1) == ip_family(a2))
1998-10-22 22:40:50 +02:00
{
PG_RETURN_BOOL(ip_bits(a1) < ip_bits(a2)
2005-10-15 04:49:52 +02:00
&& bitncmp(ip_addr(a1), ip_addr(a2), ip_bits(a1)) == 0);
1998-10-22 22:40:50 +02:00
}
PG_RETURN_BOOL(false);
1998-10-22 22:40:50 +02:00
}
Datum
network_supeq(PG_FUNCTION_ARGS)
1998-10-22 22:40:50 +02:00
{
inet *a1 = PG_GETARG_INET_P(0);
inet *a2 = PG_GETARG_INET_P(1);
1999-03-22 06:00:57 +01:00
if (ip_family(a1) == ip_family(a2))
1998-10-22 22:40:50 +02:00
{
PG_RETURN_BOOL(ip_bits(a1) <= ip_bits(a2)
2005-10-15 04:49:52 +02:00
&& bitncmp(ip_addr(a1), ip_addr(a2), ip_bits(a1)) == 0);
1998-10-22 22:40:50 +02:00
}
PG_RETURN_BOOL(false);
1998-10-22 22:40:50 +02:00
}
/*
* Extract data from a network datatype.
*/
Datum
network_host(PG_FUNCTION_ARGS)
1998-10-22 22:40:50 +02:00
{
inet *ip = PG_GETARG_INET_P(0);
1998-10-22 22:40:50 +02:00
text *ret;
int len;
char *ptr;
char tmp[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255/128")];
1998-10-22 22:40:50 +02:00
/* force display of max bits, regardless of masklen... */
if (inet_net_ntop(ip_family(ip), ip_addr(ip), ip_maxbits(ip),
2003-08-04 02:43:34 +02:00
tmp, sizeof(tmp)) == NULL)
ereport(ERROR,
(errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
errmsg("could not format inet value: %m")));
1999-03-22 06:00:57 +01:00
/* Suppress /n if present (shouldn't happen now) */
1998-10-22 22:40:50 +02:00
if ((ptr = strchr(tmp, '/')) != NULL)
*ptr = '\0';
/* 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);
1998-10-22 22:40:50 +02:00
}
Datum
network_show(PG_FUNCTION_ARGS)
{
inet *ip = PG_GETARG_INET_P(0);
text *ret;
int len;
char tmp[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255/128")];
if (inet_net_ntop(ip_family(ip), ip_addr(ip), ip_maxbits(ip),
2003-08-04 02:43:34 +02:00
tmp, sizeof(tmp)) == NULL)
ereport(ERROR,
(errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
errmsg("could not format inet value: %m")));
/* 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));
}
/* 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_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")];
if (ip_is_cidr(ip))
dst = inet_cidr_ntop(ip_family(ip), ip_addr(ip),
2003-08-04 02:43:34 +02:00
ip_bits(ip), tmp, sizeof(tmp));
else
dst = inet_net_ntop(ip_family(ip), ip_addr(ip),
2003-08-04 02:43:34 +02:00
ip_bits(ip), tmp, sizeof(tmp));
if (dst == NULL)
ereport(ERROR,
(errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
errmsg("could not format inet 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)
1998-10-22 22:40:50 +02:00
{
inet *ip = PG_GETARG_INET_P(0);
1999-03-22 06:00:57 +01:00
PG_RETURN_INT32(ip_bits(ip));
1998-10-22 22:40:50 +02:00
}
Datum
network_family(PG_FUNCTION_ARGS)
{
2003-08-04 02:43:34 +02:00
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)
1998-10-22 22:40:50 +02:00
{
inet *ip = PG_GETARG_INET_P(0);
inet *dst;
2003-08-04 02:43:34 +02:00
int byte;
int bits;
int maxbytes;
unsigned char mask;
2003-08-04 02:43:34 +02:00
unsigned char *a,
*b;
/* make sure any unused bits are zeroed */
dst = (inet *) palloc0(VARHDRSZ + sizeof(inet_struct));
1998-10-22 22:40:50 +02:00
2003-08-04 02:43:34 +02:00
if (ip_family(ip) == PGSQL_AF_INET)
maxbytes = 4;
2003-08-04 02:43:34 +02:00
else
maxbytes = 16;
bits = ip_bits(ip);
a = ip_addr(ip);
b = ip_addr(dst);
2003-08-04 02:43:34 +02:00
for (byte = 0; byte < maxbytes; byte++)
{
if (bits >= 8)
{
mask = 0x00;
bits -= 8;
2003-08-04 02:43:34 +02:00
}
else if (bits == 0)
mask = 0xff;
2003-08-04 02:43:34 +02:00
else
{
mask = 0xff >> bits;
bits = 0;
}
b[byte] = a[byte] | mask;
2003-08-04 02:43:34 +02:00
}
1999-03-22 06:00:57 +01:00
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);
PG_RETURN_INET_P(dst);
1998-10-22 22:40:50 +02:00
}
Datum
network_network(PG_FUNCTION_ARGS)
1998-10-22 22:40:50 +02:00
{
inet *ip = PG_GETARG_INET_P(0);
inet *dst;
2003-08-04 02:43:34 +02:00
int byte;
int bits;
unsigned char mask;
2003-08-04 02:43:34 +02:00
unsigned char *a,
*b;
/* make sure any unused bits are zeroed */
dst = (inet *) palloc0(VARHDRSZ + sizeof(inet_struct));
1998-10-22 22:40:50 +02:00
bits = ip_bits(ip);
a = ip_addr(ip);
b = ip_addr(dst);
byte = 0;
2003-08-04 02:43:34 +02:00
while (bits)
{
if (bits >= 8)
{
mask = 0xff;
bits -= 8;
2003-08-04 02:43:34 +02:00
}
else
{
mask = 0xff << (8 - bits);
bits = 0;
}
b[byte] = a[byte] & mask;
byte++;
2003-08-04 02:43:34 +02:00
}
1999-03-22 06:00:57 +01:00
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);
PG_RETURN_INET_P(dst);
1998-10-22 22:40:50 +02:00
}
Datum
network_netmask(PG_FUNCTION_ARGS)
1998-10-22 22:40:50 +02:00
{
inet *ip = PG_GETARG_INET_P(0);
inet *dst;
2003-08-04 02:43:34 +02:00
int byte;
int bits;
unsigned char mask;
unsigned char *b;
/* make sure any unused bits are zeroed */
dst = (inet *) palloc0(VARHDRSZ + sizeof(inet_struct));
1998-10-22 22:40:50 +02:00
bits = ip_bits(ip);
b = ip_addr(dst);
byte = 0;
2003-08-04 02:43:34 +02:00
while (bits)
{
if (bits >= 8)
{
mask = 0xff;
bits -= 8;
2003-08-04 02:43:34 +02:00
}
else
{
mask = 0xff << (8 - bits);
bits = 0;
}
b[byte] = mask;
byte++;
2003-08-04 02:43:34 +02:00
}
1999-03-22 06:00:57 +01:00
ip_family(dst) = ip_family(ip);
ip_bits(dst) = ip_maxbits(ip);
ip_is_cidr(dst) = false;
VARATT_SIZEP(dst) = VARHDRSZ
2003-08-04 02:43:34 +02:00
+ ((char *) ip_addr(dst) - (char *) VARDATA(dst))
+ ip_addrsize(dst);
PG_RETURN_INET_P(dst);
1998-10-22 22:40:50 +02:00
}
Datum
network_hostmask(PG_FUNCTION_ARGS)
{
inet *ip = PG_GETARG_INET_P(0);
inet *dst;
2003-08-04 02:43:34 +02:00
int byte;
int bits;
int maxbytes;
unsigned char mask;
unsigned char *b;
/* make sure any unused bits are zeroed */
2003-03-22 00:18:52 +01:00
dst = (inet *) palloc0(VARHDRSZ + sizeof(inet_struct));
2003-08-04 02:43:34 +02:00
if (ip_family(ip) == PGSQL_AF_INET)
maxbytes = 4;
2003-08-04 02:43:34 +02:00
else
maxbytes = 16;
bits = ip_maxbits(ip) - ip_bits(ip);
b = ip_addr(dst);
byte = maxbytes - 1;
2003-08-04 02:43:34 +02:00
while (bits)
{
if (bits >= 8)
{
mask = 0xff;
bits -= 8;
2003-08-04 02:43:34 +02:00
}
else
{
mask = 0xff >> (8 - bits);
bits = 0;
}
b[byte] = mask;
byte--;
2003-08-04 02:43:34 +02:00
}
ip_family(dst) = ip_family(ip);
ip_bits(dst) = ip_maxbits(ip);
ip_is_cidr(dst) = false;
VARATT_SIZEP(dst) = VARHDRSZ
2003-08-04 02:43:34 +02:00
+ ((char *) ip_addr(dst) - (char *) VARDATA(dst))
+ ip_addrsize(dst);
PG_RETURN_INET_P(dst);
}
/*
* 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)
{
switch (typid)
{
case INETOID:
case CIDROID:
{
inet *ip = DatumGetInetP(value);
2003-08-04 02:43:34 +02:00
int len;
double res;
int i;
/*
2003-08-04 02:43:34 +02:00
* Note that we don't use the full address here.
*/
if (ip_family(ip) == PGSQL_AF_INET)
len = 4;
else
len = 5;
res = ip_family(ip);
2003-08-04 02:43:34 +02:00
for (i = 0; i < len; i++)
{
res *= 256;
res += ip_addr(ip)[i];
}
return res;
break;
}
case MACADDROID:
{
macaddr *mac = DatumGetMacaddrP(value);
double res;
res = (mac->a << 16) | (mac->b << 8) | (mac->c);
res *= 256 * 256 * 256;
res += (mac->d << 16) | (mac->e << 8) | (mac->f);
return res;
}
}
/*
2005-10-15 04:49:52 +02:00
* Can't get here unless someone tries to use scalarltsel/scalargtsel on
* an operator with one network and one non-network operand.
*/
elog(ERROR, "unsupported type: %u", typid);
return 0;
}
1998-10-22 22:40:50 +02:00
/*
* int
* bitncmp(l, r, n)
2003-08-04 02:43:34 +02:00
* compare bit masks l and r, for n bits.
* return:
2003-08-04 02:43:34 +02:00
* -1, 1, or 0 in the libc tradition.
* note:
2003-08-04 02:43:34 +02:00
* network byte order assumed. this means 192.5.5.240/28 has
* 0x11110000 in its fourth octet.
* author:
2003-08-04 02:43:34 +02:00
* Paul Vixie (ISC), June 1996
1998-10-22 22:40:50 +02:00
*/
static int
bitncmp(void *l, void *r, int n)
{
2003-08-04 02:43:34 +02:00
u_int lb,
rb;
int x,
b;
b = n / 8;
x = memcmp(l, r, b);
if (x)
return x;
2003-08-04 02:43:34 +02:00
lb = ((const u_char *) l)[b];
rb = ((const u_char *) r)[b];
for (b = n % 8; b > 0; b--)
{
if (IS_HIGHBIT_SET(lb) != IS_HIGHBIT_SET(rb))
2003-08-04 02:43:34 +02:00
{
if (IS_HIGHBIT_SET(lb))
return 1;
return -1;
}
lb <<= 1;
rb <<= 1;
}
return 0;
1998-10-22 22:40:50 +02:00
}
static bool
addressOK(unsigned char *a, int bits, int family)
{
2003-08-04 02:43:34 +02:00
int byte;
int nbits;
int maxbits;
int maxbytes;
unsigned char mask;
2003-08-04 02:43:34 +02:00
if (family == PGSQL_AF_INET)
{
maxbits = 32;
maxbytes = 4;
2003-08-04 02:43:34 +02:00
}
else
{
maxbits = 128;
maxbytes = 16;
}
2003-08-02 01:22:52 +02:00
Assert(bits <= maxbits);
if (bits == maxbits)
2003-08-02 01:22:52 +02:00
return true;
byte = bits / 8;
nbits = bits % 8;
mask = 0xff;
if (bits != 0)
mask >>= nbits;
2003-08-04 02:43:34 +02:00
while (byte < maxbytes)
{
if ((a[byte] & mask) != 0)
2003-08-02 01:22:52 +02:00
return false;
mask = 0xff;
byte++;
}
2003-08-02 01:22:52 +02:00
return true;
}
/*
* These functions are used by planner to generate indexscan limits
* for clauses a << b and a <<= b
*/
/* return the minimal value for an IP on a given network */
Datum
network_scan_first(Datum in)
{
return DirectFunctionCall1(network_network, in);
}
/*
* return "last" IP on a given network. It's the broadcast address,
* 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
*
* 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),
2003-08-04 02:43:34 +02:00
Int32GetDatum(-1));
}
/*
* IP address that the client is connecting from (NULL if Unix socket)
*/
Datum
inet_client_addr(PG_FUNCTION_ARGS)
{
2004-08-29 07:07:03 +02:00
Port *port = MyProcPort;
char remote_host[NI_MAXHOST];
int ret;
if (port == NULL)
PG_RETURN_NULL();
2004-08-29 07:07:03 +02:00
switch (port->raddr.addr.ss_family)
{
case AF_INET:
#ifdef HAVE_IPV6
2004-08-29 07:07:03 +02:00
case AF_INET6:
#endif
2004-08-29 07:07:03 +02:00
break;
default:
PG_RETURN_NULL();
}
remote_host[0] = '\0';
ret = pg_getnameinfo_all(&port->raddr.addr, port->raddr.salen,
remote_host, sizeof(remote_host),
NULL, 0,
NI_NUMERICHOST | NI_NUMERICSERV);
if (ret)
PG_RETURN_NULL();
PG_RETURN_INET_P(network_in(remote_host, 0));
}
/*
* port that the client is connecting from (NULL if Unix socket)
*/
Datum
inet_client_port(PG_FUNCTION_ARGS)
{
2004-08-29 07:07:03 +02:00
Port *port = MyProcPort;
char remote_port[NI_MAXSERV];
int ret;
if (port == NULL)
PG_RETURN_NULL();
2004-08-29 07:07:03 +02:00
switch (port->raddr.addr.ss_family)
{
case AF_INET:
#ifdef HAVE_IPV6
2004-08-29 07:07:03 +02:00
case AF_INET6:
#endif
2004-08-29 07:07:03 +02:00
break;
default:
PG_RETURN_NULL();
}
remote_port[0] = '\0';
ret = pg_getnameinfo_all(&port->raddr.addr, port->raddr.salen,
NULL, 0,
remote_port, sizeof(remote_port),
NI_NUMERICHOST | NI_NUMERICSERV);
if (ret)
PG_RETURN_NULL();
PG_RETURN_DATUM(DirectFunctionCall1(int4in, CStringGetDatum(remote_port)));
}
/*
* IP address that the server accepted the connection on (NULL if Unix socket)
*/
Datum
inet_server_addr(PG_FUNCTION_ARGS)
{
2004-08-29 07:07:03 +02:00
Port *port = MyProcPort;
char local_host[NI_MAXHOST];
int ret;
if (port == NULL)
PG_RETURN_NULL();
2004-08-29 07:07:03 +02:00
switch (port->laddr.addr.ss_family)
{
case AF_INET:
#ifdef HAVE_IPV6
2004-08-29 07:07:03 +02:00
case AF_INET6:
#endif
2004-08-29 07:07:03 +02:00
break;
default:
PG_RETURN_NULL();
}
local_host[0] = '\0';
ret = pg_getnameinfo_all(&port->laddr.addr, port->laddr.salen,
local_host, sizeof(local_host),
NULL, 0,
NI_NUMERICHOST | NI_NUMERICSERV);
if (ret)
PG_RETURN_NULL();
PG_RETURN_INET_P(network_in(local_host, 0));
}
/*
* port that the server accepted the connection on (NULL if Unix socket)
*/
Datum
inet_server_port(PG_FUNCTION_ARGS)
{
2004-08-29 07:07:03 +02:00
Port *port = MyProcPort;
char local_port[NI_MAXSERV];
int ret;
if (port == NULL)
PG_RETURN_NULL();
2004-08-29 07:07:03 +02:00
switch (port->laddr.addr.ss_family)
{
case AF_INET:
#ifdef HAVE_IPV6
2004-08-29 07:07:03 +02:00
case AF_INET6:
#endif
2004-08-29 07:07:03 +02:00
break;
default:
PG_RETURN_NULL();
}
local_port[0] = '\0';
ret = pg_getnameinfo_all(&port->laddr.addr, port->laddr.salen,
NULL, 0,
local_port, sizeof(local_port),
NI_NUMERICHOST | NI_NUMERICSERV);
if (ret)
PG_RETURN_NULL();
PG_RETURN_DATUM(DirectFunctionCall1(int4in, CStringGetDatum(local_port)));
}