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