1 /*
2  * Copyright (c) 1996,1999 by Internet Software Consortium.
3  *
4  * Permission to use, copy, modify, and distribute this software for any
5  * purpose with or without fee is hereby granted, provided that the above
6  * copyright notice and this permission notice appear in all copies.
7  *
8  * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
9  * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
10  * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
11  * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
12  * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
13  * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
14  * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
15  * SOFTWARE.
16  */
17 
18 #include <sys/types.h>
19 #include <sys/socket.h>
20 #include <netinet/in.h>
21 #include <arpa/inet.h>
22 
23 #include <assert.h>
24 #include <ctype.h>
25 #include <errno.h>
26 #include <stdio.h>
27 #include <string.h>
28 #include <stdlib.h>
29 
30 #ifdef SPRINTF_CHAR
31 # define SPRINTF(x) strlen(sprintf/**/x)
32 #else
33 # define SPRINTF(x) ((size_t)sprintf x)
34 #endif
35 
36 static int	inet_net_pton_ipv4 (const char *src, u_char *dst,
37 				    size_t size) __THROW;
38 
39 /*
40  * static int
41  * inet_net_pton(af, src, dst, size)
42  *	convert network number from presentation to network format.
43  *	accepts hex octets, hex strings, decimal octets, and /CIDR.
44  *	"size" is in bytes and describes "dst".
45  * return:
46  *	number of bits, either imputed classfully or specified with /CIDR,
47  *	or -1 if some failure occurred (check errno).  ENOENT means it was
48  *	not a valid network specification.
49  * author:
50  *	Paul Vixie (ISC), June 1996
51  */
52 int
inet_net_pton(int af,const char * src,void * dst,size_t size)53 inet_net_pton (int af, const char *src, void *dst, size_t size)
54 {
55 	switch (af) {
56 	case AF_INET:
57 		return (inet_net_pton_ipv4(src, dst, size));
58 	default:
59 		__set_errno (EAFNOSUPPORT);
60 		return (-1);
61 	}
62 }
63 
64 /*
65  * static int
66  * inet_net_pton_ipv4(src, dst, size)
67  *	convert IPv4 network number from presentation to network format.
68  *	accepts hex octets, hex strings, decimal octets, and /CIDR.
69  *	"size" is in bytes and describes "dst".
70  * return:
71  *	number of bits, either imputed classfully or specified with /CIDR,
72  *	or -1 if some failure occurred (check errno).  ENOENT means it was
73  *	not an IPv4 network specification.
74  * note:
75  *	network byte order assumed.  this means 192.5.5.240/28 has
76  *	0b11110000 in its fourth octet.
77  * author:
78  *	Paul Vixie (ISC), June 1996
79  */
80 static int
inet_net_pton_ipv4(const char * src,u_char * dst,size_t size)81 inet_net_pton_ipv4 (const char *src, u_char *dst, size_t size)
82 {
83 	static const char xdigits[] = "0123456789abcdef";
84 	int n, ch, tmp, dirty, bits;
85 	const u_char *odst = dst;
86 
87 	ch = *src++;
88 	if (ch == '0' && (src[0] == 'x' || src[0] == 'X')
89 	    && isascii(src[1]) && isxdigit(src[1])) {
90 		/* Hexadecimal: Eat nybble string. */
91 		if (size <= 0)
92 			goto emsgsize;
93 		dirty = 0;
94 		tmp = 0;	/* To calm down gcc.  */
95 		src++;	/* skip x or X. */
96 		while (isxdigit((ch = *src++))) {
97 			ch = _tolower(ch);
98 			n = (const char *) __rawmemchr(xdigits, ch) - xdigits;
99 			assert(n >= 0 && n <= 15);
100 			if (dirty == 0)
101 				tmp = n;
102 			else
103 				tmp = (tmp << 4) | n;
104 			if (++dirty == 2) {
105 				if (size-- <= 0)
106 					goto emsgsize;
107 				*dst++ = (u_char) tmp;
108 				dirty = 0;
109 			}
110 		}
111 		if (dirty) {  /* Odd trailing nybble? */
112 			if (size-- <= 0)
113 				goto emsgsize;
114 			*dst++ = (u_char) (tmp << 4);
115 		}
116 	} else if (isascii(ch) && isdigit(ch)) {
117 		/* Decimal: eat dotted digit string. */
118 		for (;;) {
119 			tmp = 0;
120 			do {
121 				n = ((const char *) __rawmemchr(xdigits, ch)
122 				     - xdigits);
123 				assert(n >= 0 && n <= 9);
124 				tmp *= 10;
125 				tmp += n;
126 				if (tmp > 255)
127 					goto enoent;
128 			} while (isascii((ch = *src++)) && isdigit(ch));
129 			if (size-- <= 0)
130 				goto emsgsize;
131 			*dst++ = (u_char) tmp;
132 			if (ch == '\0' || ch == '/')
133 				break;
134 			if (ch != '.')
135 				goto enoent;
136 			ch = *src++;
137 			if (!isascii(ch) || !isdigit(ch))
138 				goto enoent;
139 		}
140 	} else
141 		goto enoent;
142 
143 	bits = -1;
144 	if (ch == '/' && isascii(src[0]) && isdigit(src[0]) && dst > odst) {
145 		/* CIDR width specifier.  Nothing can follow it. */
146 		ch = *src++;	/* Skip over the /. */
147 		bits = 0;
148 		do {
149 			n = (const char *) __rawmemchr(xdigits, ch) - xdigits;
150 			assert(n >= 0 && n <= 9);
151 			bits *= 10;
152 			bits += n;
153 		} while (isascii((ch = *src++)) && isdigit(ch));
154 		if (ch != '\0')
155 			goto enoent;
156 		if (bits > 32)
157 			goto emsgsize;
158 	}
159 
160 	/* Firey death and destruction unless we prefetched EOS. */
161 	if (ch != '\0')
162 		goto enoent;
163 
164 	/* If nothing was written to the destination, we found no address. */
165 	if (dst == odst)
166 		goto enoent;
167 	/* If no CIDR spec was given, infer width from net class. */
168 	if (bits == -1) {
169 		if (*odst >= 240)	/* Class E */
170 			bits = 32;
171 		else if (*odst >= 224)	/* Class D */
172 			bits = 4;
173 		else if (*odst >= 192)	/* Class C */
174 			bits = 24;
175 		else if (*odst >= 128)	/* Class B */
176 			bits = 16;
177 		else			/* Class A */
178 			bits = 8;
179 		/* If imputed mask is narrower than specified octets, widen. */
180 		if (bits >= 8 && bits < ((dst - odst) * 8))
181 			bits = (dst - odst) * 8;
182 	}
183 	/* Extend network to cover the actual mask. */
184 	while (bits > ((dst - odst) * 8)) {
185 		if (size-- <= 0)
186 			goto emsgsize;
187 		*dst++ = '\0';
188 	}
189 	return (bits);
190 
191  enoent:
192 	__set_errno (ENOENT);
193 	return (-1);
194 
195  emsgsize:
196 	__set_errno (EMSGSIZE);
197 	return (-1);
198 }
199