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

1490 lines
30 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
*
2007-11-15 22:14:46 +01:00
* $PostgreSQL: pgsql/src/backend/utils/adt/network.c,v 1.72 2007/11/15 21:14: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"
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);
static inet *internal_inetpl(inet *ip, int64 addend);
1998-10-22 22:40:50 +02:00
/*
2007-11-15 22:14:46 +01:00
* Access macros. We use VARDATA_ANY so that we can process short-header
* varlena values without detoasting them. This requires a trick:
* VARDATA_ANY assumes the varlena header is already filled in, which is
* not the case when constructing a new value (until SET_INET_VARSIZE is
* called, which we typically can't do till the end). Therefore, we
* always initialize the newly-allocated value to zeroes (using palloc0).
* A zero length word will look like the not-1-byte case to VARDATA_ANY,
* and so we correctly construct an uncompressed value.
*
* Note that ip_maxbits() and SET_INET_VARSIZE() require
* the family field to be set correctly.
1998-10-22 22:40:50 +02:00
*/
#define ip_family(inetptr) \
(((inet_struct *) VARDATA_ANY(inetptr))->family)
1998-10-22 22:40:50 +02:00
#define ip_bits(inetptr) \
(((inet_struct *) VARDATA_ANY(inetptr))->bits)
1998-10-22 22:40:50 +02:00
#define ip_addr(inetptr) \
(((inet_struct *) VARDATA_ANY(inetptr))->ipaddr)
#define ip_maxbits(inetptr) \
(ip_family(inetptr) == PGSQL_AF_INET ? 32 : 128)
#define SET_INET_VARSIZE(dst) \
SET_VARSIZE(dst, VARHDRSZ + offsetof(inet_struct, ipaddr) + \
ip_addrsize(dst))
/*
* Return the number of bytes of address 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 INET/CIDR input routine
*/
1998-10-22 22:40:50 +02:00
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(sizeof(inet));
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
1998-10-22 22:40:50 +02:00
ip_bits(dst) = bits;
SET_INET_VARSIZE(dst);
1998-10-22 22:40:50 +02:00
return dst;
}
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, false));
1998-10-22 22:40:50 +02:00
}
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, true));
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
/*
* Common INET/CIDR output routine
1998-10-22 22:40:50 +02:00
*/
static char *
network_out(inet *src, bool is_cidr)
1998-10-22 22:40:50 +02:00
{
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 (is_cidr && 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
return pstrdup(tmp);
1998-10-22 22:40:50 +02:00
}
Datum
inet_out(PG_FUNCTION_ARGS)
{
inet *src = PG_GETARG_INET_P(0);
PG_RETURN_CSTRING(network_out(src, false));
}
1998-10-22 22:40:50 +02:00
Datum
cidr_out(PG_FUNCTION_ARGS)
1998-10-22 22:40:50 +02:00
{
inet *src = PG_GETARG_INET_P(0);
PG_RETURN_CSTRING(network_out(src, true));
1998-10-22 22:40:50 +02:00
}
2003-05-13 20:03:08 +02:00
/*
* network_recv - converts external binary format to inet
2003-05-13 20:03:08 +02:00
*
* 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
2006-10-04 02:30:14 +02:00
* allow some code-sharing on the client side. We send it correctly on
* output, but ignore the value on input.
2003-05-13 20:03:08 +02:00
*/
static inet *
network_recv(StringInfo buf, bool is_cidr)
2003-05-13 20:03:08 +02:00
{
inet *addr;
char *addrptr;
int bits;
int nb,
i;
/* make sure any unused bits in a CIDR value are zeroed */
addr = (inet *) palloc0(sizeof(inet));
2003-05-13 20:03:08 +02:00
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),
2006-10-04 02:30:14 +02:00
/* translator: %s is inet or cidr */
errmsg("invalid address family in external \"%s\" value",
is_cidr ? "cidr" : "inet")));
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),
2006-10-04 02:30:14 +02:00
/* translator: %s is inet or cidr */
errmsg("invalid bits in external \"%s\" value",
is_cidr ? "cidr" : "inet")));
2003-05-13 20:03:08 +02:00
ip_bits(addr) = bits;
i = pq_getmsgbyte(buf); /* ignore is_cidr */
2003-05-13 20:03:08 +02:00
nb = pq_getmsgbyte(buf);
if (nb != ip_addrsize(addr))
ereport(ERROR,
(errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
2006-10-04 02:30:14 +02:00
/* translator: %s is inet or cidr */
errmsg("invalid length in external \"%s\" value",
is_cidr ? "cidr" : "inet")));
2003-05-13 20:03:08 +02:00
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 (is_cidr)
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
}
SET_INET_VARSIZE(addr);
return addr;
}
Datum
inet_recv(PG_FUNCTION_ARGS)
{
StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
PG_RETURN_INET_P(network_recv(buf, false));
2003-05-13 20:03:08 +02:00
}
Datum
cidr_recv(PG_FUNCTION_ARGS)
{
StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
PG_RETURN_INET_P(network_recv(buf, true));
2003-05-13 20:03:08 +02:00
}
2003-05-13 20:03:08 +02:00
/*
* network_send - converts inet to binary format
2003-05-13 20:03:08 +02:00
*/
static bytea *
network_send(inet *addr, bool is_cidr)
2003-05-13 20:03:08 +02:00
{
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, is_cidr);
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]);
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));
2003-05-13 20:03:08 +02:00
}
Datum
cidr_send(PG_FUNCTION_ARGS)
{
inet *addr = PG_GETARG_INET_P(0);
PG_RETURN_BYTEA_P(network_send(addr, true));
2003-05-13 20:03:08 +02:00
}
Datum
inet_to_cidr(PG_FUNCTION_ARGS)
{
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_ANY(src));
memcpy(dst, src, VARSIZE_ANY(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
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_ANY(src));
memcpy(dst, src, VARSIZE_ANY(src));
ip_bits(dst) = bits;
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_ANY(src));
memcpy(dst, src, VARSIZE_ANY(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);
}
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.
*/
Datum
hashinet(PG_FUNCTION_ARGS)
{
inet *addr = PG_GETARG_INET_P(0);
int addrsize = ip_addrsize(addr);
/* XXX this assumes there are no pad bytes in the data structure */
return hash_any((unsigned char *) VARDATA_ANY(addr), 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);
SET_VARSIZE(ret, len + VARHDRSZ);
memcpy(VARDATA(ret), tmp, len);
PG_RETURN_TEXT_P(ret);
1998-10-22 22:40:50 +02:00
}
/*
* network_show implements the inet and cidr casts to text. This is not
* quite the same behavior as network_out, hence we can't drop it in favor
* of CoerceViaIO.
*/
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);
SET_VARSIZE(ret, len + VARHDRSZ);
memcpy(VARDATA(ret), tmp, len);
PG_RETURN_TEXT_P(ret);
}
Datum
inet_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_net_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 inet value: %m")));
/* Return string as a text datum */
len = strlen(tmp);
ret = (text *) palloc(len + VARHDRSZ);
SET_VARSIZE(ret, len + VARHDRSZ);
memcpy(VARDATA(ret), tmp, len);
PG_RETURN_TEXT_P(ret);
}
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);
SET_VARSIZE(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(sizeof(inet));
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);
SET_INET_VARSIZE(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(sizeof(inet));
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);
SET_INET_VARSIZE(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(sizeof(inet));
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);
SET_INET_VARSIZE(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 */
dst = (inet *) palloc0(sizeof(inet));
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);
SET_INET_VARSIZE(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.
*/
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;
/*
* Note that we don't use the full address for IPv6.
*/
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();
clean_ipv6_addr(port->raddr.addr.ss_family, remote_host);
PG_RETURN_INET_P(network_in(remote_host, false));
}
/*
* 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();
clean_ipv6_addr(port->laddr.addr.ss_family, local_host);
PG_RETURN_INET_P(network_in(local_host, false));
}
/*
* 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)));
}
Datum
inetnot(PG_FUNCTION_ARGS)
{
inet *ip = PG_GETARG_INET_P(0);
inet *dst;
dst = (inet *) palloc0(sizeof(inet));
{
2006-10-04 02:30:14 +02:00
int nb = ip_addrsize(ip);
unsigned char *pip = ip_addr(ip);
unsigned char *pdst = ip_addr(dst);
while (nb-- > 0)
pdst[nb] = ~pip[nb];
}
ip_bits(dst) = ip_bits(ip);
ip_family(dst) = ip_family(ip);
SET_INET_VARSIZE(dst);
PG_RETURN_INET_P(dst);
}
Datum
inetand(PG_FUNCTION_ARGS)
{
inet *ip = PG_GETARG_INET_P(0);
inet *ip2 = PG_GETARG_INET_P(1);
inet *dst;
dst = (inet *) palloc0(sizeof(inet));
if (ip_family(ip) != ip_family(ip2))
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("cannot AND inet values of different sizes")));
else
{
2006-10-04 02:30:14 +02:00
int nb = ip_addrsize(ip);
unsigned char *pip = ip_addr(ip);
unsigned char *pip2 = ip_addr(ip2);
unsigned char *pdst = ip_addr(dst);
while (nb-- > 0)
pdst[nb] = pip[nb] & pip2[nb];
}
ip_bits(dst) = Max(ip_bits(ip), ip_bits(ip2));
ip_family(dst) = ip_family(ip);
SET_INET_VARSIZE(dst);
PG_RETURN_INET_P(dst);
}
Datum
inetor(PG_FUNCTION_ARGS)
{
inet *ip = PG_GETARG_INET_P(0);
inet *ip2 = PG_GETARG_INET_P(1);
inet *dst;
dst = (inet *) palloc0(sizeof(inet));
if (ip_family(ip) != ip_family(ip2))
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("cannot OR inet values of different sizes")));
else
{
2006-10-04 02:30:14 +02:00
int nb = ip_addrsize(ip);
unsigned char *pip = ip_addr(ip);
unsigned char *pip2 = ip_addr(ip2);
unsigned char *pdst = ip_addr(dst);
while (nb-- > 0)
pdst[nb] = pip[nb] | pip2[nb];
}
ip_bits(dst) = Max(ip_bits(ip), ip_bits(ip2));
ip_family(dst) = ip_family(ip);
SET_INET_VARSIZE(dst);
PG_RETURN_INET_P(dst);
}
static inet *
internal_inetpl(inet *ip, int64 addend)
{
inet *dst;
dst = (inet *) palloc0(sizeof(inet));
{
2006-10-04 02:30:14 +02:00
int nb = ip_addrsize(ip);
unsigned char *pip = ip_addr(ip);
unsigned char *pdst = ip_addr(dst);
int carry = 0;
while (nb-- > 0)
{
carry = pip[nb] + (int) (addend & 0xFF) + carry;
pdst[nb] = (unsigned char) (carry & 0xFF);
carry >>= 8;
2006-10-04 02:30:14 +02:00
/*
* We have to be careful about right-shifting addend because
2006-10-04 02:30:14 +02:00
* right-shift isn't portable for negative values, while simply
* dividing by 256 doesn't work (the standard rounding is in the
* wrong direction, besides which there may be machines out there
* that round the wrong way). So, explicitly clear the low-order
* byte to remove any doubt about the correct result of the
* division, and then divide rather than shift.
*/
addend &= ~((int64) 0xFF);
addend /= 0x100;
}
2006-10-04 02:30:14 +02:00
/*
2006-10-04 02:30:14 +02:00
* At this point we should have addend and carry both zero if original
* addend was >= 0, or addend -1 and carry 1 if original addend was <
* 0. Anything else means overflow.
*/
if (!((addend == 0 && carry == 0) ||
(addend == -1 && carry == 1)))
ereport(ERROR,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("result is out of range")));
}
ip_bits(dst) = ip_bits(ip);
ip_family(dst) = ip_family(ip);
SET_INET_VARSIZE(dst);
return dst;
}
Datum
inetpl(PG_FUNCTION_ARGS)
{
2006-10-04 02:30:14 +02:00
inet *ip = PG_GETARG_INET_P(0);
int64 addend = PG_GETARG_INT64(1);
PG_RETURN_INET_P(internal_inetpl(ip, addend));
}
Datum
inetmi_int8(PG_FUNCTION_ARGS)
{
2006-10-04 02:30:14 +02:00
inet *ip = PG_GETARG_INET_P(0);
int64 addend = PG_GETARG_INT64(1);
PG_RETURN_INET_P(internal_inetpl(ip, -addend));
}
Datum
inetmi(PG_FUNCTION_ARGS)
{
inet *ip = PG_GETARG_INET_P(0);
inet *ip2 = PG_GETARG_INET_P(1);
int64 res = 0;
if (ip_family(ip) != ip_family(ip2))
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("cannot subtract inet values of different sizes")));
else
{
/*
2006-10-04 02:30:14 +02:00
* We form the difference using the traditional complement, increment,
* and add rule, with the increment part being handled by starting the
* carry off at 1. If you don't think integer arithmetic is done in
* two's complement, too bad.
*/
2006-10-04 02:30:14 +02:00
int nb = ip_addrsize(ip);
int byte = 0;
unsigned char *pip = ip_addr(ip);
unsigned char *pip2 = ip_addr(ip2);
int carry = 1;
while (nb-- > 0)
{
2006-10-04 02:30:14 +02:00
int lobyte;
carry = pip[nb] + (~pip2[nb] & 0xFF) + carry;
lobyte = carry & 0xFF;
if (byte < sizeof(int64))
{
res |= ((int64) lobyte) << (byte * 8);
}
else
{
/*
2006-10-04 02:30:14 +02:00
* Input wider than int64: check for overflow. All bytes to
* the left of what will fit should be 0 or 0xFF, depending on
* sign of the now-complete result.
*/
if ((res < 0) ? (lobyte != 0xFF) : (lobyte != 0))
ereport(ERROR,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("result is out of range")));
}
carry >>= 8;
byte++;
}
/*
2006-10-04 02:30:14 +02:00
* If input is narrower than int64, overflow is not possible, but we
* have to do proper sign extension.
*/
if (carry == 0 && byte < sizeof(int64))
res |= ((int64) -1) << (byte * 8);
}
PG_RETURN_INT64(res);
}
/*
* clean_ipv6_addr --- remove any '%zone' part from an IPv6 address string
*
* XXX This should go away someday!
*
* This is a kluge needed because we don't yet support zones in stored inet
2007-11-15 22:14:46 +01:00
* values. Since the result of getnameinfo() might include a zone spec,
* call this to remove it anywhere we want to feed getnameinfo's output to
2007-11-15 22:14:46 +01:00
* network_in. Beats failing entirely.
*
* An alternative approach would be to let network_in ignore %-parts for
* itself, but that would mean we'd silently drop zone specs in user input,
* which seems not such a good idea.
*/
void
clean_ipv6_addr(int addr_family, char *addr)
{
#ifdef HAVE_IPV6
if (addr_family == AF_INET6)
{
2007-11-15 22:14:46 +01:00
char *pct = strchr(addr, '%');
if (pct)
*pct = '\0';
}
#endif
}