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
36static 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 */
52int
53inet_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 */
80static int
81inet_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