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 <stdint.h> 49 #include <netdb.h> 50 #include <stdlib.h> 51 #include <strings.h> 52 53 int 54 inet_matchaddr(const void *sa, const char *name) 55 { 56 int ret = -1; 57 char *lname, *mp, *p; 58 char *ep; 59 int serrno = errno; 60 uint32_t claddr4 = 0; 61 62 if ((p = lname = strdup(name)) == NULL) { 63 errno = ENOMEM; 64 return (-1); 65 } 66 67 if ((mp = strchr(p, '/')) != NULL) 68 *mp++ = '\0'; 69 70 switch (((struct sockaddr_in *)sa)->sin_family) { 71 case AF_INET6: { 72 char *pp; 73 ipaddr_t ipaddr4; 74 struct in6_addr hcaddr6; 75 struct in6_addr *claddr6 = 76 &((struct sockaddr_in6 *)sa)->sin6_addr; 77 78 if (!IN6_IS_ADDR_V4MAPPED(claddr6)) { 79 /* IPv6 address */ 80 if (*p != '[') { 81 errno = EINVAL; 82 break; 83 } 84 p++; 85 86 if ((pp = strchr(p, ']')) == NULL || 87 (mp != NULL && pp != mp - 2) || 88 (mp == NULL && *(pp + 1) != '\0')) { 89 errno = EINVAL; 90 break; 91 } 92 *pp = '\0'; 93 94 if (inet_pton(AF_INET6, p, &hcaddr6) != 1) { 95 errno = EINVAL; 96 break; 97 } 98 99 if (mp != NULL) { 100 /* Match only first prefix bits */ 101 long prefix6; 102 103 errno = 0; 104 prefix6 = strtol(mp, &ep, 10); 105 if (errno != 0 || prefix6 < 0 || 106 prefix6 > 128 || *ep != '\0') { 107 errno = EINVAL; 108 break; 109 } 110 ret = IN6_ARE_PREFIXEDADDR_EQUAL(claddr6, 111 &hcaddr6, prefix6) ? 1 : 0; 112 break; 113 } else { 114 /* No prefix, exact match */ 115 ret = IN6_ARE_ADDR_EQUAL(claddr6, 116 &hcaddr6) ? 1 : 0; 117 break; 118 } 119 } else { 120 /* IPv4-mapped IPv6 address, fallthrough to IPv4 */ 121 IN6_V4MAPPED_TO_IPADDR(claddr6, ipaddr4); 122 claddr4 = ntohl(ipaddr4); 123 } 124 } 125 /*FALLTHROUGH*/ 126 case AF_INET: { 127 int i; 128 uint32_t hcaddr4 = 0, mask4; 129 130 if (claddr4 == 0) { 131 claddr4 = ntohl( 132 ((struct sockaddr_in *)sa)->sin_addr.s_addr); 133 } 134 135 for (i = 0; i < 4; i++) { 136 long qaddr4; 137 138 errno = 0; 139 qaddr4 = strtol(p, &ep, 10); 140 if (errno != 0 || qaddr4 < 0 || qaddr4 > 255 || 141 (*ep != '.' && *ep != '\0')) { 142 errno = EINVAL; 143 break; 144 } 145 hcaddr4 |= qaddr4 << ((3 - i) * 8); 146 if (*ep == '\0') 147 break; 148 p = ep + 1; 149 } 150 151 if (errno != 0) 152 break; 153 154 if (mp != NULL) { 155 /* Mask is specified explicitly */ 156 long mb; 157 158 errno = 0; 159 mb = strtol(mp, &ep, 10); 160 if (errno != 0 || mb < 0 || mb > 32 || *ep != '\0') { 161 errno = EINVAL; 162 break; 163 } 164 if (mb != 0) { 165 mask4 = 166 UINT32_MAX << 167 ((sizeof (struct in_addr) * NBBY) - mb); 168 } else { 169 mask4 = 0; 170 } 171 hcaddr4 &= mask4; 172 } else { 173 /* 174 * Use old-fashioned implicit netmasking by checking 175 * for lower-end zeroes. On the off chance we don't 176 * match any well-known prefixes, return an exact- 177 * match prefix which is misleadingly labelled as 178 * IN_CLASSE_NET. 179 */ 180 if ((hcaddr4 & IN_CLASSA_HOST) == 0) 181 mask4 = IN_CLASSA_NET; 182 else if ((hcaddr4 & IN_CLASSB_HOST) == 0) 183 mask4 = IN_CLASSB_NET; 184 else if ((hcaddr4 & IN_CLASSC_HOST) == 0) 185 mask4 = IN_CLASSC_NET; 186 else 187 mask4 = IN_CLASSE_NET; 188 } 189 190 ret = ((claddr4 & mask4) == hcaddr4) ? 1 : 0; 191 break; 192 } 193 } 194 195 free(lname); 196 197 if (ret != -1) 198 errno = serrno; 199 return (ret); 200 } 201