#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <stdio.h>
#include <isc/buffer.h>
#include <isc/region.h>
#include <isc/sockaddr.h>
#include <string.h>
#include <isc/util.h>
int
isc_sockaddr_equal(const struct sockaddr_storage *a, const struct sockaddr_storage *b) {
return (isc_sockaddr_compare(a, b, ISC_SOCKADDR_CMPADDR|
ISC_SOCKADDR_CMPPORT|
ISC_SOCKADDR_CMPSCOPE));
}
int
isc_sockaddr_eqaddr(const struct sockaddr_storage *a, const struct sockaddr_storage *b) {
return (isc_sockaddr_compare(a, b, ISC_SOCKADDR_CMPADDR|
ISC_SOCKADDR_CMPSCOPE));
}
int
isc_sockaddr_compare(const struct sockaddr_storage *a, const struct sockaddr_storage *b,
unsigned int flags)
{
struct sockaddr_in *sin_a, *sin_b;
struct sockaddr_in6 *sin6_a, *sin6_b;
REQUIRE(a != NULL && b != NULL);
if (a->ss_len != b->ss_len)
return (0);
if (a->ss_family != b->ss_family)
return (0);
switch (a->ss_family) {
case AF_INET:
sin_a = (struct sockaddr_in *) a;
sin_b = (struct sockaddr_in *) b;
if ((flags & ISC_SOCKADDR_CMPADDR) != 0 &&
memcmp(&sin_a->sin_addr, &sin_b->sin_addr,
sizeof(sin_a->sin_addr)) != 0)
return (0);
if ((flags & ISC_SOCKADDR_CMPPORT) != 0 &&
sin_a->sin_port != sin_b->sin_port)
return (0);
break;
case AF_INET6:
sin6_a = (struct sockaddr_in6 *) a;
sin6_b = (struct sockaddr_in6 *) b;
if ((flags & ISC_SOCKADDR_CMPADDR) != 0 &&
memcmp(&sin6_a->sin6_addr, &sin6_b->sin6_addr,
sizeof(sin6_a->sin6_addr)) != 0)
return (0);
if ((flags & ISC_SOCKADDR_CMPSCOPE) != 0 &&
sin6_a->sin6_scope_id != sin6_b->sin6_scope_id &&
((flags & ISC_SOCKADDR_CMPSCOPEZERO) == 0 ||
(sin6_a->sin6_scope_id != 0 &&
sin6_b->sin6_scope_id != 0)))
return (0);
if ((flags & ISC_SOCKADDR_CMPPORT) != 0 &&
sin6_a->sin6_port != sin6_b->sin6_port)
return (0);
break;
default:
if (memcmp(a, b, a->ss_len) != 0)
return (0);
}
return (1);
}
isc_result_t
isc_sockaddr_totext(const struct sockaddr_storage *sockaddr, isc_buffer_t *target) {
struct sockaddr_in *sin;
struct sockaddr_in6 *sin6;
char pbuf[sizeof("65000")];
unsigned int plen;
isc_region_t avail;
char tmp[NI_MAXHOST];
REQUIRE(sockaddr != NULL);
switch (sockaddr->ss_family) {
case AF_INET:
sin = (struct sockaddr_in *)sockaddr;
snprintf(pbuf, sizeof(pbuf), "%u", ntohs(sin->sin_port));
break;
case AF_INET6:
sin6 = (struct sockaddr_in6 *)sockaddr;
snprintf(pbuf, sizeof(pbuf), "%u", ntohs(sin6->sin6_port));
break;
default:
return (ISC_R_FAILURE);
}
plen = strlen(pbuf);
INSIST(plen < sizeof(pbuf));
if (getnameinfo((struct sockaddr *)sockaddr, sockaddr->ss_len,
tmp, sizeof(tmp), NULL, 0, NI_NUMERICHOST | NI_NUMERICSERV) != 0)
return (ISC_R_FAILURE);
if (strlen(tmp) > isc_buffer_availablelength(target))
return (ISC_R_NOSPACE);
isc_buffer_putmem(target, tmp, strlen(tmp));
if (1 + plen + 1 > isc_buffer_availablelength(target))
return (ISC_R_NOSPACE);
isc_buffer_putmem(target, (const unsigned char *)"#", 1);
isc_buffer_putmem(target, (const unsigned char *)pbuf, plen);
isc_buffer_availableregion(target, &avail);
INSIST(avail.length >= 1);
avail.base[0] = '\0';
return (ISC_R_SUCCESS);
}
void
isc_sockaddr_format(const struct sockaddr_storage *sa, char *array, unsigned int size) {
isc_result_t result;
isc_buffer_t buf;
if (size == 0U)
return;
isc_buffer_init(&buf, array, size);
result = isc_sockaddr_totext(sa, &buf);
if (result != ISC_R_SUCCESS) {
snprintf(array, size, "<unknown address, family %u>",
sa->ss_family);
array[size - 1] = '\0';
}
}
void
isc_sockaddr_any(struct sockaddr_storage *sockaddr)
{
struct sockaddr_in *sin = (struct sockaddr_in *) sockaddr;
memset(sockaddr, 0, sizeof(*sockaddr));
sin->sin_family = AF_INET;
sin->sin_len = sizeof(*sin);
sin->sin_addr.s_addr = INADDR_ANY;
sin->sin_port = 0;
}
void
isc_sockaddr_any6(struct sockaddr_storage *sockaddr)
{
struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) sockaddr;
memset(sockaddr, 0, sizeof(*sockaddr));
sin6->sin6_family = AF_INET6;
sin6->sin6_len = sizeof(*sin6);
sin6->sin6_addr = in6addr_any;
sin6->sin6_port = 0;
}
void
isc_sockaddr_anyofpf(struct sockaddr_storage *sockaddr, int pf) {
switch (pf) {
case AF_INET:
isc_sockaddr_any(sockaddr);
break;
case AF_INET6:
isc_sockaddr_any6(sockaddr);
break;
default:
INSIST(0);
}
}
int
isc_sockaddr_pf(const struct sockaddr_storage *sockaddr) {
return (sockaddr->ss_family);
}
in_port_t
isc_sockaddr_getport(const struct sockaddr_storage *sockaddr) {
struct sockaddr_in *sin;
struct sockaddr_in6 *sin6;
switch (sockaddr->ss_family) {
case AF_INET:
sin = (struct sockaddr_in *)sockaddr;
return (ntohs(sin->sin_port));
break;
case AF_INET6:
sin6 = (struct sockaddr_in6 *)sockaddr;
return (ntohs(sin6->sin6_port));
break;
default:
FATAL_ERROR(__FILE__, __LINE__,
"unknown address family: %d",
(int)sockaddr->ss_family);
}
}
int
isc_sockaddr_ismulticast(const struct sockaddr_storage *sockaddr) {
struct sockaddr_in *sin;
struct sockaddr_in6 *sin6;
switch (sockaddr->ss_family) {
case AF_INET:
sin = (struct sockaddr_in *)sockaddr;
return (IN_MULTICAST(ntohl(sin->sin_addr.s_addr)));
case AF_INET6:
sin6 = (struct sockaddr_in6 *)sockaddr;
return (IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr));
default:
return (0);
}
}
int
isc_sockaddr_issitelocal(const struct sockaddr_storage *sockaddr) {
struct sockaddr_in6 *sin6;
if (sockaddr->ss_family == AF_INET6) {
sin6 = (struct sockaddr_in6 *)sockaddr;
return (IN6_IS_ADDR_SITELOCAL(&sin6->sin6_addr));
}
return (0);
}
int
isc_sockaddr_islinklocal(const struct sockaddr_storage *sockaddr) {
struct sockaddr_in6 *sin6;
if (sockaddr->ss_family == AF_INET6) {
sin6 = (struct sockaddr_in6 *)sockaddr;
return (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr));
}
return (0);
}