xref: /titanic_44/usr/src/cmd/ssh/libssh/common/addrmatch.c (revision 56e7e6c6e6c82bcc8d19a12a3bd8f9fd529ba5b4)
1b07b2f5cSHuie-Ying Lee /*	$OpenBSD: addrmatch.c,v 1.4 2008/12/10 03:55:20 stevesk Exp $ */
2b07b2f5cSHuie-Ying Lee 
3b07b2f5cSHuie-Ying Lee /*
4b07b2f5cSHuie-Ying Lee  * Copyright (c) 2004-2008 Damien Miller <djm@mindrot.org>
5b07b2f5cSHuie-Ying Lee  *
6b07b2f5cSHuie-Ying Lee  * Permission to use, copy, modify, and distribute this software for any
7b07b2f5cSHuie-Ying Lee  * purpose with or without fee is hereby granted, provided that the above
8b07b2f5cSHuie-Ying Lee  * copyright notice and this permission notice appear in all copies.
9b07b2f5cSHuie-Ying Lee  *
10b07b2f5cSHuie-Ying Lee  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11b07b2f5cSHuie-Ying Lee  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12b07b2f5cSHuie-Ying Lee  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13b07b2f5cSHuie-Ying Lee  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14b07b2f5cSHuie-Ying Lee  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15b07b2f5cSHuie-Ying Lee  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16b07b2f5cSHuie-Ying Lee  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17b07b2f5cSHuie-Ying Lee  */
18b07b2f5cSHuie-Ying Lee 
19b07b2f5cSHuie-Ying Lee #include "includes.h"
20b07b2f5cSHuie-Ying Lee 
21b07b2f5cSHuie-Ying Lee #include <sys/types.h>
22b07b2f5cSHuie-Ying Lee #include <sys/socket.h>
23b07b2f5cSHuie-Ying Lee #include <netinet/in.h>
24b07b2f5cSHuie-Ying Lee #include <arpa/inet.h>
25b07b2f5cSHuie-Ying Lee 
26b07b2f5cSHuie-Ying Lee #include <netdb.h>
27b07b2f5cSHuie-Ying Lee #include <string.h>
28b07b2f5cSHuie-Ying Lee #include <stdlib.h>
29b07b2f5cSHuie-Ying Lee #include <stdio.h>
30b07b2f5cSHuie-Ying Lee #include <stdarg.h>
31b07b2f5cSHuie-Ying Lee 
32b07b2f5cSHuie-Ying Lee #include "match.h"
33b07b2f5cSHuie-Ying Lee #include "log.h"
34b07b2f5cSHuie-Ying Lee #include "xmalloc.h"
35b07b2f5cSHuie-Ying Lee 
36b07b2f5cSHuie-Ying Lee struct xaddr {
37b07b2f5cSHuie-Ying Lee 	sa_family_t	af;
38b07b2f5cSHuie-Ying Lee 	union {
39b07b2f5cSHuie-Ying Lee 		struct in_addr		v4;
40b07b2f5cSHuie-Ying Lee 		struct in6_addr		v6;
41b07b2f5cSHuie-Ying Lee 		u_int8_t		addr8[16];
42b07b2f5cSHuie-Ying Lee 		u_int32_t		addr32[4];
43b07b2f5cSHuie-Ying Lee 	} xa;		    /* 128-bit address */
44b07b2f5cSHuie-Ying Lee 	u_int32_t	scope_id;	/* iface scope id for v6 */
45b07b2f5cSHuie-Ying Lee #define v4	xa.v4
46b07b2f5cSHuie-Ying Lee #define v6	xa.v6
47b07b2f5cSHuie-Ying Lee #define addr8	xa.addr8
48b07b2f5cSHuie-Ying Lee #define addr32	xa.addr32
49b07b2f5cSHuie-Ying Lee };
50b07b2f5cSHuie-Ying Lee 
51b07b2f5cSHuie-Ying Lee static int
addr_unicast_masklen(int af)52b07b2f5cSHuie-Ying Lee addr_unicast_masklen(int af)
53b07b2f5cSHuie-Ying Lee {
54b07b2f5cSHuie-Ying Lee 	switch (af) {
55b07b2f5cSHuie-Ying Lee 	case AF_INET:
56b07b2f5cSHuie-Ying Lee 		return 32;
57b07b2f5cSHuie-Ying Lee 	case AF_INET6:
58b07b2f5cSHuie-Ying Lee 		return 128;
59b07b2f5cSHuie-Ying Lee 	default:
60b07b2f5cSHuie-Ying Lee 		return -1;
61b07b2f5cSHuie-Ying Lee 	}
62b07b2f5cSHuie-Ying Lee }
63b07b2f5cSHuie-Ying Lee 
64b07b2f5cSHuie-Ying Lee static inline int
masklen_valid(int af,u_int masklen)65b07b2f5cSHuie-Ying Lee masklen_valid(int af, u_int masklen)
66b07b2f5cSHuie-Ying Lee {
67b07b2f5cSHuie-Ying Lee 	switch (af) {
68b07b2f5cSHuie-Ying Lee 	case AF_INET:
69b07b2f5cSHuie-Ying Lee 		return masklen <= 32 ? 0 : -1;
70b07b2f5cSHuie-Ying Lee 	case AF_INET6:
71b07b2f5cSHuie-Ying Lee 		return masklen <= 128 ? 0 : -1;
72b07b2f5cSHuie-Ying Lee 	default:
73b07b2f5cSHuie-Ying Lee 		return -1;
74b07b2f5cSHuie-Ying Lee 	}
75b07b2f5cSHuie-Ying Lee }
76b07b2f5cSHuie-Ying Lee 
77b07b2f5cSHuie-Ying Lee /*
78b07b2f5cSHuie-Ying Lee  * Convert struct sockaddr to struct xaddr
79b07b2f5cSHuie-Ying Lee  * Returns 0 on success, -1 on failure.
80b07b2f5cSHuie-Ying Lee  */
81b07b2f5cSHuie-Ying Lee static int
addr_sa_to_xaddr(struct sockaddr * sa,socklen_t slen,struct xaddr * xa)82b07b2f5cSHuie-Ying Lee addr_sa_to_xaddr(struct sockaddr *sa, socklen_t slen, struct xaddr *xa)
83b07b2f5cSHuie-Ying Lee {
84*56e7e6c6SHuie-Ying Lee 	/* LINTED E_BAD_PTR_CAST_ALIGN */
85b07b2f5cSHuie-Ying Lee 	struct sockaddr_in *in4 = (struct sockaddr_in *)sa;
86*56e7e6c6SHuie-Ying Lee 	/* LINTED E_BAD_PTR_CAST_ALIGN */
87b07b2f5cSHuie-Ying Lee 	struct sockaddr_in6 *in6 = (struct sockaddr_in6 *)sa;
88b07b2f5cSHuie-Ying Lee 
89b07b2f5cSHuie-Ying Lee 	memset(xa, '\0', sizeof(*xa));
90b07b2f5cSHuie-Ying Lee 
91b07b2f5cSHuie-Ying Lee 	switch (sa->sa_family) {
92b07b2f5cSHuie-Ying Lee 	case AF_INET:
93b07b2f5cSHuie-Ying Lee 		if (slen < sizeof(*in4))
94b07b2f5cSHuie-Ying Lee 			return -1;
95b07b2f5cSHuie-Ying Lee 		xa->af = AF_INET;
96b07b2f5cSHuie-Ying Lee 		memcpy(&xa->v4, &in4->sin_addr, sizeof(xa->v4));
97b07b2f5cSHuie-Ying Lee 		break;
98b07b2f5cSHuie-Ying Lee 	case AF_INET6:
99b07b2f5cSHuie-Ying Lee 		if (slen < sizeof(*in6))
100b07b2f5cSHuie-Ying Lee 			return -1;
101b07b2f5cSHuie-Ying Lee 		xa->af = AF_INET6;
102b07b2f5cSHuie-Ying Lee 		memcpy(&xa->v6, &in6->sin6_addr, sizeof(xa->v6));
103b07b2f5cSHuie-Ying Lee #ifdef HAVE_STRUCT_SOCKADDR_IN6_SIN6_SCOPE_ID
104b07b2f5cSHuie-Ying Lee 		xa->scope_id = in6->sin6_scope_id;
105b07b2f5cSHuie-Ying Lee #endif
106b07b2f5cSHuie-Ying Lee 		break;
107b07b2f5cSHuie-Ying Lee 	default:
108b07b2f5cSHuie-Ying Lee 		return -1;
109b07b2f5cSHuie-Ying Lee 	}
110b07b2f5cSHuie-Ying Lee 
111b07b2f5cSHuie-Ying Lee 	return 0;
112b07b2f5cSHuie-Ying Lee }
113b07b2f5cSHuie-Ying Lee 
114b07b2f5cSHuie-Ying Lee /*
115b07b2f5cSHuie-Ying Lee  * Calculate a netmask of length 'l' for address family 'af' and
116b07b2f5cSHuie-Ying Lee  * store it in 'n'.
117b07b2f5cSHuie-Ying Lee  * Returns 0 on success, -1 on failure.
118b07b2f5cSHuie-Ying Lee  */
119b07b2f5cSHuie-Ying Lee static int
addr_netmask(int af,u_int l,struct xaddr * n)120b07b2f5cSHuie-Ying Lee addr_netmask(int af, u_int l, struct xaddr *n)
121b07b2f5cSHuie-Ying Lee {
122b07b2f5cSHuie-Ying Lee 	int i;
123b07b2f5cSHuie-Ying Lee 
124b07b2f5cSHuie-Ying Lee 	if (masklen_valid(af, l) != 0 || n == NULL)
125b07b2f5cSHuie-Ying Lee 		return -1;
126b07b2f5cSHuie-Ying Lee 
127b07b2f5cSHuie-Ying Lee 	memset(n, '\0', sizeof(*n));
128b07b2f5cSHuie-Ying Lee 	switch (af) {
129b07b2f5cSHuie-Ying Lee 	case AF_INET:
130b07b2f5cSHuie-Ying Lee 		n->af = AF_INET;
131b07b2f5cSHuie-Ying Lee 		n->v4.s_addr = htonl((0xffffffff << (32 - l)) & 0xffffffff);
132b07b2f5cSHuie-Ying Lee 		return 0;
133b07b2f5cSHuie-Ying Lee 	case AF_INET6:
134b07b2f5cSHuie-Ying Lee 		n->af = AF_INET6;
135b07b2f5cSHuie-Ying Lee 		for (i = 0; i < 4 && l >= 32; i++, l -= 32)
136b07b2f5cSHuie-Ying Lee 			n->addr32[i] = 0xffffffffU;
137b07b2f5cSHuie-Ying Lee 		if (i < 4 && l != 0)
138b07b2f5cSHuie-Ying Lee 			n->addr32[i] = htonl((0xffffffff << (32 - l)) &
139b07b2f5cSHuie-Ying Lee 			    0xffffffff);
140b07b2f5cSHuie-Ying Lee 		return 0;
141b07b2f5cSHuie-Ying Lee 	default:
142b07b2f5cSHuie-Ying Lee 		return -1;
143b07b2f5cSHuie-Ying Lee 	}
144b07b2f5cSHuie-Ying Lee }
145b07b2f5cSHuie-Ying Lee 
146b07b2f5cSHuie-Ying Lee /*
147b07b2f5cSHuie-Ying Lee  * Perform logical AND of addresses 'a' and 'b', storing result in 'dst'.
148b07b2f5cSHuie-Ying Lee  * Returns 0 on success, -1 on failure.
149b07b2f5cSHuie-Ying Lee  */
150b07b2f5cSHuie-Ying Lee static int
addr_and(struct xaddr * dst,const struct xaddr * a,const struct xaddr * b)151b07b2f5cSHuie-Ying Lee addr_and(struct xaddr *dst, const struct xaddr *a, const struct xaddr *b)
152b07b2f5cSHuie-Ying Lee {
153b07b2f5cSHuie-Ying Lee 	int i;
154b07b2f5cSHuie-Ying Lee 
155b07b2f5cSHuie-Ying Lee 	if (dst == NULL || a == NULL || b == NULL || a->af != b->af)
156b07b2f5cSHuie-Ying Lee 		return -1;
157b07b2f5cSHuie-Ying Lee 
158b07b2f5cSHuie-Ying Lee 	memcpy(dst, a, sizeof(*dst));
159b07b2f5cSHuie-Ying Lee 	switch (a->af) {
160b07b2f5cSHuie-Ying Lee 	case AF_INET:
161b07b2f5cSHuie-Ying Lee 		dst->v4.s_addr &= b->v4.s_addr;
162b07b2f5cSHuie-Ying Lee 		return 0;
163b07b2f5cSHuie-Ying Lee 	case AF_INET6:
164b07b2f5cSHuie-Ying Lee 		dst->scope_id = a->scope_id;
165b07b2f5cSHuie-Ying Lee 		for (i = 0; i < 4; i++)
166b07b2f5cSHuie-Ying Lee 			dst->addr32[i] &= b->addr32[i];
167b07b2f5cSHuie-Ying Lee 		return 0;
168b07b2f5cSHuie-Ying Lee 	default:
169b07b2f5cSHuie-Ying Lee 		return -1;
170b07b2f5cSHuie-Ying Lee 	}
171b07b2f5cSHuie-Ying Lee }
172b07b2f5cSHuie-Ying Lee 
173b07b2f5cSHuie-Ying Lee /*
174b07b2f5cSHuie-Ying Lee  * Compare addresses 'a' and 'b'
175b07b2f5cSHuie-Ying Lee  * Return 0 if addresses are identical, -1 if (a < b) or 1 if (a > b)
176b07b2f5cSHuie-Ying Lee  */
177b07b2f5cSHuie-Ying Lee static int
addr_cmp(const struct xaddr * a,const struct xaddr * b)178b07b2f5cSHuie-Ying Lee addr_cmp(const struct xaddr *a, const struct xaddr *b)
179b07b2f5cSHuie-Ying Lee {
180b07b2f5cSHuie-Ying Lee 	int i;
181b07b2f5cSHuie-Ying Lee 
182b07b2f5cSHuie-Ying Lee 	if (a->af != b->af)
183b07b2f5cSHuie-Ying Lee 		return a->af == AF_INET6 ? 1 : -1;
184b07b2f5cSHuie-Ying Lee 
185b07b2f5cSHuie-Ying Lee 	switch (a->af) {
186b07b2f5cSHuie-Ying Lee 	case AF_INET:
187b07b2f5cSHuie-Ying Lee 		if (a->v4.s_addr == b->v4.s_addr)
188b07b2f5cSHuie-Ying Lee 			return 0;
189b07b2f5cSHuie-Ying Lee 		return ntohl(a->v4.s_addr) > ntohl(b->v4.s_addr) ? 1 : -1;
190b07b2f5cSHuie-Ying Lee 	case AF_INET6:
191b07b2f5cSHuie-Ying Lee 		for (i = 0; i < 16; i++)
192b07b2f5cSHuie-Ying Lee 			if (a->addr8[i] - b->addr8[i] != 0)
193b07b2f5cSHuie-Ying Lee 				return a->addr8[i] > b->addr8[i] ? 1 : -1;
194b07b2f5cSHuie-Ying Lee 		if (a->scope_id == b->scope_id)
195b07b2f5cSHuie-Ying Lee 			return 0;
196b07b2f5cSHuie-Ying Lee 		return a->scope_id > b->scope_id ? 1 : -1;
197b07b2f5cSHuie-Ying Lee 	default:
198b07b2f5cSHuie-Ying Lee 		return -1;
199b07b2f5cSHuie-Ying Lee 	}
200b07b2f5cSHuie-Ying Lee }
201b07b2f5cSHuie-Ying Lee 
202b07b2f5cSHuie-Ying Lee /*
203b07b2f5cSHuie-Ying Lee  * Parse string address 'p' into 'n'
204b07b2f5cSHuie-Ying Lee  * Returns 0 on success, -1 on failure.
205b07b2f5cSHuie-Ying Lee  */
206b07b2f5cSHuie-Ying Lee static int
addr_pton(const char * p,struct xaddr * n)207b07b2f5cSHuie-Ying Lee addr_pton(const char *p, struct xaddr *n)
208b07b2f5cSHuie-Ying Lee {
209b07b2f5cSHuie-Ying Lee 	struct addrinfo hints, *ai;
210b07b2f5cSHuie-Ying Lee 
211b07b2f5cSHuie-Ying Lee 	memset(&hints, '\0', sizeof(hints));
212b07b2f5cSHuie-Ying Lee 	hints.ai_flags = AI_NUMERICHOST;
213b07b2f5cSHuie-Ying Lee 
214b07b2f5cSHuie-Ying Lee 	if (p == NULL || getaddrinfo(p, NULL, &hints, &ai) != 0)
215b07b2f5cSHuie-Ying Lee 		return -1;
216b07b2f5cSHuie-Ying Lee 
217b07b2f5cSHuie-Ying Lee 	if (ai == NULL || ai->ai_addr == NULL)
218b07b2f5cSHuie-Ying Lee 		return -1;
219b07b2f5cSHuie-Ying Lee 
220b07b2f5cSHuie-Ying Lee 	if (n != NULL &&
221b07b2f5cSHuie-Ying Lee 	    addr_sa_to_xaddr(ai->ai_addr, ai->ai_addrlen, n) == -1) {
222b07b2f5cSHuie-Ying Lee 		freeaddrinfo(ai);
223b07b2f5cSHuie-Ying Lee 		return -1;
224b07b2f5cSHuie-Ying Lee 	}
225b07b2f5cSHuie-Ying Lee 
226b07b2f5cSHuie-Ying Lee 	freeaddrinfo(ai);
227b07b2f5cSHuie-Ying Lee 	return 0;
228b07b2f5cSHuie-Ying Lee }
229b07b2f5cSHuie-Ying Lee 
230b07b2f5cSHuie-Ying Lee /*
231b07b2f5cSHuie-Ying Lee  * Perform bitwise negation of address
232b07b2f5cSHuie-Ying Lee  * Returns 0 on success, -1 on failure.
233b07b2f5cSHuie-Ying Lee  */
234b07b2f5cSHuie-Ying Lee static int
addr_invert(struct xaddr * n)235b07b2f5cSHuie-Ying Lee addr_invert(struct xaddr *n)
236b07b2f5cSHuie-Ying Lee {
237b07b2f5cSHuie-Ying Lee 	int i;
238b07b2f5cSHuie-Ying Lee 
239b07b2f5cSHuie-Ying Lee 	if (n == NULL)
240b07b2f5cSHuie-Ying Lee 		return (-1);
241b07b2f5cSHuie-Ying Lee 
242b07b2f5cSHuie-Ying Lee 	switch (n->af) {
243b07b2f5cSHuie-Ying Lee 	case AF_INET:
244b07b2f5cSHuie-Ying Lee 		n->v4.s_addr = ~n->v4.s_addr;
245b07b2f5cSHuie-Ying Lee 		return (0);
246b07b2f5cSHuie-Ying Lee 	case AF_INET6:
247b07b2f5cSHuie-Ying Lee 		for (i = 0; i < 4; i++)
248b07b2f5cSHuie-Ying Lee 			n->addr32[i] = ~n->addr32[i];
249b07b2f5cSHuie-Ying Lee 		return (0);
250b07b2f5cSHuie-Ying Lee 	default:
251b07b2f5cSHuie-Ying Lee 		return (-1);
252b07b2f5cSHuie-Ying Lee 	}
253b07b2f5cSHuie-Ying Lee }
254b07b2f5cSHuie-Ying Lee 
255b07b2f5cSHuie-Ying Lee /*
256b07b2f5cSHuie-Ying Lee  * Calculate a netmask of length 'l' for address family 'af' and
257b07b2f5cSHuie-Ying Lee  * store it in 'n'.
258b07b2f5cSHuie-Ying Lee  * Returns 0 on success, -1 on failure.
259b07b2f5cSHuie-Ying Lee  */
260b07b2f5cSHuie-Ying Lee static int
addr_hostmask(int af,u_int l,struct xaddr * n)261b07b2f5cSHuie-Ying Lee addr_hostmask(int af, u_int l, struct xaddr *n)
262b07b2f5cSHuie-Ying Lee {
263b07b2f5cSHuie-Ying Lee 	if (addr_netmask(af, l, n) == -1 || addr_invert(n) == -1)
264b07b2f5cSHuie-Ying Lee 		return (-1);
265b07b2f5cSHuie-Ying Lee 	return (0);
266b07b2f5cSHuie-Ying Lee }
267b07b2f5cSHuie-Ying Lee 
268b07b2f5cSHuie-Ying Lee /*
269b07b2f5cSHuie-Ying Lee  * Test whether address 'a' is all zeros (i.e. 0.0.0.0 or ::)
270b07b2f5cSHuie-Ying Lee  * Returns 0 on if address is all-zeros, -1 if not all zeros or on failure.
271b07b2f5cSHuie-Ying Lee  */
272b07b2f5cSHuie-Ying Lee static int
addr_is_all0s(const struct xaddr * a)273b07b2f5cSHuie-Ying Lee addr_is_all0s(const struct xaddr *a)
274b07b2f5cSHuie-Ying Lee {
275b07b2f5cSHuie-Ying Lee 	int i;
276b07b2f5cSHuie-Ying Lee 
277b07b2f5cSHuie-Ying Lee 	switch (a->af) {
278b07b2f5cSHuie-Ying Lee 	case AF_INET:
279b07b2f5cSHuie-Ying Lee 		return (a->v4.s_addr == 0 ? 0 : -1);
280b07b2f5cSHuie-Ying Lee 	case AF_INET6:;
281b07b2f5cSHuie-Ying Lee 		for (i = 0; i < 4; i++)
282b07b2f5cSHuie-Ying Lee 			if (a->addr32[i] != 0)
283b07b2f5cSHuie-Ying Lee 				return (-1);
284b07b2f5cSHuie-Ying Lee 		return (0);
285b07b2f5cSHuie-Ying Lee 	default:
286b07b2f5cSHuie-Ying Lee 		return (-1);
287b07b2f5cSHuie-Ying Lee 	}
288b07b2f5cSHuie-Ying Lee }
289b07b2f5cSHuie-Ying Lee 
290b07b2f5cSHuie-Ying Lee /*
291b07b2f5cSHuie-Ying Lee  * Test whether host portion of address 'a', as determined by 'masklen'
292b07b2f5cSHuie-Ying Lee  * is all zeros.
293b07b2f5cSHuie-Ying Lee  * Returns 0 on if host portion of address is all-zeros,
294b07b2f5cSHuie-Ying Lee  * -1 if not all zeros or on failure.
295b07b2f5cSHuie-Ying Lee  */
296b07b2f5cSHuie-Ying Lee static int
addr_host_is_all0s(const struct xaddr * a,u_int masklen)297b07b2f5cSHuie-Ying Lee addr_host_is_all0s(const struct xaddr *a, u_int masklen)
298b07b2f5cSHuie-Ying Lee {
299b07b2f5cSHuie-Ying Lee 	struct xaddr tmp_addr, tmp_mask, tmp_result;
300b07b2f5cSHuie-Ying Lee 
301b07b2f5cSHuie-Ying Lee 	memcpy(&tmp_addr, a, sizeof(tmp_addr));
302b07b2f5cSHuie-Ying Lee 	if (addr_hostmask(a->af, masklen, &tmp_mask) == -1)
303b07b2f5cSHuie-Ying Lee 		return (-1);
304b07b2f5cSHuie-Ying Lee 	if (addr_and(&tmp_result, &tmp_addr, &tmp_mask) == -1)
305b07b2f5cSHuie-Ying Lee 		return (-1);
306b07b2f5cSHuie-Ying Lee 	return (addr_is_all0s(&tmp_result));
307b07b2f5cSHuie-Ying Lee }
308b07b2f5cSHuie-Ying Lee 
309b07b2f5cSHuie-Ying Lee /*
310b07b2f5cSHuie-Ying Lee  * Parse a CIDR address (x.x.x.x/y or xxxx:yyyy::/z).
311b07b2f5cSHuie-Ying Lee  * Return -1 on parse error, -2 on inconsistency or 0 on success.
312b07b2f5cSHuie-Ying Lee  */
313b07b2f5cSHuie-Ying Lee static int
addr_pton_cidr(const char * p,struct xaddr * n,u_int * l)314b07b2f5cSHuie-Ying Lee addr_pton_cidr(const char *p, struct xaddr *n, u_int *l)
315b07b2f5cSHuie-Ying Lee {
316b07b2f5cSHuie-Ying Lee 	struct xaddr tmp;
317b07b2f5cSHuie-Ying Lee 	long unsigned int masklen = 999;
318b07b2f5cSHuie-Ying Lee 	char addrbuf[64], *mp, *cp;
319b07b2f5cSHuie-Ying Lee 
320b07b2f5cSHuie-Ying Lee 	/* Don't modify argument */
321b07b2f5cSHuie-Ying Lee 	if (p == NULL || strlcpy(addrbuf, p, sizeof(addrbuf)) > sizeof(addrbuf))
322b07b2f5cSHuie-Ying Lee 		return -1;
323b07b2f5cSHuie-Ying Lee 
324b07b2f5cSHuie-Ying Lee 	if ((mp = strchr(addrbuf, '/')) != NULL) {
325b07b2f5cSHuie-Ying Lee 		*mp = '\0';
326b07b2f5cSHuie-Ying Lee 		mp++;
327b07b2f5cSHuie-Ying Lee 		masklen = strtoul(mp, &cp, 10);
328b07b2f5cSHuie-Ying Lee 		if (*mp == '\0' || *cp != '\0' || masklen > 128)
329b07b2f5cSHuie-Ying Lee 			return -1;
330b07b2f5cSHuie-Ying Lee 	}
331b07b2f5cSHuie-Ying Lee 
332b07b2f5cSHuie-Ying Lee 	if (addr_pton(addrbuf, &tmp) == -1)
333b07b2f5cSHuie-Ying Lee 		return -1;
334b07b2f5cSHuie-Ying Lee 
335b07b2f5cSHuie-Ying Lee 	if (mp == NULL)
336b07b2f5cSHuie-Ying Lee 		masklen = addr_unicast_masklen(tmp.af);
337b07b2f5cSHuie-Ying Lee 	if (masklen_valid(tmp.af, masklen) == -1)
338b07b2f5cSHuie-Ying Lee 		return -2;
339b07b2f5cSHuie-Ying Lee 	if (addr_host_is_all0s(&tmp, masklen) != 0)
340b07b2f5cSHuie-Ying Lee 		return -2;
341b07b2f5cSHuie-Ying Lee 
342b07b2f5cSHuie-Ying Lee 	if (n != NULL)
343b07b2f5cSHuie-Ying Lee 		memcpy(n, &tmp, sizeof(*n));
344b07b2f5cSHuie-Ying Lee 	if (l != NULL)
345b07b2f5cSHuie-Ying Lee 		*l = masklen;
346b07b2f5cSHuie-Ying Lee 
347b07b2f5cSHuie-Ying Lee 	return 0;
348b07b2f5cSHuie-Ying Lee }
349b07b2f5cSHuie-Ying Lee 
350b07b2f5cSHuie-Ying Lee static int
addr_netmatch(const struct xaddr * host,const struct xaddr * net,u_int masklen)351b07b2f5cSHuie-Ying Lee addr_netmatch(const struct xaddr *host, const struct xaddr *net, u_int masklen)
352b07b2f5cSHuie-Ying Lee {
353b07b2f5cSHuie-Ying Lee 	struct xaddr tmp_mask, tmp_result;
354b07b2f5cSHuie-Ying Lee 
355b07b2f5cSHuie-Ying Lee 	if (host->af != net->af)
356b07b2f5cSHuie-Ying Lee 		return -1;
357b07b2f5cSHuie-Ying Lee 
358b07b2f5cSHuie-Ying Lee 	if (addr_netmask(host->af, masklen, &tmp_mask) == -1)
359b07b2f5cSHuie-Ying Lee 		return -1;
360b07b2f5cSHuie-Ying Lee 	if (addr_and(&tmp_result, host, &tmp_mask) == -1)
361b07b2f5cSHuie-Ying Lee 		return -1;
362b07b2f5cSHuie-Ying Lee 	return addr_cmp(&tmp_result, net);
363b07b2f5cSHuie-Ying Lee }
364b07b2f5cSHuie-Ying Lee 
365b07b2f5cSHuie-Ying Lee /*
366b07b2f5cSHuie-Ying Lee  * Match "addr" against list pattern list "_list", which may contain a
367b07b2f5cSHuie-Ying Lee  * mix of CIDR addresses and old-school wildcards.
368b07b2f5cSHuie-Ying Lee  *
369b07b2f5cSHuie-Ying Lee  * If addr is NULL, then no matching is performed, but _list is parsed
370b07b2f5cSHuie-Ying Lee  * and checked for well-formedness.
371b07b2f5cSHuie-Ying Lee  *
372b07b2f5cSHuie-Ying Lee  * Returns 1 on match found (never returned when addr == NULL).
373b07b2f5cSHuie-Ying Lee  * Returns 0 on if no match found, or no errors found when addr == NULL.
374b07b2f5cSHuie-Ying Lee  * Returns -1 on negated match found (never returned when addr == NULL).
375b07b2f5cSHuie-Ying Lee  * Returns -2 on invalid list entry.
376b07b2f5cSHuie-Ying Lee  */
377b07b2f5cSHuie-Ying Lee int
addr_match_list(const char * addr,const char * _list)378b07b2f5cSHuie-Ying Lee addr_match_list(const char *addr, const char *_list)
379b07b2f5cSHuie-Ying Lee {
380b07b2f5cSHuie-Ying Lee 	char *list, *cp, *o;
381b07b2f5cSHuie-Ying Lee 	struct xaddr try_addr, match_addr;
382b07b2f5cSHuie-Ying Lee 	u_int masklen, neg;
383b07b2f5cSHuie-Ying Lee 	int ret = 0, r;
384b07b2f5cSHuie-Ying Lee 
385b07b2f5cSHuie-Ying Lee 	if (addr != NULL && addr_pton(addr, &try_addr) != 0) {
386b07b2f5cSHuie-Ying Lee 		debug2("%s: couldn't parse address %.100s", __func__, addr);
387b07b2f5cSHuie-Ying Lee 		return 0;
388b07b2f5cSHuie-Ying Lee 	}
389b07b2f5cSHuie-Ying Lee 	if ((o = list = strdup(_list)) == NULL)
390b07b2f5cSHuie-Ying Lee 		return -1;
391b07b2f5cSHuie-Ying Lee 	while ((cp = strsep(&list, ",")) != NULL) {
392b07b2f5cSHuie-Ying Lee 		neg = *cp == '!';
393b07b2f5cSHuie-Ying Lee 		if (neg)
394b07b2f5cSHuie-Ying Lee 			cp++;
395b07b2f5cSHuie-Ying Lee 		if (*cp == '\0') {
396b07b2f5cSHuie-Ying Lee 			ret = -2;
397b07b2f5cSHuie-Ying Lee 			break;
398b07b2f5cSHuie-Ying Lee 		}
399b07b2f5cSHuie-Ying Lee 		/* Prefer CIDR address matching */
400b07b2f5cSHuie-Ying Lee 		r = addr_pton_cidr(cp, &match_addr, &masklen);
401b07b2f5cSHuie-Ying Lee 		if (r == -2) {
402b07b2f5cSHuie-Ying Lee 			error("Inconsistent mask length for "
403b07b2f5cSHuie-Ying Lee 			    "network \"%.100s\"", cp);
404b07b2f5cSHuie-Ying Lee 			ret = -2;
405b07b2f5cSHuie-Ying Lee 			break;
406b07b2f5cSHuie-Ying Lee 		} else if (r == 0) {
407b07b2f5cSHuie-Ying Lee 			if (addr != NULL && addr_netmatch(&try_addr,
408b07b2f5cSHuie-Ying Lee                            &match_addr, masklen) == 0) {
409b07b2f5cSHuie-Ying Lee  foundit:
410b07b2f5cSHuie-Ying Lee 				if (neg) {
411b07b2f5cSHuie-Ying Lee 					ret = -1;
412b07b2f5cSHuie-Ying Lee 					break;
413b07b2f5cSHuie-Ying Lee 				}
414b07b2f5cSHuie-Ying Lee 				ret = 1;
415b07b2f5cSHuie-Ying Lee 			}
416b07b2f5cSHuie-Ying Lee 			continue;
417b07b2f5cSHuie-Ying Lee 		} else {
418b07b2f5cSHuie-Ying Lee 			/* If CIDR parse failed, try wildcard string match */
419b07b2f5cSHuie-Ying Lee 			if (addr != NULL && match_pattern(addr, cp) == 1)
420b07b2f5cSHuie-Ying Lee 				goto foundit;
421b07b2f5cSHuie-Ying Lee 		}
422b07b2f5cSHuie-Ying Lee 	}
423b07b2f5cSHuie-Ying Lee 	xfree(o);
424b07b2f5cSHuie-Ying Lee 
425b07b2f5cSHuie-Ying Lee 	return ret;
426b07b2f5cSHuie-Ying Lee }
427