diff options
Diffstat (limited to 'usr/klibc/inet/inet_pton.c')
-rw-r--r-- | usr/klibc/inet/inet_pton.c | 78 |
1 files changed, 78 insertions, 0 deletions
diff --git a/usr/klibc/inet/inet_pton.c b/usr/klibc/inet/inet_pton.c new file mode 100644 index 0000000..19fe16e --- /dev/null +++ b/usr/klibc/inet/inet_pton.c @@ -0,0 +1,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; + } +} |