Logo Search packages:      
Sourcecode: postgresql-8.4 version File versions

network.c

/*
 *    PostgreSQL type definitions for the INET and CIDR types.
 *
 *    $PostgreSQL: pgsql/src/backend/utils/adt/network.c,v 1.74 2009/06/11 14:49:03 momjian Exp $
 *
 *    Jon Postel RIP 16 Oct 1998
 */

#include "postgres.h"

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#include "access/hash.h"
#include "catalog/pg_type.h"
#include "libpq/ip.h"
#include "libpq/libpq-be.h"
#include "libpq/pqformat.h"
#include "miscadmin.h"
#include "utils/builtins.h"
#include "utils/inet.h"


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);
static int  ip_addrsize(inet *inetptr);
static inet *internal_inetpl(inet *ip, int64 addend);

/*
 *    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.
 */

#define ip_family(inetptr) \
      (((inet_struct *) VARDATA_ANY(inetptr))->family)

#define ip_bits(inetptr) \
      (((inet_struct *) VARDATA_ANY(inetptr))->bits)

#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)
{
      switch (ip_family(inetptr))
      {
            case PGSQL_AF_INET:
                  return 4;
            case PGSQL_AF_INET6:
                  return 16;
            default:
                  return 0;
      }
}

/*
 * Common INET/CIDR input routine
 */
static inet *
network_in(char *src, bool is_cidr)
{
      int               bits;
      inet     *dst;

      dst = (inet *) palloc0(sizeof(inet));

      /*
       * 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 (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),
                                     is_cidr ? ip_addrsize(dst) : -1);
      if ((bits < 0) || (bits > ip_maxbits(dst)))
            ereport(ERROR,
                        (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
            /* translator: first %s is inet or cidr */
                         errmsg("invalid input syntax for type %s: \"%s\"",
                                    is_cidr ? "cidr" : "inet", src)));

      /*
       * 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.")));
      }

      ip_bits(dst) = bits;
      SET_INET_VARSIZE(dst);

      return dst;
}

Datum
inet_in(PG_FUNCTION_ARGS)
{
      char     *src = PG_GETARG_CSTRING(0);

      PG_RETURN_INET_P(network_in(src, false));
}

Datum
cidr_in(PG_FUNCTION_ARGS)
{
      char     *src = PG_GETARG_CSTRING(0);

      PG_RETURN_INET_P(network_in(src, true));
}


/*
 * Common INET/CIDR output routine
 */
static char *
network_out(inet *src, bool is_cidr)
{
      char        tmp[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255/128")];
      char     *dst;
      int               len;

      dst = inet_net_ntop(ip_family(src), ip_addr(src), ip_bits(src),
                                    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)
      {
            len = strlen(tmp);
            snprintf(tmp + len, sizeof(tmp) - len, "/%u", ip_bits(src));
      }

      return pstrdup(tmp);
}

Datum
inet_out(PG_FUNCTION_ARGS)
{
      inet     *src = PG_GETARG_INET_P(0);

      PG_RETURN_CSTRING(network_out(src, false));
}

Datum
cidr_out(PG_FUNCTION_ARGS)
{
      inet     *src = PG_GETARG_INET_P(0);

      PG_RETURN_CSTRING(network_out(src, true));
}


/*
 *          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.
 */
static inet *
network_recv(StringInfo buf, bool is_cidr)
{
      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));

      ip_family(addr) = pq_getmsgbyte(buf);
      if (ip_family(addr) != PGSQL_AF_INET &&
            ip_family(addr) != PGSQL_AF_INET6)
            ereport(ERROR,
                        (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
            /* 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),
            /* translator: %s is inet or cidr */
                         errmsg("invalid bits in external \"%s\" value",
                                    is_cidr ? "cidr" : "inet")));
      ip_bits(addr) = bits;
      i = pq_getmsgbyte(buf);       /* ignore is_cidr */
      nb = pq_getmsgbyte(buf);
      if (nb != ip_addrsize(addr))
            ereport(ERROR,
                        (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
            /* translator: %s is inet or cidr */
                         errmsg("invalid length in external \"%s\" value",
                                    is_cidr ? "cidr" : "inet")));

      addrptr = (char *) ip_addr(addr);
      for (i = 0; i < nb; i++)
            addrptr[i] = pq_getmsgbyte(buf);

      /*
       * Error check: CIDR values must not have any bits set beyond the masklen.
       */
      if (is_cidr)
      {
            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.")));
      }

      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));
}

Datum
cidr_recv(PG_FUNCTION_ARGS)
{
      StringInfo  buf = (StringInfo) PG_GETARG_POINTER(0);

      PG_RETURN_INET_P(network_recv(buf, true));
}


