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#if defined(LIBC_SCCS) && !defined(lint)
19static const char rcsid[] = "$BINDId: inet_net_pton.c,v 1.11 1999/01/08 19:23:44 vixie Exp $";
20#endif
21
22#include <sys/types.h>
23#include <sys/socket.h>
24#include <netinet/in.h>
25#include <arpa/inet.h>
26
27#include <assert.h>
28#include <ctype.h>
29#include <errno.h>
30#include <stdio.h>
31#include <string.h>
32#include <stdlib.h>
33
34#ifdef SPRINTF_CHAR
35# define SPRINTF(x) strlen(sprintf/**/x)
36#else
37# define SPRINTF(x) ((size_t)sprintf x)
38#endif
39
40static int inet_net_pton_ipv4 (const char *src, u_char *dst,
41 size_t size) __THROW;
42
43/*
44 * static int
45 * inet_net_pton(af, src, dst, size)
46 * convert network number from presentation to network format.
47 * accepts hex octets, hex strings, decimal octets, and /CIDR.
48 * "size" is in bytes and describes "dst".
49 * return:
50 * number of bits, either imputed classfully or specified with /CIDR,
51 * or -1 if some failure occurred (check errno). ENOENT means it was
52 * not a valid network specification.
53 * author:
54 * Paul Vixie (ISC), June 1996
55 */
56int
57inet_net_pton (int af, const char *src, void *dst, size_t size)
58{
59 switch (af) {
60 case AF_INET:
61 return (inet_net_pton_ipv4(src, dst, size));
62 default:
63 __set_errno (EAFNOSUPPORT);
64 return (-1);
65 }
66}
67
68/*
69 * static int
70 * inet_net_pton_ipv4(src, dst, size)
71 * convert IPv4 network number from presentation to network format.
72 * accepts hex octets, hex strings, decimal octets, and /CIDR.
73 * "size" is in bytes and describes "dst".
74 * return:
75 * number of bits, either imputed classfully or specified with /CIDR,
76 * or -1 if some failure occurred (check errno). ENOENT means it was
77 * not an IPv4 network specification.
78 * note:
79 * network byte order assumed. this means 192.5.5.240/28 has
80 * 0b11110000 in its fourth octet.
81 * author:
82 * Paul Vixie (ISC), June 1996
83 */
84static int
85inet_net_pton_ipv4 (const char *src, u_char *dst, size_t size)
86{
87 static const char xdigits[] = "0123456789abcdef";
88 int n, ch, tmp, dirty, bits;
89 const u_char *odst = dst;
90
91 ch = *src++;
92 if (ch == '0' && (src[0] == 'x' || src[0] == 'X')
93 && isascii(src[1]) && isxdigit(src[1])) {
94 /* Hexadecimal: Eat nybble string. */
95 if (size <= 0)
96 goto emsgsize;
97 dirty = 0;
98 tmp = 0; /* To calm down gcc. */
99 src++; /* skip x or X. */
100 while (isxdigit((ch = *src++))) {
101 ch = _tolower(ch);
102 n = (const char *) __rawmemchr(xdigits, ch) - xdigits;
103 assert(n >= 0 && n <= 15);
104 if (dirty == 0)
105 tmp = n;
106 else
107 tmp = (tmp << 4) | n;
108 if (++dirty == 2) {
109 if (size-- <= 0)
110 goto emsgsize;
111 *dst++ = (u_char) tmp;
112 dirty = 0;
113 }
114 }
115 if (dirty) { /* Odd trailing nybble? */
116 if (size-- <= 0)
117 goto emsgsize;
118 *dst++ = (u_char) (tmp << 4);
119 }
120 } else if (isascii(ch) && isdigit(ch)) {
121 /* Decimal: eat dotted digit string. */
122 for (;;) {
123 tmp = 0;
124 do {
125 n = ((const char *) __rawmemchr(xdigits, ch)
126 - xdigits);
127 assert(n >= 0 && n <= 9);
128 tmp *= 10;
129 tmp += n;
130 if (tmp > 255)
131 goto enoent;
132 } while (isascii((ch = *src++)) && isdigit(ch));
133 if (size-- <= 0)
134 goto emsgsize;
135 *dst++ = (u_char) tmp;
136 if (ch == '\0' || ch == '/')
137 break;
138 if (ch != '.')
139 goto enoent;
140 ch = *src++;
141 if (!isascii(ch) || !isdigit(ch))
142 goto enoent;
143 }
144 } else
145 goto enoent;
146
147 bits = -1;
148 if (ch == '/' && isascii(src[0]) && isdigit(src[0]) && dst > odst) {
149 /* CIDR width specifier. Nothing can follow it. */
150 ch = *src++; /* Skip over the /. */
151 bits = 0;
152 do {
153 n = (const char *) __rawmemchr(xdigits, ch) - xdigits;
154 assert(n >= 0 && n <= 9);
155 bits *= 10;
156 bits += n;
157 } while (isascii((ch = *src++)) && isdigit(ch));
158 if (ch != '\0')
159 goto enoent;
160 if (bits > 32)
161 goto emsgsize;
162 }
163
164 /* Firey death and destruction unless we prefetched EOS. */
165 if (ch != '\0')
166 goto enoent;
167
168 /* If nothing was written to the destination, we found no address. */
169 if (dst == odst)
170 goto enoent;
171 /* If no CIDR spec was given, infer width from net class. */
172 if (bits == -1) {
173 if (*odst >= 240) /* Class E */
174 bits = 32;
175 else if (*odst >= 224) /* Class D */
176 bits = 4;
177 else if (*odst >= 192) /* Class C */
178 bits = 24;
179 else if (*odst >= 128) /* Class B */
180 bits = 16;
181 else /* Class A */
182 bits = 8;
183 /* If imputed mask is narrower than specified octets, widen. */
184 if (bits >= 8 && bits < ((dst - odst) * 8))
185 bits = (dst - odst) * 8;
186 }
187 /* Extend network to cover the actual mask. */
188 while (bits > ((dst - odst) * 8)) {
189 if (size-- <= 0)
190 goto emsgsize;
191 *dst++ = '\0';
192 }
193 return (bits);
194
195 enoent:
196 __set_errno (ENOENT);
197 return (-1);
198
199 emsgsize:
200 __set_errno (EMSGSIZE);
201 return (-1);
202}
203