Subversion Repositories planix.SVN

Rev

Rev 2 | Blame | Compare with Previous | Last modification | View Log | RSS feed

#include <u.h>
#include <libc.h>
#include <ctype.h>
#include <ip.h>

char*
v4parseip(uchar *to, char *from)
{
        int i;
        char *p;

        p = from;
        for(i = 0; i < 4 && *p; i++){
                to[i] = strtoul(p, &p, 0);
                if(*p == '.')
                        p++;
        }
        switch(CLASS(to)){
        case 0:  /* class A - 1 uchar net */
        case 1:
                if(i == 3){
                        to[3] = to[2];
                        to[2] = to[1];
                        to[1] = 0;
                } else if (i == 2){
                        to[3] = to[1];
                        to[1] = 0;
                }
                break;
        case 2: /* class B - 2 uchar net */
                if(i == 3){
                        to[3] = to[2];
                        to[2] = 0;
                }
                break;
        }
        return p;
}

static int
ipcharok(int c)
{
        return c == '.' || c == ':' || isascii(c) && isxdigit(c);
}

static int
delimchar(int c)
{
        if(c == '\0')
                return 1;
        if(c == '.' || c == ':' || isascii(c) && isalnum(c))
                return 0;
        return 1;
}

/*
 * `from' may contain an address followed by other characters,
 * at least in /boot, so we permit whitespace (and more) after the address.
 * we do ensure that "delete" cannot be parsed as "de::".
 *
 * some callers don't check the return value for errors, so
 * set `to' to something distinctive in the case of a parse error.
 */
vlong
parseip(uchar *to, char *from)
{
        int i, elipsis = 0, v4 = 1;
        ulong x;
        char *p, *op;

        memset(to, 0, IPaddrlen);
        p = from;
        for(i = 0; i < IPaddrlen && ipcharok(*p); i+=2){
                op = p;
                x = strtoul(p, &p, 16);
                if((*p == '.' && i <= IPaddrlen-4) || (*p == 0 && i == 0)){
                        /* ends with v4 */
                        p = v4parseip(to+i, op);
                        i += 4;
                        break;
                }
                /* v6: at most 4 hex digits, followed by colon or delim */
                if(x != (ushort)x || *p != ':' && !delimchar(*p)) {
                        memset(to, 0, IPaddrlen);
                        return -1;                      /* parse error */
                }
                to[i] = x>>8;
                to[i+1] = x;
                if(*p == ':'){
                        v4 = 0;
                        if(*++p == ':'){        /* :: is elided zero short(s) */
                                if (elipsis) {
                                        memset(to, 0, IPaddrlen);
                                        return -1;      /* second :: */
                                }
                                elipsis = i+2;
                                p++;
                        }
                } else if (p == op)             /* strtoul made no progress? */
                        break;
        }
        if (p == from || !delimchar(*p)) {
                memset(to, 0, IPaddrlen);
                return -1;                              /* parse error */
        }
        if(i < IPaddrlen){
                memmove(&to[elipsis+IPaddrlen-i], &to[elipsis], i-elipsis);
                memset(&to[elipsis], 0, IPaddrlen-i);
        }
        if(v4){
                to[10] = to[11] = 0xff;
                return nhgetl(to + IPv4off);
        } else
                return 6;
}

/*
 *  hack to allow ip v4 masks to be entered in the old
 *  style
 */
vlong
parseipmask(uchar *to, char *from)
{
        int i, w;
        vlong x;
        uchar *p;

        if(*from == '/'){
                /* as a number of prefix bits */
                i = atoi(from+1);
                if(i < 0)
                        i = 0;
                if(i > 128)
                        i = 128;
                w = i;
                memset(to, 0, IPaddrlen);
                for(p = to; i >= 8; i -= 8)
                        *p++ = 0xff;
                if(i > 0)
                        *p = ~((1<<(8-i))-1);
                x = nhgetl(to+IPv4off);
                /*
                 * identify as ipv6 if the mask is inexpressible as a v4 mask
                 * (because it has too few mask bits).  Arguably, we could
                 * always return 6 here.
                 */
                if (w < 8*(IPaddrlen-IPv4addrlen))
                        return 6;
        } else {
                /* as a straight v4 bit mask */
                x = parseip(to, from);
                if (x != -1)
                        x = (ulong)nhgetl(to + IPv4off);
                if(memcmp(to, v4prefix, IPv4off) == 0)
                        memset(to, 0xff, IPv4off);
        }
        return x;
}

/*
 *  parse a v4 ip address/mask in cidr format
 */
char*
v4parsecidr(uchar *addr, uchar *mask, char *from)
{
        int i;
        char *p;
        uchar *a;

        p = v4parseip(addr, from);

        if(*p == '/'){
                /* as a number of prefix bits */
                i = strtoul(p+1, &p, 0);
                if(i > 32)
                        i = 32;
                memset(mask, 0, IPv4addrlen);
                for(a = mask; i >= 8; i -= 8)
                        *a++ = 0xff;
                if(i > 0)
                        *a = ~((1<<(8-i))-1);
        } else 
                memcpy(mask, defmask(addr), IPv4addrlen);
        return p;
}