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