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