GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: lib/libc/net/inet_net_pton.c Lines: 46 93 49.5 %
Date: 2017-11-07 Branches: 40 109 36.7 %

Line Branch Exec Source
1
/*	$OpenBSD: inet_net_pton.c,v 1.10 2017/03/06 18:16:27 millert Exp $	*/
2
3
/*
4
 * Copyright (c) 2012 by Gilles Chehade <gilles@openbsd.org>
5
 * Copyright (c) 1996,1999 by Internet Software Consortium.
6
 *
7
 * Permission to use, copy, modify, and distribute this software for any
8
 * purpose with or without fee is hereby granted, provided that the above
9
 * copyright notice and this permission notice appear in all copies.
10
 *
11
 * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
12
 * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
13
 * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
14
 * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
15
 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
16
 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
17
 * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
18
 * SOFTWARE.
19
 */
20
21
#include <sys/types.h>
22
#include <sys/socket.h>
23
#include <netinet/in.h>
24
#include <arpa/inet.h>
25
26
#include <assert.h>
27
#include <ctype.h>
28
#include <errno.h>
29
#include <stdio.h>
30
#include <string.h>
31
#include <stdlib.h>
32
33
static int	inet_net_pton_ipv4(const char *, u_char *, size_t);
34
static int	inet_net_pton_ipv6(const char *, u_char *, size_t);
35
36
/*
37
 * static int
38
 * inet_net_pton(af, src, dst, size)
39
 *	convert network number from presentation to network format.
40
 *	accepts hex octets, hex strings, decimal octets, and /CIDR.
41
 *	"size" is in bytes and describes "dst".
42
 * return:
43
 *	number of bits, either imputed classfully or specified with /CIDR,
44
 *	or -1 if some failure occurred (check errno).  ENOENT means it was
45
 *	not a valid network specification.
46
 * author:
47
 *	Paul Vixie (ISC), June 1996
48
 */
49
int
50
inet_net_pton(int af, const char *src, void *dst, size_t size)
51
{
52
938
	switch (af) {
53
	case AF_INET:
54
469
		return (inet_net_pton_ipv4(src, dst, size));
55
	case AF_INET6:
56
		return (inet_net_pton_ipv6(src, dst, size));
57
	default:
58
		errno = EAFNOSUPPORT;
59
		return (-1);
60
	}
61
469
}
62
63
/*
64
 * static int
65
 * inet_net_pton_ipv4(src, dst, size)
66
 *	convert IPv4 network number from presentation to network format.
67
 *	accepts hex octets, hex strings, decimal octets, and /CIDR.
68
 *	"size" is in bytes and describes "dst".
69
 * return:
70
 *	number of bits, either imputed classfully or specified with /CIDR,
71
 *	or -1 if some failure occurred (check errno).  ENOENT means it was
72
 *	not an IPv4 network specification.
73
 * note:
74
 *	network byte order assumed.  this means 192.5.5.240/28 has
75
 *	0x11110000 in its fourth octet.
76
 * author:
77
 *	Paul Vixie (ISC), June 1996
78
 */
