|  | /* | 
|  | * This file is part of the UCB release of Plan 9. It is subject to the license | 
|  | * terms in the LICENSE file found in the top-level directory of this | 
|  | * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No | 
|  | * part of the UCB release of Plan 9, including this file, may be copied, | 
|  | * modified, propagated, or distributed except according to the terms contained | 
|  | * in the LICENSE file. | 
|  | */ | 
|  | #include <stdlib.h> | 
|  |  | 
|  | #include <iplib/iplib.h> | 
|  | #include <parlib/parlib.h> | 
|  | #include <signal.h> | 
|  | #include <stdio.h> | 
|  | #include <unistd.h> | 
|  |  | 
|  | static int isascii(int c) | 
|  | { | 
|  | return ((c >= 0) && (c <= 128)); | 
|  | } | 
|  | static int isalnum(int c) | 
|  | { | 
|  | return ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || | 
|  | (c >= '0' && c <= '9')); | 
|  | } | 
|  | static int isdigit(int c) | 
|  | { | 
|  | return ((c >= '0' && c <= '9')); | 
|  | } | 
|  | static int isxdigit(int c) | 
|  | { | 
|  | return ((c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F') || | 
|  | (c >= '0' && c <= '9')); | 
|  | } | 
|  |  | 
|  | char *v4parseip(uint8_t *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. | 
|  | */ | 
|  | int64_t parseip(uint8_t *to, char *from) | 
|  | { | 
|  | int i, elipsis = 0, v4 = 1; | 
|  | uint32_t 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 == '.' || (*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 != (uint16_t)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 | 
|  | */ | 
|  | int64_t parseipmask(uint8_t *to, char *from) | 
|  | { | 
|  | int i, w; | 
|  | int64_t x; | 
|  | uint8_t *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 = (uint32_t)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(uint8_t *addr, uint8_t *mask, char *from) | 
|  | { | 
|  | int i; | 
|  | char *p; | 
|  | uint8_t *a; | 
|  |  | 
|  | p = v4parseip(addr, from); | 
|  |  | 
|  | if (*p == '/') { | 
|  | /* as a number of prefix bits */ | 
|  | i = strtoul(p + 1, &p, 0); | 
|  | /* We might have been passed a v6 mask - the signal for that | 
|  | * will be having more than 32 bits. */ | 
|  | if (i >= 32) | 
|  | i -= 128 - 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; | 
|  | } |