aboutsummaryrefslogtreecommitdiffstats
path: root/usr/klibc/inet/inet_pton.c
blob: 19fe16e03f98d441d76d85cfb910aded8b47c443 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
/*
 * inet/inet_pton.c
 */

#include <stdio.h>
#include <stdint.h>
#include <errno.h>
#include <ctype.h>
#include <string.h>
#include <arpa/inet.h>
#include <netinet/in6.h>

static inline int hexval(int ch)
{
	if (ch >= '0' && ch <= '9') {
		return ch - '0';
	} else if (ch >= 'A' && ch <= 'F') {
		return ch - 'A' + 10;
	} else if (ch >= 'a' && ch <= 'f') {
		return ch - 'a' + 10;
	} else {
		return -1;
	}
}

int inet_pton(int af, const char *src, void *dst)
{
	switch (af) {
	case AF_INET:
		return inet_aton(src, (struct in_addr *)dst);

	case AF_INET6:
		{
			struct in6_addr *d = (struct in6_addr *)dst;
			int colons = 0, dcolons = 0;
			int i;
			const char *p;

			/* A double colon will increment colons by 2,
			   dcolons by 1 */
			for (p = dst; *p; p++) {
				if (p[0] == ':') {
					colons++;
					if (p[1] == ':')
						dcolons++;
				} else if (!isxdigit(*p))
					return 0;	/* Invalid address */
			}

			if (colons > 7 || dcolons > 1
			    || (!dcolons && colons != 7))
				return 0;	/* Invalid address */

			memset(d, 0, sizeof(struct in6_addr));

			i = 0;
			for (p = dst; *p; p++) {
				if (*p == ':') {
					if (p[1] == ':') {
						i += (8 - colons);
					} else {
						i++;
					}
				} else {
					d->s6_addr16[i] =
					    htons((ntohs(d->s6_addr16[i]) << 4)
						  + hexval(*p));
				}
			}

			return 1;
		}

	default:
		errno = EAFNOSUPPORT;
		return -1;
	}
}