Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/lib/libc/inet/inet_net_pton.c
104793 views
1
/* $OpenBSD: inet_net_pton.c,v 1.14 2022/12/27 17:10:06 jmc Exp $ */
2
3
/*
4
* Copyright (c) 2012 by Gilles Chehade <[email protected]>
5
* Copyright (c) 1996,1999 by Internet Software Consortium.
6
*
7
* SPDX-License-Identifier: ISC
8
*
9
* Permission to use, copy, modify, and distribute this software for any
10
* purpose with or without fee is hereby granted, provided that the above
11
* copyright notice and this permission notice appear in all copies.
12
*
13
* THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
14
* ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
15
* OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
16
* CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
17
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
18
* PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
19
* ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
20
* SOFTWARE.
21
*/
22
23
#include "port_before.h"
24
25
#include <sys/types.h>
26
#include <sys/socket.h>
27
#include <netinet/in.h>
28
#include <arpa/inet.h>
29
30
#include <assert.h>
31
#include <ctype.h>
32
#include <errno.h>
33
#include <stdio.h>
34
#include <string.h>
35
#include <stdlib.h>
36
37
#include "port_after.h"
38
39
static int inet_net_pton_ipv4(const char *, u_char *, size_t);
40
static int inet_net_pton_ipv6(const char *, u_char *, size_t);
41
42
/*
43
* static int
44
* inet_net_pton(af, src, dst, size)
45
* convert network number from presentation to network format.
46
* accepts hex octets, hex strings, decimal octets, and /CIDR.
47
* "size" is in bytes and describes "dst".
48
* return:
49
* number of bits, either imputed classfully or specified with /CIDR,
50
* or -1 if some failure occurred (check errno). ENOENT means it was
51
* not a valid network specification.
52
* author:
53
* Paul Vixie (ISC), June 1996
54
*/
55
int
56
inet_net_pton(int af, const char *src, void *dst, size_t size)
57
{
58
switch (af) {
59
case AF_INET:
60
return (inet_net_pton_ipv4(src, dst, size));
61
case AF_INET6:
62
return (inet_net_pton_ipv6(src, dst, size));
63
default:
64
errno = EAFNOSUPPORT;
65
return (-1);
66
}
67
}
68
69
/*
70
* static int
71
* inet_net_pton_ipv4(src, dst, size)
72
* convert IPv4 network number from presentation to network format.
73
* accepts hex octets, hex strings, decimal octets, and /CIDR.
74
* "size" is in bytes and describes "dst".
75
* return:
76
* number of bits, either imputed classfully or specified with /CIDR,
77
* or -1 if some failure occurred (check errno). ENOENT means it was
78
* not an IPv4 network specification.
79
* note:
80
* network byte order assumed. this means 192.5.5.240/28 has
81
* 0b11110000 in its fourth octet.
82
* author:
83
* Paul Vixie (ISC), June 1996
84
*/
85
static int
86
inet_net_pton_ipv4(const char *src, u_char *dst, size_t size)
87
{
88
static const char
89
xdigits[] = "0123456789abcdef",
90
digits[] = "0123456789";
91
int n, ch, tmp, dirty, bits;
92
const u_char *odst = dst;
93
94
ch = (unsigned char)*src++;
95
if (ch == '0' && (src[0] == 'x' || src[0] == 'X')
96
&& isascii((unsigned char)src[1]) && isxdigit((unsigned char)src[1])) {
97
/* Hexadecimal: Eat nybble string. */
98
if (size == 0)
99
goto emsgsize;
100
tmp = 0, dirty = 0;
101
src++; /* skip x or X. */
102
while ((ch = (unsigned char)*src++) != '\0' &&
103
isascii(ch) && isxdigit(ch)) {
104
if (isupper(ch))
105
ch = tolower(ch);
106
n = strchr(xdigits, ch) - xdigits;
107
assert(n >= 0 && n <= 15);
108
if (dirty == 0)
109
tmp = n;
110
else
111
tmp = (tmp << 4) | n;
112
if (++dirty == 2) {
113
if (size-- == 0)
114
goto emsgsize;
115
*dst++ = (u_char) tmp;
116
dirty = 0;
117
}
118
}
119
if (dirty) { /* Odd trailing nybble? */
120
if (size-- == 0)
121
goto emsgsize;
122
*dst++ = (u_char) (tmp << 4);
123
}
124
} else if (isascii(ch) && isdigit(ch)) {
125
/* Decimal: eat dotted digit string. */
126
for (;;) {
127
tmp = 0;
128
do {
129
n = strchr(digits, ch) - digits;
130
assert(n >= 0 && n <= 9);
131
tmp *= 10;
132
tmp += n;
133
if (tmp > 255)
134
goto enoent;
135
} while ((ch = (unsigned char)*src++) != '\0' &&
136
isascii(ch) && isdigit(ch));
137
if (size-- == 0)
138
goto emsgsize;
139
*dst++ = (u_char) tmp;
140
if (ch == '\0' || ch == '/')
141
break;
142
if (ch != '.')
143
goto enoent;
144
ch = (unsigned char)*src++;
145
if (!isascii(ch) || !isdigit(ch))
146
goto enoent;
147
}
148
} else
149
goto enoent;
150
151
bits = -1;
152
if (ch == '/' && isascii((unsigned char)src[0]) &&
153
isdigit((unsigned char)src[0]) && dst > odst) {
154
/* CIDR width specifier. Nothing can follow it. */
155
ch = (unsigned char)*src++; /* Skip over the /. */
156
bits = 0;
157
do {
158
n = strchr(digits, ch) - digits;
159
assert(n >= 0 && n <= 9);
160
bits *= 10;
161
bits += n;
162
if (bits > 32)
163
goto emsgsize;
164
} while ((ch = (unsigned char)*src++) != '\0' &&
165
isascii(ch) && isdigit(ch));
166
if (ch != '\0')
167
goto enoent;
168
}
169
170
/* Fiery death and destruction unless we prefetched EOS. */
171
if (ch != '\0')
172
goto enoent;
173
174
/* If nothing was written to the destination, we found no address. */
175
if (dst == odst)
176
goto enoent;
177
/* If no CIDR spec was given, infer width from net class. */
178
if (bits == -1) {
179
if (*odst >= 240) /* Class E */
180
bits = 32;
181
else if (*odst >= 224) /* Class D */
182
bits = 4;
183
else if (*odst >= 192) /* Class C */
184
bits = 24;
185
else if (*odst >= 128) /* Class B */
186
bits = 16;
187
else /* Class A */
188
bits = 8;
189
/* If imputed mask is narrower than specified octets, widen. */
190
if (bits < ((dst - odst) * 8))
191
bits = (dst - odst) * 8;
192
}
193
/* Extend network to cover the actual mask. */
194
while (bits > ((dst - odst) * 8)) {
195
if (size-- == 0)
196
goto emsgsize;
197
*dst++ = '\0';
198
}
199
return (bits);
200
201
enoent:
202
errno = ENOENT;
203
return (-1);
204
205
emsgsize:
206
errno = EMSGSIZE;
207
return (-1);
208
}
209
210
211
static int
212
inet_net_pton_ipv6(const char *src, u_char *dst, size_t size)
213
{
214
struct in6_addr in6;
215
int ret;
216
int bits;
217
size_t bytes;
218
char buf[INET6_ADDRSTRLEN + sizeof("/128")];
219
char *sep;
220
const char *errstr;
221
222
if (strlcpy(buf, src, sizeof buf) >= sizeof buf) {
223
errno = EMSGSIZE;
224
return (-1);
225
}
226
227
sep = strchr(buf, '/');
228
if (sep != NULL)
229
*sep++ = '\0';
230
231
ret = inet_pton(AF_INET6, buf, &in6);
232
if (ret != 1)
233
return (-1);
234
235
if (sep == NULL)
236
bits = 128;
237
else {
238
bits = strtonum(sep, 0, 128, &errstr);
239
if (errstr) {
240
errno = EINVAL;
241
return (-1);
242
}
243
}
244
245
bytes = (bits + 7) / 8;
246
if (bytes > size) {
247
errno = EMSGSIZE;
248
return (-1);
249
}
250
memcpy(dst, &in6.s6_addr, bytes);
251
return (bits);
252
}
253
254
/*
255
* Weak aliases for applications that use certain private entry points,
256
* and fail to include <arpa/inet.h>.
257
*/
258
#undef inet_net_pton
259
__weak_reference(__inet_net_pton, inet_net_pton);
260
261