1 /* 2 * This file and its contents are supplied under the terms of the 3 * Common Development and Distribution License ("CDDL"), version 1.0. 4 * You may only use this file in accordance with the terms of version 5 * 1.0 of the CDDL. 6 * 7 * A full copy of the text of the CDDL should have accompanied this 8 * source. A copy of the CDDL is also available via the Internet at 9 * http://www.illumos.org/license/CDDL. 10 */ 11 12 /* 13 * Copyright 2016 Nexenta Systems, Inc. 14 */ 15 16 /* 17 * inet_matchaddr 18 * 19 * Match IPv4 or IPv6 address provided in sa (sockaddr_in/sockaddr_in6) 20 * against standard text representation specified in name: 21 * 22 * IPv4: 23 * IPv4 24 * IPv4/netmask 25 * 26 * IPv6: 27 * [IPv6] 28 * [IPv6]/prefix 29 * 30 * Return values: 31 * 32 * 0 mismatch 33 * 1 match 34 * -1 error occured, caller should check errno: 35 * EINVAL access list entry is invalid 36 * ENOMEM failed to allocate memory 37 */ 38 39 #include <sys/socket.h> 40 #include <sys/types.h> 41 42 #include <arpa/inet.h> 43 44 #include <netinet/in.h> 45 46 #include <ctype.h> 47 #include <errno.h> 48 #include <netdb.h> 49 #include <stdlib.h> 50 #include <strings.h> 51 52 int 53 inet_matchaddr(const void *sa, const char *name) 54 { 55 int ret = -1; 56 char *lname, *mp, *p; 57 char *ep; 58 int serrno = errno; 59 uint32_t claddr4 = 0; 60 61 if ((p = lname = strdup(name)) == NULL) { 62 errno = ENOMEM; 63 return (-1); 64 } 65 66 if ((mp = strchr(p, '/')) != NULL) 67 *mp++ = '\0'; 68 69 switch (((struct sockaddr_in *)sa)->sin_family) { 70 case AF_INET6: { 71 char *pp; 72 ipaddr_t ipaddr4; 73 struct in6_addr hcaddr6; 74 struct in6_addr *claddr6 = 75 &((struct sockaddr_in6 *)sa)->sin6_addr; 76 77 if (!IN6_IS_ADDR_V4MAPPED(claddr6)) { 78 /* IPv6 address */ 79 if (*p != '[') { 80 errno = EINVAL; 81 break; 82 } 83 p++; 84 85 if ((pp = strchr(p, ']')) == NULL || 86 (mp != NULL && pp != mp - 2) || 87 (mp == NULL && *(pp + 1) != '\0')) { 88 errno = EINVAL; 89 break; 90 } 91 *pp = '\0'; 92 93 if (inet_pton(AF_INET6, p, &hcaddr6) != 1) { 94 errno = EINVAL; 95 break; 96 } 97 98 if (mp != NULL) { 99 /* Match only first prefix bits */ 100 long prefix6; 101 102 errno = 0; 103 prefix6 = strtol(mp, &ep, 10); 104 if (errno != 0 || prefix6 < 0 || 105 prefix6 > 128 || *ep != '\0') { 106 errno = EINVAL; 107 break; 108 } 109 ret = IN6_ARE_PREFIXEDADDR_EQUAL(claddr6, 110 &hcaddr6, prefix6) ? 1 : 0; 111 break; 112 } else { 113 /* No prefix, exact match */ 114 ret = IN6_ARE_ADDR_EQUAL(claddr6, 115 &hcaddr6) ? 1 : 0; 116 break; 117 } 118 } else { 119 /* IPv4-mapped IPv6 address, fallthrough to IPv4 */ 120 IN6_V4MAPPED_TO_IPADDR(claddr6, ipaddr4); 121 claddr4 = ntohl(ipaddr4); 122 } 123 /*FALLTHROUGH*/ 124 } 125 case AF_INET: { 126 int i; 127 uint32_t hcaddr4 = 0, mask4; 128 129 if (claddr4 == 0) { 130 claddr4 = ntohl( 131 ((struct sockaddr_in *)sa)->sin_addr.s_addr); 132 } 133 134 for (i = 0; i < 4; i++) { 135 long qaddr4; 136 137 errno = 0; 138 qaddr4 = strtol(p, &ep, 10); 139 if (errno != 0 || qaddr4 < 0 || qaddr4 > 255 || 140 (*ep != '.' && *ep != '\0')) { 141 errno = EINVAL; 142 break; 143 } 144 hcaddr4 |= qaddr4 << ((3 - i) * 8); 145 if (*ep == '\0') 146 break; 147 p = ep + 1; 148 } 149 150 if (errno != 0) 151 break; 152 153 if (mp != NULL) { 154 /* Mask is specified explicitly */ 155 long mb; 156 157 errno = 0; 158 mb = strtol(mp, &ep, 10); 159 if (errno != 0 || mb < 0 || mb > 32 || *ep != '\0') { 160 errno = EINVAL; 161 break; 162 } 163 mask4 = mb ? ~0 << ((sizeof (struct in_addr) * NBBY) 164 - mb) : 0; 165 hcaddr4 &= mask4; 166 } else { 167 /* 168 * Use old-fashioned implicit netmasking by checking 169 * for lower-end zeroes. On the off chance we don't 170 * match any well-known prefixes, return an exact- 171 * match prefix which is misleadingly labelled as 172 * IN_CLASSE_NET. 173 */ 174 if ((hcaddr4 & IN_CLASSA_HOST) == 0) 175 mask4 = IN_CLASSA_NET; 176 else if ((hcaddr4 & IN_CLASSB_HOST) == 0) 177 mask4 = IN_CLASSB_NET; 178 else if ((hcaddr4 & IN_CLASSC_HOST) == 0) 179 mask4 = IN_CLASSC_NET; 180 else 181 mask4 = IN_CLASSE_NET; 182 } 183 184 ret = ((claddr4 & mask4) == hcaddr4) ? 1 : 0; 185 break; 186 } 187 } 188 189 free(lname); 190 191 if (ret != -1) 192 errno = serrno; 193 return (ret); 194 } 195