#include <sys/cdefs.h>
#if defined(LIBC_SCCS) && !defined(lint)
#if 0
static const char rcsid[] = "Id: inet_ntop.c,v 1.5 2005/11/03 22:59:52 marka Exp";
#else
__RCSID("$NetBSD: inet_ntop.c,v 1.12 2018/03/02 06:31:53 lukem Exp $");
#endif
#endif
#include "port_before.h"
#include "namespace.h"
#include <sys/param.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <arpa/nameser.h>
#include <assert.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "port_after.h"
#ifdef __weak_alias
__weak_alias(inet_ntop,_inet_ntop)
#endif
static const char *inet_ntop4(const u_char *src, char *dst, socklen_t size);
static const char *inet_ntop6(const u_char *src, char *dst, socklen_t size);
const char *
inet_ntop(int af, const void *src, char *dst, socklen_t size)
{
_DIAGASSERT(src != NULL);
_DIAGASSERT(dst != NULL);
switch (af) {
case AF_INET:
return inet_ntop4(src, dst, size);
case AF_INET6:
return inet_ntop6(src, dst, size);
default:
errno = EAFNOSUPPORT;
return NULL;
}
}
static const char *
inet_ntop4(const u_char *src, char *dst, socklen_t size)
{
char tmp[sizeof "255.255.255.255"];
int l;
_DIAGASSERT(src != NULL);
_DIAGASSERT(dst != NULL);
l = snprintf(tmp, sizeof(tmp), "%u.%u.%u.%u",
src[0], src[1], src[2], src[3]);
if (l <= 0 || (socklen_t) l >= size) {
errno = ENOSPC;
return NULL;
}
strlcpy(dst, tmp, size);
return dst;
}
static const char *
inet_ntop6(const u_char *src, char *dst, socklen_t size)
{
char tmp[sizeof "ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255"];
char *tp, *ep;
struct { int base, len; } best, cur;
u_int words[NS_IN6ADDRSZ / NS_INT16SZ];
int i;
int advance;
_DIAGASSERT(src != NULL);
_DIAGASSERT(dst != NULL);
memset(words, '\0', sizeof words);
for (i = 0; i < NS_IN6ADDRSZ; i++)
words[i / 2] |= (src[i] << ((1 - (i % 2)) << 3));
best.base = -1;
best.len = 0;
cur.base = -1;
cur.len = 0;
for (i = 0; i < (NS_IN6ADDRSZ / NS_INT16SZ); i++) {
if (words[i] == 0) {
if (cur.base == -1)
cur.base = i, cur.len = 1;
else
cur.len++;
} else {
if (cur.base != -1) {
if (best.base == -1 || cur.len > best.len)
best = cur;
cur.base = -1;
}
}
}
if (cur.base != -1) {
if (best.base == -1 || cur.len > best.len)
best = cur;
}
if (best.base != -1 && best.len < 2)
best.base = -1;
tp = tmp;
ep = tmp + sizeof(tmp);
for (i = 0; i < (NS_IN6ADDRSZ / NS_INT16SZ); i++) {
if (best.base != -1 && i >= best.base &&
i < (best.base + best.len)) {
if (i == best.base)
*tp++ = ':';
continue;
}
if (i != 0) {
if (tp + 1 >= ep)
goto out;
*tp++ = ':';
}
if (i == 6 && best.base == 0 &&
(best.len == 6 ||
(best.len == 7 && words[7] != 0x0001) ||
(best.len == 5 && words[5] == 0xffff))) {
if (!inet_ntop4(src + 12, tp, (socklen_t)(ep - tp)))
goto out;
tp += strlen(tp);
break;
}
advance = snprintf(tp, (size_t)(ep - tp), "%x", words[i]);
if (advance <= 0 || advance >= ep - tp)
goto out;
tp += advance;
}
if (best.base != -1 && (best.base + best.len) ==
(NS_IN6ADDRSZ / NS_INT16SZ)) {
if (tp + 1 >= ep)
goto out;
*tp++ = ':';
}
if (tp + 1 >= ep)
goto out;
*tp++ = '\0';
if ((size_t)(tp - tmp) > size)
goto out;
strlcpy(dst, tmp, size);
return dst;
out:
errno = ENOSPC;
return NULL;
}