#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <ctype.h>
#include <errno.h>
#include <stdint.h>
#include <netdb.h>
#include <stdlib.h>
#include <strings.h>
int
inet_matchaddr(const void *sa, const char *name)
{
int ret = -1;
char *lname, *mp, *p;
char *ep;
int serrno = errno;
uint32_t claddr4 = 0;
if ((p = lname = strdup(name)) == NULL) {
errno = ENOMEM;
return (-1);
}
if ((mp = strchr(p, '/')) != NULL)
*mp++ = '\0';
switch (((struct sockaddr_in *)sa)->sin_family) {
case AF_INET6: {
char *pp;
ipaddr_t ipaddr4;
struct in6_addr hcaddr6;
struct in6_addr *claddr6 =
&((struct sockaddr_in6 *)sa)->sin6_addr;
if (!IN6_IS_ADDR_V4MAPPED(claddr6)) {
if (*p != '[') {
errno = EINVAL;
break;
}
p++;
if ((pp = strchr(p, ']')) == NULL ||
(mp != NULL && pp != mp - 2) ||
(mp == NULL && *(pp + 1) != '\0')) {
errno = EINVAL;
break;
}
*pp = '\0';
if (inet_pton(AF_INET6, p, &hcaddr6) != 1) {
errno = EINVAL;
break;
}
if (mp != NULL) {
long prefix6;
errno = 0;
prefix6 = strtol(mp, &ep, 10);
if (errno != 0 || prefix6 < 0 ||
prefix6 > 128 || *ep != '\0') {
errno = EINVAL;
break;
}
ret = IN6_ARE_PREFIXEDADDR_EQUAL(claddr6,
&hcaddr6, prefix6) ? 1 : 0;
break;
} else {
ret = IN6_ARE_ADDR_EQUAL(claddr6,
&hcaddr6) ? 1 : 0;
break;
}
} else {
IN6_V4MAPPED_TO_IPADDR(claddr6, ipaddr4);
claddr4 = ntohl(ipaddr4);
}
}
case AF_INET: {
int i;
uint32_t hcaddr4 = 0, mask4;
if (claddr4 == 0) {
claddr4 = ntohl(
((struct sockaddr_in *)sa)->sin_addr.s_addr);
}
for (i = 0; i < 4; i++) {
long qaddr4;
errno = 0;
qaddr4 = strtol(p, &ep, 10);
if (errno != 0 || qaddr4 < 0 || qaddr4 > 255 ||
(*ep != '.' && *ep != '\0')) {
errno = EINVAL;
break;
}
hcaddr4 |= qaddr4 << ((3 - i) * 8);
if (*ep == '\0')
break;
p = ep + 1;
}
if (errno != 0)
break;
if (mp != NULL) {
long mb;
errno = 0;
mb = strtol(mp, &ep, 10);
if (errno != 0 || mb < 0 || mb > 32 || *ep != '\0') {
errno = EINVAL;
break;
}
if (mb != 0) {
mask4 =
UINT32_MAX <<
((sizeof (struct in_addr) * NBBY) - mb);
} else {
mask4 = 0;
}
hcaddr4 &= mask4;
} else {
if ((hcaddr4 & IN_CLASSA_HOST) == 0)
mask4 = IN_CLASSA_NET;
else if ((hcaddr4 & IN_CLASSB_HOST) == 0)
mask4 = IN_CLASSB_NET;
else if ((hcaddr4 & IN_CLASSC_HOST) == 0)
mask4 = IN_CLASSC_NET;
else
mask4 = IN_CLASSE_NET;
}
ret = ((claddr4 & mask4) == hcaddr4) ? 1 : 0;
break;
}
}
free(lname);
if (ret != -1)
errno = serrno;
return (ret);
}