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