| /* |
| * 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; |
| } |