/*
 *          network_send            - converts inet to binary format
 */
static bytea *
network_send(inet *addr, bool is_cidr)
{
      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);
      nb = ip_addrsize(addr);
      if (nb < 0)
            nb = 0;
      pq_sendbyte(&buf, nb);
      addrptr = (char *) ip_addr(addr);
      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));
}

Datum
cidr_send(PG_FUNCTION_ARGS)
{
      inet     *addr = PG_GETARG_INET_P(0);

      PG_RETURN_BYTEA_P(network_send(addr, true));
}


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;

      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);
}

/*
 *    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.
 */

static int32
network_cmp_internal(inet *a1, inet *a2)
{
      if (ip_family(a1) == ip_family(a2))
      {
            int               order;

            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 bitncmp(ip_addr(a1), ip_addr(a2), ip_maxbits(a1));
      }

      return ip_family(a1) - ip_family(a2);
}

Datum
network_cmp(PG_FUNCTION_ARGS)
{
      inet     *a1 = PG_GETARG_INET_P(0);
      inet     *a2 = PG_GETARG_INET_P(1);

      PG_RETURN_INT32(network_cmp_internal(a1, a2));
}

/*
 *    Boolean ordering tests.
 */
Datum
network_lt(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_le(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_eq(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_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);
}

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)
{
      inet     *a1 = PG_GETARG_INET_P(0);
      inet     *a2 = PG_GETARG_INET_P(1);

      if (ip_family(a1) == ip_family(a2))
      {
            PG_RETURN_BOOL(ip_bits(a1) > ip_bits(a2)
                               && bitncmp(ip_addr(a1), ip_addr(a2), ip_bits(a2)) == 0);
      }

      PG_RETURN_BOOL(false);
}

Datum
network_subeq(PG_FUNCTION_ARGS)
{
      inet     *a1 = PG_GETARG_INET_P(0);
      inet     *a2 = PG_GETARG_INET_P(1);

      if (ip_family(a1) == ip_family(a2))
      {
            PG_RETURN_BOOL(ip_bits(a1) >= ip_bits(a2)
                               && bitncmp(ip_addr(a1), ip_addr(a2), ip_bits(a2)) == 0);
      }

      PG_RETURN_BOOL(false);
}

Datum
network_sup(PG_FUNCTION_ARGS)
{
      inet     *a1 = PG_GETARG_INET_P(0);
      inet     *a2 = PG_GETARG_INET_P(1);

      if (ip_family(a1) == ip_family(a2))
      {
            PG_RETURN_BOOL(ip_bits(a1) < ip_bits(a2)
                               && bitncmp(ip_addr(a1), ip_addr(a2), ip_bits(a1)) == 0);
      }

      PG_RETURN_BOOL(false);
}

Datum
network_supeq(PG_FUNCTION_ARGS)
{
      inet     *a1 = PG_GETARG_INET_P(0);
      inet     *a2 = PG_GETARG_INET_P(1);

      if (ip_family(a1) == ip_family(a2))
      {
            PG_RETURN_BOOL(ip_bits(a1) <= ip_bits(a2)
                               && bitncmp(ip_addr(a1), ip_addr(a2), ip_bits(a1)) == 0);
      }

      PG_RETURN_BOOL(false);
}

/*
 * Extract data from a network datatype.
 */
Datum
network_host(PG_FUNCTION_ARGS)
{
      inet     *ip = PG_GETARG_INET_P(0);
      char     *ptr;
      char        tmp[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255/128")];

      /* 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)
            ereport(ERROR,
                        (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
                         errmsg("could not format inet value: %m")));

      /* Suppress /n if present (shouldn't happen now) */
      if ((ptr = strchr(tmp, '/')) != NULL)
            *ptr = '\0';

      PG_RETURN_TEXT_P(cstring_to_text(tmp));
}

/*
 * 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);
      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),
                                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));
      }

      PG_RETURN_TEXT_P(cstring_to_text(tmp));
}

Datum
inet_abbrev(PG_FUNCTION_ARGS)
{
      inet     *ip = PG_GETARG_INET_P(0);
      char     *dst;
      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")));

      PG_RETURN_TEXT_P(cstring_to_text(tmp));
}

Datum
cidr_abbrev(PG_FUNCTION_ARGS)
{
      inet     *ip = PG_GETARG_INET_P(0);
      char     *dst;
      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")));

      PG_RETURN_TEXT_P(cstring_to_text(tmp));
}

Datum
network_masklen(PG_FUNCTION_ARGS)
{
      inet     *ip = PG_GETARG_INET_P(0);

      PG_RETURN_INT32(ip_bits(ip));
}

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(sizeof(inet));

      if (ip_family(ip) == PGSQL_AF_INET)
            maxbytes = 4;
      else
            maxbytes = 16;

      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);
      SET_INET_VARSIZE(dst);

      PG_RETURN_INET_P(dst);
}

Datum
network_network(PG_FUNCTION_ARGS)
{
      inet     *ip = PG_GETARG_INET_P(0);
      inet     *dst;
      int byte;
      int               bits;
      unsigned char mask;
      unsigned char *a,
                     *b;

      /* make sure any unused bits are zeroed */
      dst = (inet *) palloc0(sizeof(inet));

      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);
      SET_INET_VARSIZE(dst);

      PG_RETURN_INET_P(dst);
}

