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

inet_net_pton.c

/*
 * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
 * Copyright (c) 1996,1999 by Internet Software Consortium.
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
 * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 *
 *      $PostgreSQL$
 */

#if defined(LIBC_SCCS) && !defined(lint)
static const char rcsid[] = "Id: inet_net_pton.c,v 1.4.2.3 2004/03/17 00:40:11 marka Exp $";
#endif

#include "postgres.h"

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

#include "utils/builtins.h"
#include "utils/inet.h"


static int  inet_net_pton_ipv4(const char *src, u_char *dst);
static int  inet_cidr_pton_ipv4(const char *src, u_char *dst, size_t size);
static int  inet_net_pton_ipv6(const char *src, u_char *dst);
static int  inet_cidr_pton_ipv6(const char *src, u_char *dst, size_t size);


/*
 * int
 * inet_net_pton(af, src, dst, size)
 *    convert network number from presentation to network format.
 *    accepts hex octets, hex strings, decimal octets, and /CIDR.
 *    "size" is in bytes and describes "dst".
 * return:
 *    number of bits, either imputed classfully or specified with /CIDR,
 *    or -1 if some failure occurred (check errno).  ENOENT means it was
 *    not a valid network specification.
 * author:
 *    Paul Vixie (ISC), June 1996
 *
 * Changes:
 *    I added the inet_cidr_pton function (also from Paul) and changed
 *    the names to reflect their current use.
 *
 */
int
inet_net_pton(int af, const char *src, void *dst, size_t size)
{
      switch (af)
      {
            case PGSQL_AF_INET:
                  return size == -1 ?
                        inet_net_pton_ipv4(src, dst) :
                        inet_cidr_pton_ipv4(src, dst, size);
            case PGSQL_AF_INET6:
                  return size == -1 ?
                        inet_net_pton_ipv6(src, dst) :
                        inet_cidr_pton_ipv6(src, dst, size);
            default:
                  errno = EAFNOSUPPORT;
                  return (-1);
      }
}

/*
 * static int
 * inet_cidr_pton_ipv4(src, dst, size)
 *    convert IPv4 network number from presentation to network format.
 *    accepts hex octets, hex strings, decimal octets, and /CIDR.
 *    "size" is in bytes and describes "dst".
 * return:
 *    number of bits, either imputed classfully or specified with /CIDR,
 *    or -1 if some failure occurred (check errno).  ENOENT means it was
 *    not an IPv4 network specification.
 * note:
 *    network byte order assumed.  this means 192.5.5.240/28 has
 *    0b11110000 in its fourth octet.
 * author:
 *    Paul Vixie (ISC), June 1996
 */
static int
inet_cidr_pton_ipv4(const char *src, u_char *dst, size_t size)
{
      static const char xdigits[] = "0123456789abcdef";
      static const char digits[] = "0123456789";
      int               n,
                        ch,
                        tmp = 0,
                        dirty,
                        bits;
      const u_char *odst = dst;

      ch = *src++;
      if (ch == '0' && (src[0] == 'x' || src[0] == 'X')
            && isxdigit((unsigned char) src[1]))
      {
            /* Hexadecimal: Eat nybble string. */
            if (size <= 0U)
                  goto emsgsize;
            dirty = 0;
            src++;                              /* skip x or X. */
            while ((ch = *src++) != '\0' && isxdigit((unsigned char) ch))
            {
                  if (isupper((unsigned char) ch))
                        ch = tolower((unsigned char) ch);
                  n = strchr(xdigits, ch) - xdigits;
                  assert(n >= 0 && n <= 15);
                  if (dirty == 0)
                        tmp = n;
                  else
                        tmp = (tmp << 4) | n;
                  if (++dirty == 2)
                  {
                        if (size-- <= 0U)
                              goto emsgsize;
                        *dst++ = (u_char) tmp;
                        dirty = 0;
                  }
            }
            if (dirty)
            {                                   /* Odd trailing nybble? */
                  if (size-- <= 0U)
                        goto emsgsize;
                  *dst++ = (u_char) (tmp << 4);
            }
      }
      else if (isdigit((unsigned char) ch))
      {
            /* Decimal: eat dotted digit string. */
            for (;;)
            {
                  tmp = 0;
                  do
                  {
                        n = strchr(digits, ch) - digits;
                        assert(n >= 0 && n <= 9);
                        tmp *= 10;
                        tmp += n;
                        if (tmp > 255)
                              goto enoent;
                  } while ((ch = *src++) != '\0' &&
                               isdigit((unsigned char) ch));
                  if (size-- <= 0U)
                        goto emsgsize;
                  *dst++ = (u_char) tmp;
                  if (ch == '\0' || ch == '/')
                        break;
                  if (ch != '.')
                        goto enoent;
                  ch = *src++;
                  if (!isdigit((unsigned char) ch))
                        goto enoent;
            }
      }
      else
            goto enoent;

      bits = -1;
      if (ch == '/' && isdigit((unsigned char) src[0]) && dst > odst)
      {
            /* CIDR width specifier.  Nothing can follow it. */
            ch = *src++;                  /* Skip over the /. */
            bits = 0;
            do
            {
                  n = strchr(digits, ch) - digits;
                  assert(n >= 0 && n <= 9);
                  bits *= 10;
                  bits += n;
            } while ((ch = *src++) != '\0' && isdigit((unsigned char) ch));
            if (ch != '\0')
                  goto enoent;
            if (bits > 32)
                  goto emsgsize;
      }

      /* Firey death and destruction unless we prefetched EOS. */
      if (ch != '\0')
            goto enoent;

      /* If nothing was written to the destination, we found no address. */
      if (dst == odst)
            goto enoent;
      /* If no CIDR spec was given, infer width from net class. */
      if (bits == -1)
      {
            if (*odst >= 240)       /* Class E */
                  bits = 32;
            else if (*odst >= 224)  /* Class D */
                  bits = 8;
            else if (*odst >= 192)  /* Class C */
                  bits = 24;
            else if (*odst >= 128)  /* Class B */
                  bits = 16;
            else
                  /* Class A */
                  bits = 8;
            /* If imputed mask is narrower than specified octets, widen. */
            if (bits < ((dst - odst) * 8))
                  bits = (dst - odst) * 8;

            /*
             * If there are no additional bits specified for a class D address
             * adjust bits to 4.
             */
            if (bits == 8 && *odst == 224)
                  bits = 4;
      }
      /* Extend network to cover the actual mask. */
      while (bits > ((dst - odst) * 8))
      {
            if (size-- <= 0U)
                  goto emsgsize;
            *dst++ = '\0';
      }
      return (bits);

enoent:
      errno = ENOENT;
      return (-1);

emsgsize:
      errno = EMSGSIZE;
      return (-1);
}

