xref: /plan9/sys/src/libip/parseip.c (revision 03673ce56a3aeac7442d591897da78186bbb5bca)
13e12c5d1SDavid du Colombier #include <u.h>
23e12c5d1SDavid du Colombier #include <libc.h>
3ea58ad6fSDavid du Colombier #include <ctype.h>
47dd7cddfSDavid du Colombier #include <ip.h>
53e12c5d1SDavid du Colombier 
67dd7cddfSDavid du Colombier char*
v4parseip(uchar * to,char * from)77dd7cddfSDavid du Colombier v4parseip(uchar *to, char *from)
83e12c5d1SDavid du Colombier {
93e12c5d1SDavid du Colombier 	int i;
103e12c5d1SDavid du Colombier 	char *p;
113e12c5d1SDavid du Colombier 
123e12c5d1SDavid du Colombier 	p = from;
133e12c5d1SDavid du Colombier 	for(i = 0; i < 4 && *p; i++){
143e12c5d1SDavid du Colombier 		to[i] = strtoul(p, &p, 0);
153e12c5d1SDavid du Colombier 		if(*p == '.')
163e12c5d1SDavid du Colombier 			p++;
173e12c5d1SDavid du Colombier 	}
183e12c5d1SDavid du Colombier 	switch(CLASS(to)){
197dd7cddfSDavid du Colombier 	case 0:	/* class A - 1 uchar net */
203e12c5d1SDavid du Colombier 	case 1:
213e12c5d1SDavid du Colombier 		if(i == 3){
223e12c5d1SDavid du Colombier 			to[3] = to[2];
233e12c5d1SDavid du Colombier 			to[2] = to[1];
243e12c5d1SDavid du Colombier 			to[1] = 0;
253e12c5d1SDavid du Colombier 		} else if (i == 2){
263e12c5d1SDavid du Colombier 			to[3] = to[1];
273e12c5d1SDavid du Colombier 			to[1] = 0;
283e12c5d1SDavid du Colombier 		}
293e12c5d1SDavid du Colombier 		break;
307dd7cddfSDavid du Colombier 	case 2:	/* class B - 2 uchar net */
313e12c5d1SDavid du Colombier 		if(i == 3){
323e12c5d1SDavid du Colombier 			to[3] = to[2];
333e12c5d1SDavid du Colombier 			to[2] = 0;
343e12c5d1SDavid du Colombier 		}
353e12c5d1SDavid du Colombier 		break;
363e12c5d1SDavid du Colombier 	}
377dd7cddfSDavid du Colombier 	return p;
387dd7cddfSDavid du Colombier }
397dd7cddfSDavid du Colombier 
40ea58ad6fSDavid du Colombier static int
ipcharok(int c)41ea58ad6fSDavid du Colombier ipcharok(int c)
42ea58ad6fSDavid du Colombier {
43ea58ad6fSDavid du Colombier 	return c == '.' || c == ':' || isascii(c) && isxdigit(c);
44ea58ad6fSDavid du Colombier }
45ea58ad6fSDavid du Colombier 
46ea58ad6fSDavid du Colombier static int
delimchar(int c)47ea58ad6fSDavid du Colombier delimchar(int c)
48ea58ad6fSDavid du Colombier {
49ea58ad6fSDavid du Colombier 	if(c == '\0')
50ea58ad6fSDavid du Colombier 		return 1;
51ea58ad6fSDavid du Colombier 	if(c == '.' || c == ':' || isascii(c) && isalnum(c))
52ea58ad6fSDavid du Colombier 		return 0;
53ea58ad6fSDavid du Colombier 	return 1;
54ea58ad6fSDavid du Colombier }
55ea58ad6fSDavid du Colombier 
56ea58ad6fSDavid du Colombier /*
57ea58ad6fSDavid du Colombier  * `from' may contain an address followed by other characters,
58ea58ad6fSDavid du Colombier  * at least in /boot, so we permit whitespace (and more) after the address.
59ea58ad6fSDavid du Colombier  * we do ensure that "delete" cannot be parsed as "de::".
60ea58ad6fSDavid du Colombier  *
61ea58ad6fSDavid du Colombier  * some callers don't check the return value for errors, so
62ea58ad6fSDavid du Colombier  * set `to' to something distinctive in the case of a parse error.
63ea58ad6fSDavid du Colombier  */
64ea58ad6fSDavid du Colombier vlong
parseip(uchar * to,char * from)657dd7cddfSDavid du Colombier parseip(uchar *to, char *from)
667dd7cddfSDavid du Colombier {
677dd7cddfSDavid du Colombier 	int i, elipsis = 0, v4 = 1;
687dd7cddfSDavid du Colombier 	ulong x;
697dd7cddfSDavid du Colombier 	char *p, *op;
707dd7cddfSDavid du Colombier 
717dd7cddfSDavid du Colombier 	memset(to, 0, IPaddrlen);
727dd7cddfSDavid du Colombier 	p = from;
73ea58ad6fSDavid du Colombier 	for(i = 0; i < IPaddrlen && ipcharok(*p); i+=2){
747dd7cddfSDavid du Colombier 		op = p;
757dd7cddfSDavid du Colombier 		x = strtoul(p, &p, 16);
76*03673ce5SDavid du Colombier 		if((*p == '.' && i <= IPaddrlen-4) || (*p == 0 && i == 0)){
77*03673ce5SDavid du Colombier 			/* ends with v4 */
787dd7cddfSDavid du Colombier 			p = v4parseip(to+i, op);
797dd7cddfSDavid du Colombier 			i += 4;
807dd7cddfSDavid du Colombier 			break;
817dd7cddfSDavid du Colombier 		}
82ea58ad6fSDavid du Colombier 		/* v6: at most 4 hex digits, followed by colon or delim */
83ea58ad6fSDavid du Colombier 		if(x != (ushort)x || *p != ':' && !delimchar(*p)) {
84ea58ad6fSDavid du Colombier 			memset(to, 0, IPaddrlen);
85ea58ad6fSDavid du Colombier 			return -1;			/* parse error */
86ea58ad6fSDavid du Colombier 		}
877dd7cddfSDavid du Colombier 		to[i] = x>>8;
887dd7cddfSDavid du Colombier 		to[i+1] = x;
897dd7cddfSDavid du Colombier 		if(*p == ':'){
907dd7cddfSDavid du Colombier 			v4 = 0;
91ea58ad6fSDavid du Colombier 			if(*++p == ':'){	/* :: is elided zero short(s) */
92ea58ad6fSDavid du Colombier 				if (elipsis) {
93ea58ad6fSDavid du Colombier 					memset(to, 0, IPaddrlen);
94ea58ad6fSDavid du Colombier 					return -1;	/* second :: */
95ea58ad6fSDavid du Colombier 				}
967dd7cddfSDavid du Colombier 				elipsis = i+2;
977dd7cddfSDavid du Colombier 				p++;
987dd7cddfSDavid du Colombier 			}
99ea58ad6fSDavid du Colombier 		} else if (p == op)		/* strtoul made no progress? */
100ea58ad6fSDavid du Colombier 			break;
1017dd7cddfSDavid du Colombier 	}
102ea58ad6fSDavid du Colombier 	if (p == from || !delimchar(*p)) {
103ea58ad6fSDavid du Colombier 		memset(to, 0, IPaddrlen);
104ea58ad6fSDavid du Colombier 		return -1;				/* parse error */
1057dd7cddfSDavid du Colombier 	}
106ea58ad6fSDavid du Colombier 	if(i < IPaddrlen){
107ea58ad6fSDavid du Colombier 		memmove(&to[elipsis+IPaddrlen-i], &to[elipsis], i-elipsis);
108ea58ad6fSDavid du Colombier 		memset(&to[elipsis], 0, IPaddrlen-i);
1097dd7cddfSDavid du Colombier 	}
1107dd7cddfSDavid du Colombier 	if(v4){
1117dd7cddfSDavid du Colombier 		to[10] = to[11] = 0xff;
112ea58ad6fSDavid du Colombier 		return nhgetl(to + IPv4off);
1137dd7cddfSDavid du Colombier 	} else
1147dd7cddfSDavid du Colombier 		return 6;
1157dd7cddfSDavid du Colombier }
1167dd7cddfSDavid du Colombier 
1177dd7cddfSDavid du Colombier /*
1187dd7cddfSDavid du Colombier  *  hack to allow ip v4 masks to be entered in the old
1197dd7cddfSDavid du Colombier  *  style
1207dd7cddfSDavid du Colombier  */
121ea58ad6fSDavid du Colombier vlong
parseipmask(uchar * to,char * from)1227dd7cddfSDavid du Colombier parseipmask(uchar *to, char *from)
1237dd7cddfSDavid du Colombier {
124c62728daSDavid du Colombier 	int i, w;
125ea58ad6fSDavid du Colombier 	vlong x;
1267dd7cddfSDavid du Colombier 	uchar *p;
1277dd7cddfSDavid du Colombier 
1287dd7cddfSDavid du Colombier 	if(*from == '/'){
1297dd7cddfSDavid du Colombier 		/* as a number of prefix bits */
1307dd7cddfSDavid du Colombier 		i = atoi(from+1);
1317dd7cddfSDavid du Colombier 		if(i < 0)
1327dd7cddfSDavid du Colombier 			i = 0;
1337dd7cddfSDavid du Colombier 		if(i > 128)
1347dd7cddfSDavid du Colombier 			i = 128;
135c62728daSDavid du Colombier 		w = i;
1367dd7cddfSDavid du Colombier 		memset(to, 0, IPaddrlen);
1377dd7cddfSDavid du Colombier 		for(p = to; i >= 8; i -= 8)
1387dd7cddfSDavid du Colombier 			*p++ = 0xff;
1397dd7cddfSDavid du Colombier 		if(i > 0)
1407dd7cddfSDavid du Colombier 			*p = ~((1<<(8-i))-1);
1417dd7cddfSDavid du Colombier 		x = nhgetl(to+IPv4off);
142c62728daSDavid du Colombier 		/*
143c62728daSDavid du Colombier 		 * identify as ipv6 if the mask is inexpressible as a v4 mask
144ea58ad6fSDavid du Colombier 		 * (because it has too few mask bits).  Arguably, we could
145c62728daSDavid du Colombier 		 * always return 6 here.
146c62728daSDavid du Colombier 		 */
147ea58ad6fSDavid du Colombier 		if (w < 8*(IPaddrlen-IPv4addrlen))
148c62728daSDavid du Colombier 			return 6;
1497dd7cddfSDavid du Colombier 	} else {
150ea58ad6fSDavid du Colombier 		/* as a straight v4 bit mask */
1517dd7cddfSDavid du Colombier 		x = parseip(to, from);
152ea58ad6fSDavid du Colombier 		if (x != -1)
153ea58ad6fSDavid du Colombier 			x = (ulong)nhgetl(to + IPv4off);
1547dd7cddfSDavid du Colombier 		if(memcmp(to, v4prefix, IPv4off) == 0)
1557dd7cddfSDavid du Colombier 			memset(to, 0xff, IPv4off);
1567dd7cddfSDavid du Colombier 	}
1577dd7cddfSDavid du Colombier 	return x;
1587dd7cddfSDavid du Colombier }
1597dd7cddfSDavid du Colombier 
1607dd7cddfSDavid du Colombier /*
1617dd7cddfSDavid du Colombier  *  parse a v4 ip address/mask in cidr format
1627dd7cddfSDavid du Colombier  */
1637dd7cddfSDavid du Colombier char*
v4parsecidr(uchar * addr,uchar * mask,char * from)1647dd7cddfSDavid du Colombier v4parsecidr(uchar *addr, uchar *mask, char *from)
1657dd7cddfSDavid du Colombier {
1667dd7cddfSDavid du Colombier 	int i;
1677dd7cddfSDavid du Colombier 	char *p;
1687dd7cddfSDavid du Colombier 	uchar *a;
1697dd7cddfSDavid du Colombier 
1707dd7cddfSDavid du Colombier 	p = v4parseip(addr, from);
1717dd7cddfSDavid du Colombier 
1727dd7cddfSDavid du Colombier 	if(*p == '/'){
1737dd7cddfSDavid du Colombier 		/* as a number of prefix bits */
1747dd7cddfSDavid du Colombier 		i = strtoul(p+1, &p, 0);
1757dd7cddfSDavid du Colombier 		if(i > 32)
1767dd7cddfSDavid du Colombier 			i = 32;
1777dd7cddfSDavid du Colombier 		memset(mask, 0, IPv4addrlen);
1787dd7cddfSDavid du Colombier 		for(a = mask; i >= 8; i -= 8)
1797dd7cddfSDavid du Colombier 			*a++ = 0xff;
1807dd7cddfSDavid du Colombier 		if(i > 0)
1817dd7cddfSDavid du Colombier 			*a = ~((1<<(8-i))-1);
1827dd7cddfSDavid du Colombier 	} else
1837dd7cddfSDavid du Colombier 		memcpy(mask, defmask(addr), IPv4addrlen);
1847dd7cddfSDavid du Colombier 	return p;
1853e12c5d1SDavid du Colombier }
186