Header And Logo

PostgreSQL
| The world's most advanced open source database.

Defines | Functions

inet_net_pton.c File Reference

#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"
Include dependency graph for inet_net_pton.c:

Go to the source code of this file.

Defines

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

Functions

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 (int af, const char *src, void *dst, size_t size)
static int getbits (const char *src, int *bitsp)
static int getv4 (const char *src, u_char *dst, int *bitsp)

Define Documentation

#define NS_IN6ADDRSZ   16

Definition at line 435 of file inet_net_pton.c.

Referenced by inet_cidr_pton_ipv6().

#define NS_INADDRSZ   4

Definition at line 437 of file inet_net_pton.c.

Referenced by inet_cidr_pton_ipv6().

#define NS_INT16SZ   2

Definition at line 436 of file inet_net_pton.c.

Referenced by inet_cidr_pton_ipv6().


Function Documentation

static int getbits ( const char *  src,
int *  bitsp 
) [static]

Definition at line 350 of file inet_net_pton.c.

References digits, NULL, and val.

Referenced by getv4(), and inet_cidr_pton_ipv6().

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

Definition at line 383 of file inet_net_pton.c.

References digits, getbits(), NULL, and val.

Referenced by inet_cidr_pton_ipv6().

{
    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_cidr_pton_ipv4 ( const char *  src,
u_char *  dst,
size_t  size 
) [static]

Definition at line 98 of file inet_net_pton.c.

References assert, and digits.

Referenced by inet_net_pton().

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

static int inet_cidr_pton_ipv6 ( const char *  src,
u_char *  dst,
size_t  size 
) [static]

Definition at line 440 of file inet_net_pton.c.

References digits, getbits(), getv4(), i, NS_IN6ADDRSZ, NS_INADDRSZ, NS_INT16SZ, NULL, and val.

Referenced by inet_net_pton(), and inet_net_pton_ipv6().

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

int inet_net_pton ( int  af,
const char *  src,
void *  dst,
size_t  size 
)

Definition at line 63 of file inet_net_pton.c.

References inet_cidr_pton_ipv4(), inet_cidr_pton_ipv6(), inet_net_pton_ipv4(), inet_net_pton_ipv6(), PGSQL_AF_INET, and PGSQL_AF_INET6.

Referenced by network_in().

{
    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_net_pton_ipv4 ( const char *  src,
u_char *  dst 
) [static]

Definition at line 261 of file inet_net_pton.c.

References assert, and digits.

Referenced by inet_net_pton().

{
    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 inet_net_pton_ipv6 ( const char *  src,
u_char *  dst 
) [static]

Definition at line 430 of file inet_net_pton.c.

References inet_cidr_pton_ipv6().

Referenced by inet_net_pton().

{
    return inet_cidr_pton_ipv6(src, dst, 16);
}