Datum
network_netmask(PG_FUNCTION_ARGS)
{
      inet     *ip = PG_GETARG_INET_P(0);
      inet     *dst;
      int byte;
      int               bits;
      unsigned char mask;
      unsigned char *b;

      /* make sure any unused bits are zeroed */
      dst = (inet *) palloc0(sizeof(inet));

      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;
            }

            b[byte] = mask;
            byte    ++;
      }

      ip_family(dst) = ip_family(ip);
      ip_bits(dst) = ip_maxbits(ip);
      SET_INET_VARSIZE(dst);

      PG_RETURN_INET_P(dst);
}

Datum
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(sizeof(inet));

      if (ip_family(ip) == PGSQL_AF_INET)
            maxbytes = 4;
      else
            maxbytes = 16;

      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;
            }

            b[byte] = mask;
            byte    --;
      }

      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);
                        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);
                        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;
                  }
      }

      /*
       * 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;
}

/*
 * 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
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 (IS_HIGHBIT_SET(lb) != IS_HIGHBIT_SET(rb))
            {
                  if (IS_HIGHBIT_SET(lb))
                        return 1;
                  return -1;
            }
            lb <<= 1;
            rb <<= 1;
      }
      return 0;
}

static bool
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;
      }
      Assert(bits <= maxbits);

      if (bits == maxbits)
            return true;

      byte = bits / 8;

      nbits = bits % 8;
      mask = 0xff;
      if (bits != 0)
            mask >>= nbits;

      while (byte <maxbytes)
      {
            if ((a[byte] &mask) != 0)
                  return false;
            mask = 0xff;
            byte    ++;
      }

      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),
                                             Int32GetDatum(-1));
}


/*
 * IP address that the client is connecting from (NULL if Unix socket)
 */
Datum
inet_client_addr(PG_FUNCTION_ARGS)
{
      Port     *port = MyProcPort;
      char        remote_host[NI_MAXHOST];
      int               ret;

      if (port == NULL)
            PG_RETURN_NULL();

      switch (port->raddr.addr.ss_family)
      {
            case AF_INET:
#ifdef HAVE_IPV6
            case AF_INET6:
#endif
                  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)
{
      Port     *port = MyProcPort;
      char        remote_port[NI_MAXSERV];
      int               ret;

      if (port == NULL)
            PG_RETURN_NULL();

      switch (port->raddr.addr.ss_family)
      {
            case AF_INET:
#ifdef HAVE_IPV6
            case AF_INET6:
#endif
                  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)
{
      Port     *port = MyProcPort;
      char        local_host[NI_MAXHOST];
      int               ret;

      if (port == NULL)
            PG_RETURN_NULL();

      switch (port->laddr.addr.ss_family)
      {
            case AF_INET:
#ifdef HAVE_IPV6
            case AF_INET6:
#endif
                  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)
{
      Port     *port = MyProcPort;
      char        local_port[NI_MAXSERV];
      int               ret;

      if (port == NULL)
            PG_RETURN_NULL();

      switch (port->laddr.addr.ss_family)
      {
            case AF_INET:
#ifdef HAVE_IPV6
            case AF_INET6:
#endif
                  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));

      {
            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
      {
            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
      {
            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));

      {
            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;

                  /*
                   * We have to be careful about right-shifting addend because
                   * 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;
            }

            /*
             * 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)
{
      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)
{
      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
      {
            /*
             * 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.
             */
            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)
            {
                  int               lobyte;

                  carry = pip[nb] + (~pip2[nb] & 0xFF) + carry;
                  lobyte = carry & 0xFF;
                  if (byte <sizeof(int64))
                  {
                        res |= ((int64) lobyte) << (byte *8);
                  }
                  else
                  {
                        /*
                         * 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    ++;
            }

            /*
             * 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
 * 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
 * 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)
      {
            char     *pct = strchr(addr, '%');

            if (pct)
                  *pct = '\0';
      }
#endif
}

Generated by  Doxygen 1.6.0   Back to index