/*
 * int
 * inet_net_pton(af, src, dst, *bits)
 *    convert network address from presentation to network format.
 *    accepts inet_pton()'s input for this "af" plus trailing "/CIDR".
 *    "dst" is assumed large enough for its "af".  "bits" is set to the
 *    /CIDR prefix length, which can have defaults (like /32 for IPv4).
 * return:
 *    -1 if an error occurred (inspect errno; ENOENT means bad format).
 *    0 if successful conversion occurred.
 * note:
 *    192.5.5.1/28 has a nonzero host part, which means it isn't a network
 *    as called for by inet_cidr_pton() but it can be a host address with
 *    an included netmask.
 * author:
 *    Paul Vixie (ISC), October 1998
 */
static int
inet_net_pton_ipv4(const char *src, u_char *dst)
{
      static const char digits[] = "0123456789";
      const u_char *odst = dst;
      int               n,
                        ch,
                        tmp,
                        bits;
      size_t            size = 4;

      /* Get the mantissa. */
      while (ch = *src++, isdigit((unsigned char) ch))
      {
            tmp = 0;
            do
            {
                  n = strchr(digits, ch) - digits;
                  assert(n >= 0 && n <= 9);
                  tmp *= 10;
                  tmp += n;
                  if (tmp > 255)
                        goto enoent;
            } while ((ch = *src++) != '\0' && isdigit((unsigned char) ch));
            if (size-- == 0)
                  goto emsgsize;
            *dst++ = (u_char) tmp;
            if (ch == '\0' || ch == '/')
                  break;
            if (ch != '.')
                  goto enoent;
      }

      /* Get the prefix length if any. */
      bits = -1;
      if (ch == '/' && isdigit((unsigned char) src[0]) && dst > odst)
      {
            /* CIDR width specifier.  Nothing can follow it. */
            ch = *src++;                  /* Skip over the /. */
            bits = 0;
            do
            {
                  n = strchr(digits, ch) - digits;
                  assert(n >= 0 && n <= 9);
                  bits *= 10;
                  bits += n;
            } while ((ch = *src++) != '\0' && isdigit((unsigned char) ch));
            if (ch != '\0')
                  goto enoent;
            if (bits > 32)
                  goto emsgsize;
      }

      /* Firey death and destruction unless we prefetched EOS. */
      if (ch != '\0')
            goto enoent;

      /* Prefix length can default to /32 only if all four octets spec'd. */
      if (bits == -1)
      {
            if (dst - odst == 4)
                  bits = 32;
            else
                  goto enoent;
      }

      /* If nothing was written to the destination, we found no address. */
      if (dst == odst)
            goto enoent;

      /* If prefix length overspecifies mantissa, life is bad. */
      if ((bits / 8) > (dst - odst))
            goto enoent;

      /* Extend address to four octets. */
      while (size-- > 0)
            *dst++ = 0;

      return bits;

enoent:
      errno = ENOENT;
      return (-1);

emsgsize:
      errno = EMSGSIZE;
      return (-1);
}

