1 /*
2  * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
3  * Copyright (c) 1996,1999 by Internet Software Consortium.
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
15  * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  *
17  *	  src/backend/utils/adt/inet_net_pton.c
18  */
19 
20 #if defined(LIBC_SCCS) && !defined(lint)
21 static const char rcsid[] = "Id: inet_net_pton.c,v 1.4.2.3 2004/03/17 00:40:11 marka Exp $";
22 #endif
23 
24 #include "postgres.h"
25 
26 #include <sys/socket.h>
27 #include <netinet/in.h>
28 #include <arpa/inet.h>
29 #include <assert.h>
30 #include <ctype.h>
31 
32 #include "utils/builtins.h" /* pgrminclude ignore */	/* needed on some
33 														 * platforms */
34 #include "utils/inet.h"
35 
36 
37 static int	inet_net_pton_ipv4(const char *src, u_char *dst);
38 static int	inet_cidr_pton_ipv4(const char *src, u_char *dst, size_t size);
39 static int	inet_net_pton_ipv6(const char *src, u_char *dst);
40 static int	inet_cidr_pton_ipv6(const char *src, u_char *dst, size_t size);
41 
42 
43 /*
44  * 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  *
56  * Changes:
57  *	I added the inet_cidr_pton function (also from Paul) and changed
58  *	the names to reflect their current use.
59  *
60  */
61 int
inet_net_pton(int af,const char * src,void * dst,size_t size)62 inet_net_pton(int af, const char *src, void *dst, size_t size)
63 {
64 	switch (af)
65 	{
66 		case PGSQL_AF_INET:
67 			return size == -1 ?
68 				inet_net_pton_ipv4(src, dst) :
69 				inet_cidr_pton_ipv4(src, dst, size);
70 		case PGSQL_AF_INET6:
71 			return size == -1 ?
72 				inet_net_pton_ipv6(src, dst) :
73 				inet_cidr_pton_ipv6(src, dst, size);
74 		default:
75 			errno = EAFNOSUPPORT;
76 			return -1;
77 	}
78 }
79 
80 /*
81  * static int
82  * inet_cidr_pton_ipv4(src, dst, size)
83  *	convert IPv4 network number from presentation to network format.
84  *	accepts hex octets, hex strings, decimal octets, and /CIDR.
85  *	"size" is in bytes and describes "dst".
86  * return:
87  *	number of bits, either imputed classfully or specified with /CIDR,
88  *	or -1 if some failure occurred (check errno).  ENOENT means it was
89  *	not an IPv4 network specification.
90  * note:
91  *	network byte order assumed.  this means 192.5.5.240/28 has
92  *	0b11110000 in its fourth octet.
93  * author:
94  *	Paul Vixie (ISC), June 1996
95  */
96 static int
inet_cidr_pton_ipv4(const char * src,u_char * dst,size_t size)97 inet_cidr_pton_ipv4(const char *src, u_char *dst, size_t size)
98 {
99 	static const char xdigits[] = "0123456789abcdef";
100 	static const char digits[] = "0123456789";
101 	int			n,
102 				ch,
103 				tmp = 0,
104 				dirty,
105 				bits;
106 	const u_char *odst = dst;
107 
108 	ch = *src++;
109 	if (ch == '0' && (src[0] == 'x' || src[0] == 'X')
110 		&& isxdigit((unsigned char) src[1]))
111 	{
112 		/* Hexadecimal: Eat nybble string. */
113 		if (size <= 0U)
114 			goto emsgsize;
115 		dirty = 0;
116 		src++;					/* skip x or X. */
117 		while ((ch = *src++) != '\0' && isxdigit((unsigned char) ch))
118 		{
119 			if (isupper((unsigned char) ch))
120 				ch = tolower((unsigned char) ch);
121 			n = strchr(xdigits, ch) - xdigits;
122 			assert(n >= 0 && n <= 15);
123 			if (dirty == 0)
124 				tmp = n;
125 			else
126 				tmp = (tmp << 4) | n;
127 			if (++dirty == 2)
128 			{
129 				if (size-- <= 0U)
130 					goto emsgsize;
131 				*dst++ = (u_char) tmp;
132 				dirty = 0;
133 			}
134 		}
135 		if (dirty)
136 		{						/* Odd trailing nybble? */
137 			if (size-- <= 0U)
138 				goto emsgsize;
139 			*dst++ = (u_char) (tmp << 4);
140 		}
141 	}
142 	else if (isdigit((unsigned char) ch))
143 	{
144 		/* Decimal: eat dotted digit string. */
145 		for (;;)
146 		{
147 			tmp = 0;
148 			do
149 			{
150 				n = strchr(digits, ch) - digits;
151 				assert(n >= 0 && n <= 9);
152 				tmp *= 10;
153 				tmp += n;
154 				if (tmp > 255)
155 					goto enoent;
156 			} while ((ch = *src++) != '\0' &&
157 					 isdigit((unsigned char) ch));
158 			if (size-- <= 0U)
159 				goto emsgsize;
160 			*dst++ = (u_char) tmp;
161 			if (ch == '\0' || ch == '/')
162 				break;
163 			if (ch != '.')
164 				goto enoent;
165 			ch = *src++;
166 			if (!isdigit((unsigned char) ch))
167 				goto enoent;
168 		}
169 	}
170 	else
171 		goto enoent;
172 
173 	bits = -1;
174 	if (ch == '/' && isdigit((unsigned char) src[0]) && dst > odst)
175 	{
176 		/* CIDR width specifier.  Nothing can follow it. */
177 		ch = *src++;			/* Skip over the /. */
178 		bits = 0;
179 		do
180 		{
181 			n = strchr(digits, ch) - digits;
182 			assert(n >= 0 && n <= 9);
183 			bits *= 10;
184 			bits += n;
185 		} while ((ch = *src++) != '\0' && isdigit((unsigned char) ch));
186 		if (ch != '\0')
187 			goto enoent;
188 		if (bits > 32)
189 			goto emsgsize;
190 	}
191 
192 	/* Firey death and destruction unless we prefetched EOS. */
193 	if (ch != '\0')
194 		goto enoent;
195 
196 	/* If nothing was written to the destination, we found no address. */
197 	if (dst == odst)
198 		goto enoent;
199 	/* If no CIDR spec was given, infer width from net class. */
200 	if (bits == -1)
201 	{
202 		if (*odst >= 240)		/* Class E */
203 			bits = 32;
204 		else if (*odst >= 224)	/* Class D */
205 			bits = 8;
206 		else if (*odst >= 192)	/* Class C */
207 			bits = 24;
208 		else if (*odst >= 128)	/* Class B */
209 			bits = 16;
210 		else
211 			/* Class A */
212 			bits = 8;
213 		/* If imputed mask is narrower than specified octets, widen. */
214 		if (bits < ((dst - odst) * 8))
215 			bits = (dst - odst) * 8;
216 
217 		/*
218 		 * If there are no additional bits specified for a class D address
219 		 * adjust bits to 4.
220 		 */
221 		if (bits == 8 && *odst == 224)
222 			bits = 4;
223 	}
224 	/* Extend network to cover the actual mask. */
225 	while (bits > ((dst - odst) * 8))
226 	{
227 		if (size-- <= 0U)
228 			goto emsgsize;
229 		*dst++ = '\0';
230 	}
231 	return bits;
232 
233 enoent:
234 	errno = ENOENT;
235 	return -1;
236 
237 emsgsize:
238 	errno = EMSGSIZE;
239 	return -1;
240 }
241 
242 /*
243  * int
244  * inet_net_pton(af, src, dst, *bits)
245  *	convert network address from presentation to network format.
246  *	accepts inet_pton()'s input for this "af" plus trailing "/CIDR".
247  *	"dst" is assumed large enough for its "af".  "bits" is set to the
248  *	/CIDR prefix length, which can have defaults (like /32 for IPv4).
249  * return:
250  *	-1 if an error occurred (inspect errno; ENOENT means bad format).
251  *	0 if successful conversion occurred.
252  * note:
253  *	192.5.5.1/28 has a nonzero host part, which means it isn't a network
254  *	as called for by inet_cidr_pton() but it can be a host address with
255  *	an included netmask.
256  * author:
257  *	Paul Vixie (ISC), October 1998
258  */
259 static int
inet_net_pton_ipv4(const char * src,u_char * dst)260 inet_net_pton_ipv4(const char *src, u_char *dst)
261 {
262 	static const char digits[] = "0123456789";
263 	const u_char *odst = dst;
264 	int			n,
265 				ch,
266 				tmp,
267 				bits;
268 	size_t		size = 4;
269 
270 	/* Get the mantissa. */
271 	while (ch = *src++, isdigit((unsigned char) ch))
272 	{
273 		tmp = 0;
274 		do
275 		{
276 			n = strchr(digits, ch) - digits;
277 			assert(n >= 0 && n <= 9);
278 			tmp *= 10;
279 			tmp += n;
280 			if (tmp > 255)
281 				goto enoent;
282 		} while ((ch = *src++) != '\0' && isdigit((unsigned char) ch));
283 		if (size-- == 0)
284 			goto emsgsize;
285 		*dst++ = (u_char) tmp;
286 		if (ch == '\0' || ch == '/')
287 			break;
288 		if (ch != '.')
289 			goto enoent;
290 	}
291 
292 	/* Get the prefix length if any. */
293 	bits = -1;
294 	if (ch == '/' && isdigit((unsigned char) src[0]) && dst > odst)
295 	{
296 		/* CIDR width specifier.  Nothing can follow it. */
297 		ch = *src++;			/* Skip over the /. */
298 		bits = 0;
299 		do
300 		{
301 			n = strchr(digits, ch) - digits;
302 			assert(n >= 0 && n <= 9);
303 			bits *= 10;
304 			bits += n;
305 		} while ((ch = *src++) != '\0' && isdigit((unsigned char) ch));
306 		if (ch != '\0')
307 			goto enoent;
308 		if (bits > 32)
309 			goto emsgsize;
310 	}
311 
312 	/* Firey death and destruction unless we prefetched EOS. */
313 	if (ch != '\0')
314 		goto enoent;
315 
316 	/* Prefix length can default to /32 only if all four octets spec'd. */
317 	if (bits == -1)
318 	{
319 		if (dst - odst == 4)
320 			bits = 32;
321 		else
322 			goto enoent;
323 	}
324 
325 	/* If nothing was written to the destination, we found no address. */
326 	if (dst == odst)
327 		goto enoent;
328 
329 	/* If prefix length overspecifies mantissa, life is bad. */
330 	if ((bits / 8) > (dst - odst))
331 		goto enoent;
332 
333 	/* Extend address to four octets. */
334 	while (size-- > 0)
335 		*dst++ = 0;
336 
337 	return bits;
338 
339 enoent:
340 	errno = ENOENT;
341 	return -1;
342 
343 emsgsize:
344 	errno = EMSGSIZE;
345 	return -1;
346 }
347 
348 static int
getbits(const char * src,int * bitsp)349 getbits(const char *src, int *bitsp)
350 {
351 	static const char digits[] = "0123456789";
352 	int			n;
353 	int			val;
354 	char		ch;
355 
356 	val = 0;
357 	n = 0;
358 	while ((ch = *src++) != '\0')
359 	{
360 		const char *pch;
361 
362 		pch = strchr(digits, ch);
363 		if (pch != NULL)
364 		{
365 			if (n++ != 0 && val == 0)	/* no leading zeros */
366 				return 0;
367 			val *= 10;
368 			val += (pch - digits);
369 			if (val > 128)		/* range */
370 				return 0;
371 			continue;
372 		}
373 		return 0;
374 	}
375 	if (n == 0)
376 		return 0;
377 	*bitsp = val;
378 	return 1;
379 }
380 
381 static int
getv4(const char * src,u_char * dst,int * bitsp)382 getv4(const char *src, u_char *dst, int *bitsp)
383 {
384 	static const char digits[] = "0123456789";
385 	u_char	   *odst = dst;
386 	int			n;
387 	u_int		val;
388 	char		ch;
389 
390 	val = 0;
391 	n = 0;
392 	while ((ch = *src++) != '\0')
393 	{
394 		const char *pch;
395 
396 		pch = strchr(digits, ch);
397 		if (pch != NULL)
398 		{
399 			if (n++ != 0 && val == 0)	/* no leading zeros */
400 				return 0;
401 			val *= 10;
402 			val += (pch - digits);
403 			if (val > 255)		/* range */
404 				return 0;
405 			continue;
406 		}
407 		if (ch == '.' || ch == '/')
408 		{
409 			if (dst - odst > 3) /* too many octets? */
410 				return 0;
411 			*dst++ = val;
412 			if (ch == '/')
413 				return getbits(src, bitsp);
414 			val = 0;
415 			n = 0;
416 			continue;
417 		}
418 		return 0;
419 	}
420 	if (n == 0)
421 		return 0;
422 	if (dst - odst > 3)			/* too many octets? */
423 		return 0;
424 	*dst++ = val;
425 	return 1;
426 }
427 
428 static int
inet_net_pton_ipv6(const char * src,u_char * dst)429 inet_net_pton_ipv6(const char *src, u_char *dst)
430 {
431 	return inet_cidr_pton_ipv6(src, dst, 16);
432 }
433 
434 #define NS_IN6ADDRSZ 16
435 #define NS_INT16SZ 2
436 #define NS_INADDRSZ 4
437 
438 static int
inet_cidr_pton_ipv6(const char * src,u_char * dst,size_t size)439 inet_cidr_pton_ipv6(const char *src, u_char *dst, size_t size)
440 {
441 	static const char xdigits_l[] = "0123456789abcdef",
442 				xdigits_u[] = "0123456789ABCDEF";
443 	u_char		tmp[NS_IN6ADDRSZ],
444 			   *tp,
445 			   *endp,
446 			   *colonp;
447 	const char *xdigits,
448 			   *curtok;
449 	int			ch,
450 				saw_xdigit;
451 	u_int		val;
452 	int			digits;
453 	int			bits;
454 
455 	if (size < NS_IN6ADDRSZ)
456 		goto emsgsize;
457 
458 	memset((tp = tmp), '\0', NS_IN6ADDRSZ);
459 	endp = tp + NS_IN6ADDRSZ;
460 	colonp = NULL;
461 	/* Leading :: requires some special handling. */
462 	if (*src == ':')
463 		if (*++src != ':')
464 			goto enoent;
465 	curtok = src;
466 	saw_xdigit = 0;
467 	val = 0;
468 	digits = 0;
469 	bits = -1;
470 	while ((ch = *src++) != '\0')
471 	{
472 		const char *pch;
473 
474 		if ((pch = strchr((xdigits = xdigits_l), ch)) == NULL)
475 			pch = strchr((xdigits = xdigits_u), ch);
476 		if (pch != NULL)
477 		{
478 			val <<= 4;
479 			val |= (pch - xdigits);
480 			if (++digits > 4)
481 				goto enoent;
482 			saw_xdigit = 1;
483 			continue;
484 		}
485 		if (ch == ':')
486 		{
487 			curtok = src;
488 			if (!saw_xdigit)
489 			{
490 				if (colonp)
491 					goto enoent;
492 				colonp = tp;
493 				continue;
494 			}
495 			else if (*src == '\0')
496 				goto enoent;
497 			if (tp + NS_INT16SZ > endp)
498 				goto enoent;
499 			*tp++ = (u_char) (val >> 8) & 0xff;
500 			*tp++ = (u_char) val & 0xff;
501 			saw_xdigit = 0;
502 			digits = 0;
503 			val = 0;
504 			continue;
505 		}
506 		if (ch == '.' && ((tp + NS_INADDRSZ) <= endp) &&
507 			getv4(curtok, tp, &bits) > 0)
508 		{
509 			tp += NS_INADDRSZ;
510 			saw_xdigit = 0;
511 			break;				/* '\0' was seen by inet_pton4(). */
512 		}
513 		if (ch == '/' && getbits(src, &bits) > 0)
514 			break;
515 		goto enoent;
516 	}
517 	if (saw_xdigit)
518 	{
519 		if (tp + NS_INT16SZ > endp)
520 			goto enoent;
521 		*tp++ = (u_char) (val >> 8) & 0xff;
522 		*tp++ = (u_char) val & 0xff;
523 	}
524 	if (bits == -1)
525 		bits = 128;
526 
527 	endp = tmp + 16;
528 
529 	if (colonp != NULL)
530 	{
531 		/*
532 		 * Since some memmove()'s erroneously fail to handle overlapping
533 		 * regions, we'll do the shift by hand.
534 		 */
535 		const int	n = tp - colonp;
536 		int			i;
537 
538 		if (tp == endp)
539 			goto enoent;
540 		for (i = 1; i <= n; i++)
541 		{
542 			endp[-i] = colonp[n - i];
543 			colonp[n - i] = 0;
544 		}
545 		tp = endp;
546 	}
547 	if (tp != endp)
548 		goto enoent;
549 
550 	/*
551 	 * Copy out the result.
552 	 */
553 	memcpy(dst, tmp, NS_IN6ADDRSZ);
554 
555 	return bits;
556 
557 enoent:
558 	errno = ENOENT;
559 	return -1;
560 
561 emsgsize:
562 	errno = EMSGSIZE;
563 	return -1;
564 }
565