/* * This file and its contents are supplied under the terms of the * Common Development and Distribution License ("CDDL"), version 1.0. * You may only use this file in accordance with the terms of version * 1.0 of the CDDL. * * A full copy of the text of the CDDL should have accompanied this * source. A copy of the CDDL is also available via the Internet at * http://www.illumos.org/license/CDDL. */ /* * Copyright 2016 Nexenta Systems, Inc. */ /* * inet_matchaddr * * Match IPv4 or IPv6 address provided in sa (sockaddr_in/sockaddr_in6) * against standard text representation specified in name: * * IPv4: * IPv4 * IPv4/netmask * * IPv6: * [IPv6] * [IPv6]/prefix * * Return values: * * 0 mismatch * 1 match * -1 error occured, caller should check errno: * EINVAL access list entry is invalid * ENOMEM failed to allocate memory */ #include #include #include #include #include #include #include #include #include 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)) { /* IPv6 address */ 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) { /* Match only first prefix bits */ 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 { /* No prefix, exact match */ ret = IN6_ARE_ADDR_EQUAL(claddr6, &hcaddr6) ? 1 : 0; break; } } else { /* IPv4-mapped IPv6 address, fallthrough to IPv4 */ IN6_V4MAPPED_TO_IPADDR(claddr6, ipaddr4); claddr4 = ntohl(ipaddr4); } } /*FALLTHROUGH*/ 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) { /* Mask is specified explicitly */ long mb; errno = 0; mb = strtol(mp, &ep, 10); if (errno != 0 || mb < 0 || mb > 32 || *ep != '\0') { errno = EINVAL; break; } mask4 = mb ? ~0 << ((sizeof (struct in_addr) * NBBY) - mb) : 0; hcaddr4 &= mask4; } else { /* * Use old-fashioned implicit netmasking by checking * for lower-end zeroes. On the off chance we don't * match any well-known prefixes, return an exact- * match prefix which is misleadingly labelled as * IN_CLASSE_NET. */ 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); }