static int
getbits(const char *src, int *bitsp)
{
      static const char digits[] = "0123456789";
      int               n;
      int               val;
      char        ch;

      val = 0;
      n = 0;
      while ((ch = *src++) != '\0')
      {
            const char *pch;

            pch = strchr(digits, ch);
            if (pch != NULL)
            {
                  if (n++ != 0 && val == 0)     /* no leading zeros */
                        return (0);
                  val *= 10;
                  val += (pch - digits);
                  if (val > 128)          /* range */
                        return (0);
                  continue;
            }
            return (0);
      }
      if (n == 0)
            return (0);
      *bitsp = val;
      return (1);
}

static int
getv4(const char *src, u_char *dst, int *bitsp)
{
      static const char digits[] = "0123456789";
      u_char         *odst = dst;
      int               n;
      u_int       val;
      char        ch;

      val = 0;
      n = 0;
      while ((ch = *src++) != '\0')
      {
            const char *pch;

            pch = strchr(digits, ch);
            if (pch != NULL)
            {
                  if (n++ != 0 && val == 0)     /* no leading zeros */
                        return (0);
                  val *= 10;
                  val += (pch - digits);
                  if (val > 255)          /* range */
                        return (0);
                  continue;
            }
            if (ch == '.' || ch == '/')
            {
                  if (dst - odst > 3) /* too many octets? */
                        return (0);
                  *dst++ = val;
                  if (ch == '/')
                        return (getbits(src, bitsp));
                  val = 0;
                  n = 0;
                  continue;
            }
            return (0);
      }
      if (n == 0)
            return (0);
      if (dst - odst > 3)                 /* too many octets? */
            return (0);
      *dst++ = val;
      return (1);
}

static int
inet_net_pton_ipv6(const char *src, u_char *dst)
{
      return inet_cidr_pton_ipv6(src, dst, 16);
}

#define NS_IN6ADDRSZ 16
#define NS_INT16SZ 2
#define NS_INADDRSZ 4

static int
inet_cidr_pton_ipv6(const char *src, u_char *dst, size_t size)
{
      static const char xdigits_l[] = "0123456789abcdef",
                        xdigits_u[] = "0123456789ABCDEF";
      u_char            tmp[NS_IN6ADDRSZ],
                     *tp,
                     *endp,
                     *colonp;
      const char *xdigits,
                     *curtok;
      int               ch,
                        saw_xdigit;
      u_int       val;
      int               digits;
      int               bits;

      if (size < NS_IN6ADDRSZ)
            goto emsgsize;

      memset((tp = tmp), '\0', NS_IN6ADDRSZ);
      endp = tp + NS_IN6ADDRSZ;
      colonp = NULL;
      /* Leading :: requires some special handling. */
      if (*src == ':')
            if (*++src != ':')
                  goto enoent;
      curtok = src;
      saw_xdigit = 0;
      val = 0;
      digits = 0;
      bits = -1;
      while ((ch = *src++) != '\0')
      {
            const char *pch;

            if ((pch = strchr((xdigits = xdigits_l), ch)) == NULL)
                  pch = strchr((xdigits = xdigits_u), ch);
            if (pch != NULL)
            {
                  val <<= 4;
                  val |= (pch - xdigits);
                  if (++digits > 4)
                        goto enoent;
                  saw_xdigit = 1;
                  continue;
            }
            if (ch == ':')
            {
                  curtok = src;
                  if (!saw_xdigit)
                  {
                        if (colonp)
                              goto enoent;
                        colonp = tp;
                        continue;
                  }
                  else if (*src == '\0')
                        goto enoent;
                  if (tp + NS_INT16SZ > endp)
                        return (0);
                  *tp++ = (u_char) (val >> 8) & 0xff;
                  *tp++ = (u_char) val & 0xff;
                  saw_xdigit = 0;
                  digits = 0;
                  val = 0;
                  continue;
            }
            if (ch == '.' && ((tp + NS_INADDRSZ) <= endp) &&
                  getv4(curtok, tp, &bits) > 0)
            {
                  tp += NS_INADDRSZ;
                  saw_xdigit = 0;
                  break;                        /* '\0' was seen by inet_pton4(). */
            }
            if (ch == '/' && getbits(src, &bits) > 0)
                  break;
            goto enoent;
      }
      if (saw_xdigit)
      {
            if (tp + NS_INT16SZ > endp)
                  goto enoent;
            *tp++ = (u_char) (val >> 8) & 0xff;
            *tp++ = (u_char) val & 0xff;
      }
      if (bits == -1)
            bits = 128;

      endp = tmp + 16;

      if (colonp != NULL)
      {
            /*
             * Since some memmove()'s erroneously fail to handle overlapping
             * regions, we'll do the shift by hand.
             */
            const int   n = tp - colonp;
            int               i;

            if (tp == endp)
                  goto enoent;
            for (i = 1; i <= n; i++)
            {
                  endp[-i] = colonp[n - i];
                  colonp[n - i] = 0;
            }
            tp = endp;
      }
      if (tp != endp)
            goto enoent;

      /*
       * Copy out the result.
       */
      memcpy(dst, tmp, NS_IN6ADDRSZ);

      return (bits);

enoent:
      errno = ENOENT;
      return (-1);

emsgsize:
      errno = EMSGSIZE;
      return (-1);
}

Generated by  Doxygen 1.6.0   Back to index