akaros/user/iplib/parseip.c
<<
>>
Prefs
   1/*
   2 * This file is part of the UCB release of Plan 9. It is subject to the license
   3 * terms in the LICENSE file found in the top-level directory of this
   4 * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
   5 * part of the UCB release of Plan 9, including this file, may be copied,
   6 * modified, propagated, or distributed except according to the terms contained
   7 * in the LICENSE file.
   8 */
   9#include <stdlib.h>
  10
  11#include <iplib/iplib.h>
  12#include <parlib/parlib.h>
  13#include <signal.h>
  14#include <stdio.h>
  15#include <unistd.h>
  16
  17static int isascii(int c)
  18{
  19        return ((c >= 0) && (c <= 128));
  20}
  21static int isalnum(int c)
  22{
  23        return ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
  24                (c >= '0' && c <= '9'));
  25}
  26static int isdigit(int c)
  27{
  28        return ((c >= '0' && c <= '9'));
  29}
  30static int isxdigit(int c)
  31{
  32        return ((c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F') ||
  33                (c >= '0' && c <= '9'));
  34}
  35
  36char *v4parseip(uint8_t *to, char *from)
  37{
  38        int i;
  39        char *p;
  40
  41        p = from;
  42        for (i = 0; i < 4 && *p; i++) {
  43                to[i] = strtoul(p, &p, 0);
  44                if (*p == '.')
  45                        p++;
  46        }
  47        switch (CLASS(to)) {
  48        case 0: /* class A - 1 uchar net */
  49        case 1:
  50                if (i == 3) {
  51                        to[3] = to[2];
  52                        to[2] = to[1];
  53                        to[1] = 0;
  54                } else if (i == 2) {
  55                        to[3] = to[1];
  56                        to[1] = 0;
  57                }
  58                break;
  59        case 2: /* class B - 2 uchar net */
  60                if (i == 3) {
  61                        to[3] = to[2];
  62                        to[2] = 0;
  63                }
  64                break;
  65        }
  66        return p;
  67}
  68
  69static int ipcharok(int c)
  70{
  71        return c == '.' || c == ':' || isascii(c) && isxdigit(c);
  72}
  73
  74static int delimchar(int c)
  75{
  76        if (c == '\0')
  77                return 1;
  78        if (c == '.' || c == ':' || isascii(c) && isalnum(c))
  79                return 0;
  80        return 1;
  81}
  82
  83/*
  84 * `from' may contain an address followed by other characters,
  85 * at least in /boot, so we permit whitespace (and more) after the address.
  86 * we do ensure that "delete" cannot be parsed as "de::".
  87 *
  88 * some callers don't check the return value for errors, so
  89 * set `to' to something distinctive in the case of a parse error.
  90 */
  91int64_t parseip(uint8_t *to, char *from)
  92{
  93        int i, elipsis = 0, v4 = 1;
  94        uint32_t x;
  95        char *p, *op;
  96
  97        memset(to, 0, IPaddrlen);
  98        p = from;
  99        for (i = 0; i < IPaddrlen && ipcharok(*p); i += 2) {
 100                op = p;
 101                x = strtoul(p, &p, 16);
 102                if (*p == '.' || (*p == 0 && i == 0)) { /* ends with v4? */
 103                        p = v4parseip(to + i, op);
 104                        i += 4;
 105                        break;
 106                }
 107                /* v6: at most 4 hex digits, followed by colon or delim */
 108                if (x != (uint16_t)x || *p != ':' && !delimchar(*p)) {
 109                        memset(to, 0, IPaddrlen);
 110                        return -1; /* parse error */
 111                }
 112                to[i] = x >> 8;
 113                to[i + 1] = x;
 114                if (*p == ':') {
 115                        v4 = 0;
 116                        if (*++p == ':') { /* :: is elided zero short(s) */
 117                                if (elipsis) {
 118                                        memset(to, 0, IPaddrlen);
 119                                        return -1; /* second :: */
 120                                }
 121                                elipsis = i + 2;
 122                                p++;
 123                        }
 124                } else if (p == op) /* strtoul made no progress? */
 125                        break;
 126        }
 127        if (p == from || !delimchar(*p)) {
 128                memset(to, 0, IPaddrlen);
 129                return -1; /* parse error */
 130        }
 131        if (i < IPaddrlen) {
 132                memmove(&to[elipsis + IPaddrlen - i], &to[elipsis],
 133                        i - elipsis);
 134                memset(&to[elipsis], 0, IPaddrlen - i);
 135        }
 136        if (v4) {
 137                to[10] = to[11] = 0xff;
 138                return nhgetl(to + IPv4off);
 139        } else
 140                return 6;
 141}
 142
 143/*
 144 *  hack to allow ip v4 masks to be entered in the old
 145 *  style
 146 */
 147int64_t parseipmask(uint8_t *to, char *from)
 148{
 149        int i, w;
 150        int64_t x;
 151        uint8_t *p;
 152
 153        if (*from == '/') {
 154                /* as a number of prefix bits */
 155                i = atoi(from + 1);
 156                if (i < 0)
 157                        i = 0;
 158                if (i > 128)
 159                        i = 128;
 160                w = i;
 161                memset(to, 0, IPaddrlen);
 162                for (p = to; i >= 8; i -= 8)
 163                        *p++ = 0xff;
 164                if (i > 0)
 165                        *p = ~((1 << (8 - i)) - 1);
 166                x = nhgetl(to + IPv4off);
 167                /*
 168                 * identify as ipv6 if the mask is inexpressible as a v4 mask
 169                 * (because it has too few mask bits).  Arguably, we could
 170                 * always return 6 here.
 171                 */
 172                if (w < 8 * (IPaddrlen - IPv4addrlen))
 173                        return 6;
 174        } else {
 175                /* as a straight v4 bit mask */
 176                x = parseip(to, from);
 177                if (x != -1)
 178                        x = (uint32_t)nhgetl(to + IPv4off);
 179                if (memcmp(to, v4prefix, IPv4off) == 0)
 180                        memset(to, 0xff, IPv4off);
 181        }
 182        return x;
 183}
 184
 185/*
 186 *  parse a v4 ip address/mask in cidr format
 187 */
 188char *v4parsecidr(uint8_t *addr, uint8_t *mask, char *from)
 189{
 190        int i;
 191        char *p;
 192        uint8_t *a;
 193
 194        p = v4parseip(addr, from);
 195
 196        if (*p == '/') {
 197                /* as a number of prefix bits */
 198                i = strtoul(p + 1, &p, 0);
 199                /* We might have been passed a v6 mask - the signal for that
 200                 * will be having more than 32 bits. */
 201                if (i >= 32)
 202                        i -= 128 - 32;
 203                memset(mask, 0, IPv4addrlen);
 204                for (a = mask; i >= 8; i -= 8)
 205                        *a++ = 0xff;
 206                if (i > 0)
 207                        *a = ~((1 << (8 - i)) - 1);
 208        } else
 209                memcpy(mask, defmask(addr), IPv4addrlen);
 210        return p;
 211}
 212