root/usr/src/cmd/cmd-inet/usr.sbin/in.routed/common.c
/*
 * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

/*
 * Common (shared) routines used by in.routed daemon and the
 * the rtquery utility program
 */

#include "defs.h"
#include <ctype.h>

/* Return the classical netmask for an IP address. */
in_addr_t                       /* host byte order */
std_mask(in_addr_t addr)        /* network byte order */
{
        addr = ntohl(addr);

        if (addr == 0)          /* default route has mask 0 */
                return (0);
        if (IN_CLASSA(addr))
                return (IN_CLASSA_NET);
        if (IN_CLASSB(addr))
                return (IN_CLASSB_NET);
        if (IN_CLASSC(addr))
                return (IN_CLASSC_NET);
        return (IN_CLASSE_NET);
}

/*
 * Get a network number as a name or a number, with an optional "/xx"
 * netmask.
 */
boolean_t                                       /* 0=bad */
getnet(const char *name,
    in_addr_t *netp,                    /* network in host byte order */
    in_addr_t *maskp)                   /* masks are always in host order */
{
        int i;
        struct netent *np;
        in_addr_t mask;                 /* in host byte order */
        struct in_addr in;              /* a network and so host byte order */
        char hname[MAXHOSTNAMELEN+1];
        char *mname, *p;


        /*
         * The "name" argument of this function can be one of
         * the follwoing:
         *      a) network name/mask
         *      b) network name
         *      c) network number/mask
         *      d) network number
         *      e) host IP address/mask
         *      f) host IP address
         *      g) "default"
         *
         * Detect and separate "1.2.3.4/24"
         */
        if (NULL != (mname = strrchr(name, '/'))) {
                i = (int)(mname - name);
                if (i > (int)sizeof (hname)-1)  /* name too long */
                        return (_B_FALSE);
                (void) memmove(hname, name, i);
                hname[i] = '\0';
                mname++;
                name = hname;
        }

        if ((in.s_addr = inet_network(name)) == (in_addr_t)-1) {
                if (mname == NULL && strcasecmp(name, "default") == 0)
                        in.s_addr = ntohl(RIP_DEFAULT);
                else if ((np = getnetbyname(name)) != NULL)
                        in.s_addr = np->n_net;
                else
                        return (_B_FALSE);
        }
        /* Left-align the host-byte-order result from above. */
        if (0 == (in.s_addr & 0xff000000))
                in.s_addr <<= 8;
        if (0 == (in.s_addr & 0xff000000))
                in.s_addr <<= 8;
        if (0 == (in.s_addr & 0xff000000))
                in.s_addr <<= 8;

        if (mname == NULL) {
                mask = std_mask(htonl(in.s_addr));
                if ((~mask & in.s_addr) != 0)
                        mask = HOST_MASK;
        } else {
                mask = (uint32_t)strtoul(mname, &p, 0);
                if (*p != '\0' || mask > 32 || mname == p)
                        return (_B_FALSE);
                if (mask != 0)
                        mask = HOST_MASK << (32-mask);
        }

        /* must have mask of 0 with default */
        if (mask != 0 && in.s_addr == RIP_DEFAULT)
                return (_B_FALSE);
        /* no host bits allowed in a network number */
        if ((~mask & in.s_addr) != 0)
                return (_B_FALSE);
        /* require non-zero network number */
        if ((mask & in.s_addr) == 0 && in.s_addr != RIP_DEFAULT)
                return (_B_FALSE);
        if ((in.s_addr >> 24) == 0 && in.s_addr != RIP_DEFAULT)
                return (_B_FALSE);
        if ((in.s_addr >> 24) == 0xff)
                return (_B_FALSE);

        *netp = in.s_addr;
        *maskp = mask;
        return (_B_TRUE);
}

/*
 * Convert string to printable characters
 */
char *
qstring(const uchar_t *srcp, int len)
{
        /*
         * Authentication schemes for RIPv2 uses the space of an
         * 20-octet route entry.
         */
        static char buf[8*20+1];
        char *prcp, *tmp_ptr;
        uchar_t c;
        const uchar_t *s2;

        s2 = srcp + len;
        while (s2 > srcp && *--s2 == '\0')
                len--;
        for (prcp = buf; len != 0 && prcp < &buf[sizeof (buf)-1]; len--) {
                c = *srcp++;
                if (isprint(c) && c != '\\') {
                        *prcp++ = c;
                        continue;
                }

                *prcp++ = '\\';
                tmp_ptr = strchr("\\\\\nn\rr\tt\bb\aa\ff", c);
                if (tmp_ptr != NULL)
                        *prcp++ = tmp_ptr[1];
                else
                        prcp += snprintf(prcp,
                            (sizeof (buf) - (strlen(buf)+1)), "%o", c);
        }
        *prcp = '\0';
        return (buf);
}

/* like strtok(), but honoring backslash and not changing the source string */
int                     /* 0=ok, -1=bad */
parse_quote(char **linep,       /* look here */
    const char *delims,         /* for these delimiters */
    char *delimp,               /* 0 or put found delimiter here */
    char *buf,                  /* copy token to here */
    int lim)                    /* at most this many bytes */
{
        char c = '\0', *pc;
        const char *p;


        pc =  *linep;
        if (*pc == '\0')
                return (-1);

        while (lim != 0) {
                c = *pc++;
                if (c == '\0')
                        break;

                if (c == '\\' && *pc != '\0') {
                        c = *pc++;
                        switch (c) {
                        case 'n':
                                c = '\n';
                                break;
                        case 'r':
                                c = '\r';
                                break;
                        case 't':
                                c = '\t';
                                break;
                        case 'b':
                                c = '\b';
                        }
                        if (c >= '0' && c <= '7') {
                                c -= '0';
                                if (*pc >= '0' && *pc <= '7') {
                                        c = (c<<3)+(*pc++ - '0');
                                        if (*pc >= '0' && *pc <= '7')
                                            c = (c<<3)+(*pc++ - '0');
                                }
                        }

                } else {
                        for (p = delims; *p != '\0'; ++p) {
                                if (*p == c || isspace(c) && *p == ' ')
                                        goto exit;
                        }
                }

                *buf++ = c;
                --lim;
        }
exit:
        if (lim == 0)
                return (-1);

        *buf = '\0';                    /* terminate copy of token */
        if (delimp != NULL)
                *delimp = c;            /* return delimiter */
        *linep = pc-1;                  /* say where we ended */
        return (0);
}

/*
 * Find the option buffer in the msg corresponding to cmsg_type.
 */
void *
find_ancillary(struct msghdr *msg, int cmsg_type)
{
        struct cmsghdr *cmsg;

        for (cmsg = CMSG_FIRSTHDR(msg); cmsg != NULL;
            cmsg = CMSG_NXTHDR(msg, cmsg)) {
                if (cmsg->cmsg_level == IPPROTO_IP &&
                    cmsg->cmsg_type == cmsg_type) {
                        return (CMSG_DATA(cmsg));
                }
        }
        return (NULL);
}