xref: /plan9/sys/src/cmd/unix/drawterm/libip/parseip.c (revision 58da3067adcdccaaa043d0bfde28ba83b7ced07d)
1 #include <u.h>
2 #include <libc.h>
3 #include <ctype.h>
4 #include <ip.h>
5 
6 char*
v4parseip(uchar * to,char * from)7 v4parseip(uchar *to, char *from)
8 {
9 	int i;
10 	char *p;
11 
12 	p = from;
13 	for(i = 0; i < 4 && *p; i++){
14 		to[i] = strtoul(p, &p, 0);
15 		if(*p == '.')
16 			p++;
17 	}
18 	switch(CLASS(to)){
19 	case 0:	/* class A - 1 uchar net */
20 	case 1:
21 		if(i == 3){
22 			to[3] = to[2];
23 			to[2] = to[1];
24 			to[1] = 0;
25 		} else if (i == 2){
26 			to[3] = to[1];
27 			to[1] = 0;
28 		}
29 		break;
30 	case 2:	/* class B - 2 uchar net */
31 		if(i == 3){
32 			to[3] = to[2];
33 			to[2] = 0;
34 		}
35 		break;
36 	}
37 	return p;
38 }
39 
40 static int
ipcharok(int c)41 ipcharok(int c)
42 {
43 	return c == '.' || c == ':' || (isascii(c) && isxdigit(c));
44 }
45 
46 static int
delimchar(int c)47 delimchar(int c)
48 {
49 	if(c == '\0')
50 		return 1;
51 	if(c == '.' || c == ':' || (isascii(c) && isalnum(c)))
52 		return 0;
53 	return 1;
54 }
55 
56 /*
57  * `from' may contain an address followed by other characters,
58  * at least in /boot, so we permit whitespace (and more) after the address.
59  * we do ensure that "delete" cannot be parsed as "de::".
60  *
61  * some callers don't check the return value for errors, so
62  * set `to' to something distinctive in the case of a parse error.
63  */
64 vlong
parseip(uchar * to,char * from)65 parseip(uchar *to, char *from)
66 {
67 	int i, elipsis = 0, v4 = 1;
68 	ulong x;
69 	char *p, *op;
70 
71 	memset(to, 0, IPaddrlen);
72 	p = from;
73 	for(i = 0; i < IPaddrlen && ipcharok(*p); i+=2){
74 		op = p;
75 		x = strtoul(p, &p, 16);
76 		if((*p == '.' && i <= IPaddrlen-4) || (*p == 0 && i == 0)){
77 			/* ends with v4 */
78 			p = v4parseip(to+i, op);
79 			i += 4;
80 			break;
81 		}
82 		/* v6: at most 4 hex digits, followed by colon or delim */
83 		if(x != (ushort)x || (*p != ':' && !delimchar(*p))) {
84 			memset(to, 0, IPaddrlen);
85 			return -1;			/* parse error */
86 		}
87 		to[i] = x>>8;
88 		to[i+1] = x;
89 		if(*p == ':'){
90 			v4 = 0;
91 			if(*++p == ':'){	/* :: is elided zero short(s) */
92 				if (elipsis) {
93 					memset(to, 0, IPaddrlen);
94 					return -1;	/* second :: */
95 				}
96 				elipsis = i+2;
97 				p++;
98 			}
99 		} else if (p == op)		/* strtoul made no progress? */
100 			break;
101 	}
102 	if (p == from || !delimchar(*p)) {
103 		memset(to, 0, IPaddrlen);
104 		return -1;				/* parse error */
105 	}
106 	if(i < IPaddrlen){
107 		memmove(&to[elipsis+IPaddrlen-i], &to[elipsis], i-elipsis);
108 		memset(&to[elipsis], 0, IPaddrlen-i);
109 	}
110 	if(v4){
111 		to[10] = to[11] = 0xff;
112 		return nhgetl(to + IPv4off);
113 	} else
114 		return 6;
115 }
116 
117 /*
118  *  hack to allow ip v4 masks to be entered in the old
119  *  style
120  */
121 vlong
parseipmask(uchar * to,char * from)122 parseipmask(uchar *to, char *from)
123 {
124 	int i, w;
125 	vlong x;
126 	uchar *p;
127 
128 	if(*from == '/'){
129 		/* as a number of prefix bits */
130 		i = atoi(from+1);
131 		if(i < 0)
132 			i = 0;
133 		if(i > 128)
134 			i = 128;
135 		w = i;
136 		memset(to, 0, IPaddrlen);
137 		for(p = to; i >= 8; i -= 8)
138 			*p++ = 0xff;
139 		if(i > 0)
140 			*p = ~((1<<(8-i))-1);
141 		x = nhgetl(to+IPv4off);
142 		/*
143 		 * identify as ipv6 if the mask is inexpressible as a v4 mask
144 		 * (because it has too few mask bits).  Arguably, we could
145 		 * always return 6 here.
146 		 */
147 		if (w < 8*(IPaddrlen-IPv4addrlen))
148 			return 6;
149 	} else {
150 		/* as a straight v4 bit mask */
151 		x = parseip(to, from);
152 		if (x != -1)
153 			x = (ulong)nhgetl(to + IPv4off);
154 		if(memcmp(to, v4prefix, IPv4off) == 0)
155 			memset(to, 0xff, IPv4off);
156 	}
157 	return x;
158 }
159 
160 /*
161  *  parse a v4 ip address/mask in cidr format
162  */
163 char*
v4parsecidr(uchar * addr,uchar * mask,char * from)164 v4parsecidr(uchar *addr, uchar *mask, char *from)
165 {
166 	int i;
167 	char *p;
168 	uchar *a;
169 
170 	p = v4parseip(addr, from);
171 
172 	if(*p == '/'){
173 		/* as a number of prefix bits */
174 		i = strtoul(p+1, &p, 0);
175 		if(i > 32)
176 			i = 32;
177 		memset(mask, 0, IPv4addrlen);
178 		for(a = mask; i >= 8; i -= 8)
179 			*a++ = 0xff;
180 		if(i > 0)
181 			*a = ~((1<<(8-i))-1);
182 	} else
183 		memcpy(mask, defmask(addr), IPv4addrlen);
184 	return p;
185 }
186