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 2015 Nexenta Systems, Inc.  All rights reserved.
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 
31 #include <sys/socket.h>
32 #include <sys/types.h>
33 
34 #include <arpa/inet.h>
35 
36 #include <netinet/in.h>
37 
38 #include <ctype.h>
39 #include <err.h>
40 #include <errno.h>
41 #include <netdb.h>
42 #include <stdlib.h>
43 #include <strings.h>
44 
45 
46 boolean_t
47 inet_matchaddr(const void *sa, const char *name)
48 {
49 	boolean_t ret = B_FALSE;
50 	char *lname, *mp, *p;
51 	uint32_t claddr4 = 0;
52 
53 	if ((p = lname = strdup(name)) == NULL)
54 		err(1, "strdup");
55 
56 	if ((mp = strchr(p, '/')) != NULL)
57 		*mp++ = '\0';
58 
59 	switch (((struct sockaddr_in *)sa)->sin_family) {
60 	case AF_INET6: {
61 		char *pp;
62 		int prefix6;
63 		ipaddr_t ipaddr4;
64 		struct in6_addr hcaddr6;
65 		struct in6_addr *claddr6 =
66 		    &((struct sockaddr_in6 *)sa)->sin6_addr;
67 
68 		if (!IN6_IS_ADDR_V4MAPPED(claddr6)) {
69 			/* IPv6 address */
70 			if ((p = strchr(p, '[')) == NULL)
71 				break;
72 			p++;
73 
74 			if ((pp = strchr(p, ']')) == NULL)
75 				break;
76 			*pp = '\0';
77 
78 			if (inet_pton(AF_INET6, p, &hcaddr6) != 1)
79 				break;
80 
81 			if (mp != NULL) {
82 				/* Match only first prefix bits */
83 				if ((prefix6 = (int)strtol(mp,
84 				    (char **)NULL, 10)) == 0)
85 					break;
86 				ret = IN6_ARE_PREFIXEDADDR_EQUAL(claddr6,
87 				    &hcaddr6, prefix6);
88 				break;
89 			} else {
90 				/* No prefix, exact match */
91 				ret = IN6_ARE_ADDR_EQUAL(claddr6, &hcaddr6);
92 				break;
93 			}
94 		} else {
95 			/* IPv4-mapped IPv6 address, fallthrough to IPv4 */
96 			IN6_V4MAPPED_TO_IPADDR(claddr6, ipaddr4);
97 			claddr4 = ntohl(ipaddr4);
98 		}
99 		/*FALLTHROUGH*/
100 	}
101 	case AF_INET: {
102 		int bits, i;
103 		uint32_t hcaddr4 = 0, mask4;
104 
105 		if (claddr4 == 0) {
106 			claddr4 = ntohl(
107 			    ((struct sockaddr_in *)sa)->sin_addr.s_addr);
108 		}
109 
110 		for (i = 0; i < 4; i++) {
111 			hcaddr4 |= (int)strtol(p, (char **)NULL, 10) <<
112 			    ((3 - i) * 8);
113 			if ((p = strchr(p, '.')) == NULL)
114 				break;
115 			p++;
116 		}
117 
118 		if (hcaddr4 == 0 && errno != 0)
119 			break;
120 
121 		if (mp != NULL) {
122 			/* Mask is specified explicitly */
123 			if ((bits = (int)strtol(mp, (char **)NULL, 10)) == 0 &&
124 			    errno != 0)
125 				break;
126 			mask4 = bits ? ~0 << ((sizeof (struct in_addr) * NBBY)
127 			    - bits) : 0;
128 			hcaddr4 &= mask4;
129 		} else {
130 			/*
131 			 * Use old-fashioned implicit netmasking by checking
132 			 * for lower-end zeroes. On the off chance we don't
133 			 * match any well-known prefixes, return an exact-
134 			 * match prefix which is misleadingly labelled as
135 			 * IN_CLASSE_NET.
136 			 */
137 			if ((hcaddr4 & IN_CLASSA_HOST) == 0)
138 				mask4 = IN_CLASSA_NET;
139 			else if ((hcaddr4 & IN_CLASSB_HOST) == 0)
140 				mask4 = IN_CLASSB_NET;
141 			else if ((hcaddr4 & IN_CLASSC_HOST) == 0)
142 				mask4 = IN_CLASSC_NET;
143 			else
144 				mask4 = IN_CLASSE_NET;
145 		}
146 
147 		ret = ((claddr4 & mask4) == hcaddr4);
148 		break;
149 	}
150 	}
151 
152 	free(lname);
153 
154 	return (ret);
155 }
156