79
static int
80
inet_net_pton_ipv4(const char *src, u_char *dst, size_t size)
81
{
82
	static const char
83
		xdigits[] = "0123456789abcdef",
84
		digits[] = "0123456789";
85
	int n, ch, tmp, dirty, bits;
86
	const u_char *odst = dst;
87
88
938
	ch = (unsigned char)*src++;
89

469
	if (ch == '0' && (src[0] == 'x' || src[0] == 'X')
90
	    && isascii((unsigned char)src[1]) && isxdigit((unsigned char)src[1])) {
91
		/* Hexadecimal: Eat nybble string. */
92
		if (size == 0)
93
			goto emsgsize;
94
		tmp = 0, dirty = 0;
95
		src++;	/* skip x or X. */
96
		while ((ch = (unsigned char)*src++) != '\0' &&
97
		    isascii(ch) && isxdigit(ch)) {
98
			if (isupper(ch))
99
				ch = tolower(ch);
100
			n = strchr(xdigits, ch) - xdigits;
101
			assert(n >= 0 && n <= 15);
102
			if (dirty == 0)
103
				tmp = n;
104
			else
105
				tmp = (tmp << 4) | n;
106
			if (++dirty == 2) {
107
				if (size-- == 0)
108
					goto emsgsize;
109
				*dst++ = (u_char) tmp;
110
				dirty = 0;
111
			}
112
		}
113
		if (dirty) {  /* Odd trailing nybble? */
114
			if (size-- == 0)
115
				goto emsgsize;
116
			*dst++ = (u_char) (tmp << 4);
117
		}
118

938
	} else if (isascii(ch) && isdigit(ch)) {
119
		/* Decimal: eat dotted digit string. */
120
		for (;;) {
121
			tmp = 0;
122
1690
			do {
123
2486
				n = strchr(digits, ch) - digits;
124
2486
				assert(n >= 0 && n <= 9);
125
2486
				tmp *= 10;
126
2486
				tmp += n;
127
2486
				if (tmp > 255)
128
					goto enoent;
129

4960
			} while ((ch = (unsigned char)*src++) != '\0' &&
130
4948
				 isascii(ch) && isdigit(ch));
131
1690
			if (size-- == 0)
132
				goto emsgsize;
133
1690
			*dst++ = (u_char) tmp;
134
1690
			if (ch == '\0' || ch == '/')
135
				break;
136
1221
			if (ch != '.')
137
				goto enoent;
138
1221
			ch = (unsigned char)*src++;
139

2442
			if (!isascii(ch) || !isdigit(ch))
140
				goto enoent;
141
		}
142
	} else
143
		goto enoent;
144
145
	bits = -1;
146

1383
	if (ch == '/' && isascii((unsigned char)src[0]) &&
147
914
	    isdigit((unsigned char)src[0]) && dst > odst) {
148
		/* CIDR width specifier.  Nothing can follow it. */
149
457
		ch = (unsigned char)*src++;	/* Skip over the /. */
150
		bits = 0;
151
457
		do {
152
912
			n = strchr(digits, ch) - digits;
153
912
			assert(n >= 0 && n <= 9);
154
912
			bits *= 10;
155
912
			bits += n;
156
912
			if (bits > 32)
157
				goto emsgsize;
158

1367
		} while ((ch = (unsigned char)*src++) != '\0' &&
159
910
			 isascii(ch) && isdigit(ch));
160
457
		if (ch != '\0')
161
			goto enoent;
162
	}
163
164
	/* Firey death and destruction unless we prefetched EOS. */
165
12
	if (ch != '\0')
166
		goto enoent;
167
168
	/* If nothing was written to the destination, we found no address. */
169
469
	if (dst == odst)
170
		goto enoent;
171
	/* If no CIDR spec was given, infer width from net class. */
172
469
	if (bits == -1) {
173
12
		if (*odst >= 240)	/* Class E */
174
			bits = 32;
175
12
		else if (*odst >= 224)	/* Class D */
176
			bits = 4;
177
12
		else if (*odst >= 192)	/* Class C */
178
12
			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
12
		if (bits < ((dst - odst) * 8))
185
12
			bits = (dst - odst) * 8;
186
	}
187
	/* Extend network to cover the actual mask. */
188
485
	while (bits > ((dst - odst) * 8)) {
189
8
		if (size-- == 0)
190
			goto emsgsize;
191
8
		*dst++ = '\0';
192
	}
193
469
	return (bits);
194
195
 enoent:
196
	errno = ENOENT;
197
	return (-1);
198
199
 emsgsize:
200
	errno = EMSGSIZE;
201
	return (-1);
202
469
}
203
204
205
static int
206
inet_net_pton_ipv6(const char *src, u_char *dst, size_t size)
207
{
208
	int	ret;
209
	int	bits;
210
	char	buf[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255:255:255:255/128")];
211
	char		*sep;
212
	const char	*errstr;
213
214
	if (strlcpy(buf, src, sizeof buf) >= sizeof buf) {
215
		errno = EMSGSIZE;
216
		return (-1);
217
	}
218
219
	sep = strchr(buf, '/');
220
	if (sep != NULL)
221
		*sep++ = '\0';
222
223
	ret = inet_pton(AF_INET6, buf, dst);
224
	if (ret != 1)
225
		return (-1);
226
227
	if (sep == NULL)
228
		return 128;
229
230
	bits = strtonum(sep, 0, 128, &errstr);
231
	if (errstr) {
232
		errno = EINVAL;
233
		return (-1);
234
	}
235
236
	return bits;
237
}