xref: /plan9/sys/src/libip/parseip.c (revision 60014d6756a98ad10929607ca84a1b7488a16cfc)
1 #include <u.h>
2 #include <libc.h>
3 #include <ctype.h>
4 #include <ip.h>
5 
6 char*
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
41 ipcharok(int c)
42 {
43 	return c == '.' || c == ':' || isascii(c) && isxdigit(c);
44 }
45 
46 static int
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
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 == '.' || (*p == 0 && i == 0)){	/* ends with v4? */
77 			p = v4parseip(to+i, op);
78 			i += 4;
79 			break;
80 		}
81 		/* v6: at most 4 hex digits, followed by colon or delim */
82 		if(x != (ushort)x || *p != ':' && !delimchar(*p)) {
83 			memset(to, 0, IPaddrlen);
84 			return -1;			/* parse error */
85 		}
86 		to[i] = x>>8;
87 		to[i+1] = x;
88 		if(*p == ':'){
89 			v4 = 0;
90 			if(*++p == ':'){	/* :: is elided zero short(s) */
91 				if (elipsis) {
92 					memset(to, 0, IPaddrlen);
93 					return -1;	/* second :: */
94 				}
95 				elipsis = i+2;
96 				p++;
97 			}
98 		} else if (p == op)		/* strtoul made no progress? */
99 			break;
100 	}
101 	if (p == from || !delimchar(*p)) {
102 		memset(to, 0, IPaddrlen);
103 		return -1;				/* parse error */
104 	}
105 	if(i < IPaddrlen){
106 		memmove(&to[elipsis+IPaddrlen-i], &to[elipsis], i-elipsis);
107 		memset(&to[elipsis], 0, IPaddrlen-i);
108 	}
109 	if(v4){
110 		to[10] = to[11] = 0xff;
111 		return nhgetl(to + IPv4off);
112 	} else
113 		return 6;
114 }
115 
116 /*
117  *  hack to allow ip v4 masks to be entered in the old
118  *  style
119  */
120 vlong
121 parseipmask(uchar *to, char *from)
122 {
123 	int i, w;
124 	vlong x;
125 	uchar *p;
126 
127 	if(*from == '/'){
128 		/* as a number of prefix bits */
129 		i = atoi(from+1);
130 		if(i < 0)
131 			i = 0;
132 		if(i > 128)
133 			i = 128;
134 		w = i;
135 		memset(to, 0, IPaddrlen);
136 		for(p = to; i >= 8; i -= 8)
137 			*p++ = 0xff;
138 		if(i > 0)
139 			*p = ~((1<<(8-i))-1);
140 		x = nhgetl(to+IPv4off);
141 		/*
142 		 * identify as ipv6 if the mask is inexpressible as a v4 mask
143 		 * (because it has too few mask bits).  Arguably, we could
144 		 * always return 6 here.
145 		 */
146 		if (w < 8*(IPaddrlen-IPv4addrlen))
147 			return 6;
148 	} else {
149 		/* as a straight v4 bit mask */
150 		x = parseip(to, from);
151 		if (x != -1)
152 			x = (ulong)nhgetl(to + IPv4off);
153 		if(memcmp(to, v4prefix, IPv4off) == 0)
154 			memset(to, 0xff, IPv4off);
155 	}
156 	return x;
157 }
158 
159 /*
160  *  parse a v4 ip address/mask in cidr format
161  */
162 char*
163 v4parsecidr(uchar *addr, uchar *mask, char *from)
164 {
165 	int i;
166 	char *p;
167 	uchar *a;
168 
169 	p = v4parseip(addr, from);
170 
171 	if(*p == '/'){
172 		/* as a number of prefix bits */
173 		i = strtoul(p+1, &p, 0);
174 		if(i > 32)
175 			i = 32;
176 		memset(mask, 0, IPv4addrlen);
177 		for(a = mask; i >= 8; i -= 8)
178 			*a++ = 0xff;
179 		if(i > 0)
180 			*a = ~((1<<(8-i))-1);
181 	} else
182 		memcpy(mask, defmask(addr), IPv4addrlen);
183 	return p;
184 }
185