]>
Commit | Line | Data |
---|---|---|
8000a965 | 1 | /* |
2 | * $Id$ | |
3 | * | |
4 | * DEBUG: section 28 Access Control | |
5 | * AUTHOR: Duane Wessels | |
6 | * | |
7 | * SQUID Web Proxy Cache http://www.squid-cache.org/ | |
8 | * ---------------------------------------------------------- | |
9 | * | |
10 | * Squid is the result of efforts by numerous individuals from | |
11 | * the Internet community; see the CONTRIBUTORS file for full | |
12 | * details. Many organizations have provided support for Squid's | |
13 | * development; see the SPONSORS file for full details. Squid is | |
14 | * Copyrighted (C) 2001 by the Regents of the University of | |
15 | * California; see the COPYRIGHT file for full details. Squid | |
16 | * incorporates software developed and/or copyrighted by other | |
17 | * sources; see the CREDITS file for full details. | |
18 | * | |
19 | * This program is free software; you can redistribute it and/or modify | |
20 | * it under the terms of the GNU General Public License as published by | |
21 | * the Free Software Foundation; either version 2 of the License, or | |
22 | * (at your option) any later version. | |
26ac0430 | 23 | * |
8000a965 | 24 | * This program is distributed in the hope that it will be useful, |
25 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
26 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
27 | * GNU General Public License for more details. | |
26ac0430 | 28 | * |
8000a965 | 29 | * You should have received a copy of the GNU General Public License |
30 | * along with this program; if not, write to the Free Software | |
31 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. | |
32 | * | |
33 | * Copyright (c) 2003, Robert Collins <robertc@squid-cache.org> | |
34 | */ | |
35 | ||
582c2af2 | 36 | #include "squid.h" |
3ad63615 AR |
37 | #include "acl/Ip.h" |
38 | #include "acl/Checklist.h" | |
582c2af2 | 39 | #include "Debug.h" |
055421ee | 40 | #include "ip/tools.h" |
0eb49b6d | 41 | #include "MemBuf.h" |
582c2af2 | 42 | #include "protos.h" |
d295d770 | 43 | #include "wordlist.h" |
8000a965 | 44 | |
45 | void * | |
46 | ACLIP::operator new (size_t byteCount) | |
47 | { | |
48 | fatal ("ACLIP::operator new: unused"); | |
49 | return (void *)1; | |
50 | } | |
51 | ||
52 | void | |
53 | ACLIP::operator delete (void *address) | |
54 | { | |
55 | fatal ("ACLIP::operator delete: unused"); | |
56 | } | |
57 | ||
60cad937 AJ |
58 | /** |
59 | * Writes an IP ACL data into a buffer, then copies the buffer into the wordlist given | |
60 | * | |
61 | \param ip ACL data structure to display | |
62 | \param state wordlist structure which is being generated | |
63 | */ | |
8000a965 | 64 | void |
65 | ACLIP::DumpIpListWalkee(acl_ip_data * const & ip, void *state) | |
66 | { | |
60cad937 | 67 | char tmpbuf[ ((MAX_IPSTRLEN*2)+6) ]; // space for 2 IPs and a CIDR mask(3) and seperators(3). |
8000a965 | 68 | MemBuf mb; |
69 | wordlist **W = static_cast<wordlist **>(state); | |
60cad937 | 70 | tmpbuf[0] = '\0'; |
62e76326 | 71 | |
60cad937 AJ |
72 | mb.init(); |
73 | assert(mb.max_capacity > 0 && 1==1 ); | |
62e76326 | 74 | |
60cad937 AJ |
75 | ip->toStr(tmpbuf, sizeof(tmpbuf) ); |
76 | assert(mb.max_capacity > 0 && 2==2 ); | |
77 | mb.append(tmpbuf, strlen(tmpbuf) ); | |
78 | assert(mb.max_capacity > 0 && 3==3); | |
8000a965 | 79 | wordlistAdd(W, mb.buf); |
2fe7eff9 | 80 | mb.clean(); |
8000a965 | 81 | } |
82 | ||
60cad937 AJ |
83 | /** |
84 | * print/format an acl_ip_data structure for debugging output. | |
85 | * | |
86 | \param buf string buffer to write to | |
87 | \param len size of the buffer available | |
8000a965 | 88 | */ |
89 | void | |
90 | acl_ip_data::toStr(char *buf, int len) const | |
91 | { | |
cc192b50 | 92 | char *b1 = buf; |
93 | char *b2 = NULL; | |
94 | char *b3 = NULL; | |
95 | int rlen = 0; | |
62e76326 | 96 | |
cc192b50 | 97 | addr1.NtoA(b1, len - rlen ); |
98 | rlen = strlen(buf); | |
99 | b2 = buf + rlen; | |
100 | ||
60cad937 | 101 | if (!addr2.IsAnyAddr()) { |
26ac0430 | 102 | b2[0] = '-'; |
742a021b | 103 | ++rlen; |
cc192b50 | 104 | addr2.NtoA(&(b2[1]), len - rlen ); |
105 | rlen = strlen(buf); | |
26ac0430 | 106 | } else |
62e76326 | 107 | b2[0] = '\0'; |
108 | ||
cc192b50 | 109 | b3 = buf + rlen; |
110 | ||
60cad937 | 111 | if (!mask.IsNoAddr()) { |
26ac0430 | 112 | b3[0] = '/'; |
742a021b | 113 | ++rlen; |
7764e92d AJ |
114 | int cidr = mask.GetCIDR() - (addr1.IsIPv4()?96:0); |
115 | snprintf(&(b3[1]), (len-rlen), "%u", (unsigned int)(cidr<0?0:cidr) ); | |
26ac0430 | 116 | } else |
62e76326 | 117 | b3[0] = '\0'; |
8000a965 | 118 | } |
119 | ||
120 | /* | |
077fe581 | 121 | * aclIpAddrNetworkCompare - The guts of the comparison for IP ACLs |
122 | * matching checks. The first argument (p) is a "host" address, | |
123 | * i.e. the IP address of a cache client. The second argument (q) | |
8000a965 | 124 | * is an entry in some address-based access control element. This |
125 | * function is called via ACLIP::match() and the splay library. | |
126 | */ | |
127 | int | |
128 | aclIpAddrNetworkCompare(acl_ip_data * const &p, acl_ip_data * const &q) | |
129 | { | |
b7ac5457 | 130 | Ip::Address A = p->addr1; |
62e76326 | 131 | |
cc192b50 | 132 | /* apply netmask */ |
133 | A.ApplyMask(q->mask); | |
62e76326 | 134 | |
8378b076 | 135 | debugs(28,9, "aclIpAddrNetworkCompare: compare: " << p->addr1 << "/" << q->mask << " (" << A << ") vs " << |
af6a12ee | 136 | q->addr1 << "-" << q->addr2 << "/" << q->mask); |
8378b076 | 137 | |
cc192b50 | 138 | if (q->addr2.IsAnyAddr()) { /* single address check */ |
62e76326 | 139 | |
cc192b50 | 140 | return A.matchIPAddr( q->addr1 ); |
62e76326 | 141 | |
cc192b50 | 142 | } else { /* range address check */ |
62e76326 | 143 | |
cc192b50 | 144 | if ( (A >= q->addr1) && (A <= q->addr2) ) |
145 | return 0; /* valid. inside range. */ | |
62e76326 | 146 | else |
cc192b50 | 147 | return A.matchIPAddr( q->addr1 ); /* outside of range, 'less than' */ |
8000a965 | 148 | } |
149 | } | |
150 | ||
151 | ||
152 | /* | |
153 | * acl_ip_data::NetworkCompare - Compare two acl_ip_data entries. Strictly | |
154 | * used by the splay insertion routine. It emits a warning if it | |
155 | * detects a "collision" or overlap that would confuse the splay | |
156 | * sorting algorithm. Much like aclDomainCompare. | |
077fe581 | 157 | * The first argument (p) is a "host" address, i.e. the IP address of a cache client. |
158 | * The second argument (b) is a "network" address that might have a subnet and/or range. | |
159 | * We mask the host address bits with the network subnet mask. | |
8000a965 | 160 | */ |
161 | int | |
162 | acl_ip_data::NetworkCompare(acl_ip_data * const & a, acl_ip_data * const &b) | |
163 | { | |
164 | int ret; | |
cc192b50 | 165 | bool bina = true; |
8000a965 | 166 | ret = aclIpAddrNetworkCompare(b, a); |
62e76326 | 167 | |
8000a965 | 168 | if (ret != 0) { |
cc192b50 | 169 | bina = false; |
62e76326 | 170 | ret = aclIpAddrNetworkCompare(a, b); |
8000a965 | 171 | } |
62e76326 | 172 | |
8000a965 | 173 | if (ret == 0) { |
cc192b50 | 174 | char buf_n1[3*(MAX_IPSTRLEN+1)]; |
175 | char buf_n2[3*(MAX_IPSTRLEN+1)]; | |
26ac0430 | 176 | if (bina) { |
cc192b50 | 177 | b->toStr(buf_n1, 3*(MAX_IPSTRLEN+1)); |
178 | a->toStr(buf_n2, 3*(MAX_IPSTRLEN+1)); | |
179 | } else { | |
180 | a->toStr(buf_n1, 3*(MAX_IPSTRLEN+1)); | |
181 | b->toStr(buf_n2, 3*(MAX_IPSTRLEN+1)); | |
182 | } | |
fa84c01d FC |
183 | debugs(28, DBG_CRITICAL, "WARNING: (" << (bina?'B':'A') << ") '" << buf_n1 << "' is a subnetwork of (" << (bina?'A':'B') << ") '" << buf_n2 << "'"); |
184 | debugs(28, DBG_CRITICAL, "WARNING: because of this '" << (bina?buf_n2:buf_n1) << "' is ignored to keep splay tree searching predictable"); | |
185 | debugs(28, DBG_CRITICAL, "WARNING: You should probably remove '" << buf_n1 << "' from the ACL named '" << AclMatchedName << "'"); | |
8000a965 | 186 | } |
62e76326 | 187 | |
8000a965 | 188 | return ret; |
189 | } | |
190 | ||
a7b15245 | 191 | /** |
077fe581 | 192 | * Decode an ascii representation (asc) of a IP netmask address or CIDR, |
193 | * and place resulting information in mask. | |
8000a965 | 194 | * This function should NOT be called if 'asc' is a hostname! |
195 | */ | |
196 | bool | |
b7ac5457 | 197 | acl_ip_data::DecodeMask(const char *asc, Ip::Address &mask, int ctype) |
8000a965 | 198 | { |
78d7484b | 199 | char junk; |
200 | int a1 = 0; | |
8000a965 | 201 | |
cc192b50 | 202 | /* default is a mask that doesn't change any IP */ |
203 | mask.SetNoAddr(); | |
204 | ||
26ac0430 | 205 | if (!asc || !*asc) { |
077fe581 | 206 | return true; |
8000a965 | 207 | } |
208 | ||
cc192b50 | 209 | /* An int mask 128, 32 */ |
210 | if ((sscanf(asc, "%d%c", &a1, &junk)==1) && | |
26ac0430 AJ |
211 | (a1 <= 128) && (a1 >= 0) |
212 | ) { | |
cc192b50 | 213 | return mask.ApplyMask(a1, ctype); |
78d7484b | 214 | } |
62e76326 | 215 | |
78d7484b | 216 | /* dotted notation */ |
cc192b50 | 217 | /* assignment returns true if asc contained an IP address as text */ |
dff9ee49 | 218 | if ((mask = asc)) { |
dff9ee49 | 219 | /* HACK: IPv4 netmasks don't cleanly map to IPv6 masks. */ |
4f875552 | 220 | debugs(28, DBG_CRITICAL, "WARNING: Netmasks are deprecated. Please use CIDR masks instead."); |
af6a12ee | 221 | if (mask.IsIPv4()) { |
dff9ee49 AJ |
222 | /* locate what CIDR mask was _probably_ meant to be in its native protocol format. */ |
223 | /* this will completely crap out with a security fail-open if the admin is playing mask tricks */ | |
224 | /* however, thats their fault, and we do warn. see bug 2601 for the effects if we don't do this. */ | |
225 | unsigned int m = mask.GetCIDR(); | |
226 | debugs(28, DBG_CRITICAL, "WARNING: IPv4 netmasks are particularly nasty when used to compare IPv6 to IPv4 ranges."); | |
4f875552 | 227 | debugs(28, DBG_CRITICAL, "WARNING: For now we will assume you meant to write /" << m); |
dff9ee49 | 228 | /* reset the mask completely, and crop to the CIDR boundary back properly. */ |
e1cc8aca | 229 | mask.SetNoAddr(); |
dff9ee49 AJ |
230 | return mask.ApplyMask(m,AF_INET); |
231 | } | |
077fe581 | 232 | return true; |
dff9ee49 | 233 | } |
62e76326 | 234 | |
077fe581 | 235 | return false; |
8000a965 | 236 | } |
237 | ||
cc192b50 | 238 | /* Handle either type of address, IPv6 will be discarded with a warning if disabled */ |
239 | #define SCAN_ACL1_6 "%[0123456789ABCDEFabcdef:]-%[0123456789ABCDEFabcdef:]/%[0123456789]" | |
240 | #define SCAN_ACL2_6 "%[0123456789ABCDEFabcdef:]-%[0123456789ABCDEFabcdef:]%c" | |
241 | #define SCAN_ACL3_6 "%[0123456789ABCDEFabcdef:]/%[0123456789]" | |
242 | #define SCAN_ACL4_6 "%[0123456789ABCDEFabcdef:]/%c" | |
243 | /* We DO need to know which is which though, for proper CIDR masking. */ | |
244 | #define SCAN_ACL1_4 "%[0123456789.]-%[0123456789.]/%[0123456789.]" | |
245 | #define SCAN_ACL2_4 "%[0123456789.]-%[0123456789.]%c" | |
246 | #define SCAN_ACL3_4 "%[0123456789.]/%[0123456789.]" | |
247 | #define SCAN_ACL4_4 "%[0123456789.]/%c" | |
8000a965 | 248 | |
249 | acl_ip_data * | |
250 | acl_ip_data::FactoryParse(const char *t) | |
251 | { | |
cc192b50 | 252 | LOCAL_ARRAY(char, addr1, 256); |
8000a965 | 253 | LOCAL_ARRAY(char, addr2, 256); |
254 | LOCAL_ARRAY(char, mask, 256); | |
cc192b50 | 255 | acl_ip_data *r = NULL; |
256 | acl_ip_data **Q = NULL; | |
b7ac5457 | 257 | Ip::Address temp; |
8000a965 | 258 | char c; |
cc192b50 | 259 | unsigned int changed; |
8000a965 | 260 | acl_ip_data *q = new acl_ip_data; |
cc192b50 | 261 | int iptype = AF_UNSPEC; |
262 | ||
263 | debugs(28, 5, "aclIpParseIpData: " << t); | |
62e76326 | 264 | |
cc192b50 | 265 | /* Special ACL RHS "all" matches entire Internet */ |
266 | if (strcasecmp(t, "all") == 0) { | |
2c1b9590 | 267 | debugs(28, 9, "aclIpParseIpData: magic 'all' found."); |
cc192b50 | 268 | q->addr1.SetAnyAddr(); |
269 | q->addr2.SetEmpty(); | |
270 | q->mask.SetAnyAddr(); | |
62e76326 | 271 | return q; |
8000a965 | 272 | } |
62e76326 | 273 | |
7764e92d AJ |
274 | /* Detect some old broken strings equivalent to 'all'. |
275 | * treat them nicely. But be loud until its fixed. */ | |
276 | if (strcasecmp(t, "0/0") == 0 || strcasecmp(t, "0.0.0.0/0") == 0 || strcasecmp(t, "0.0.0.0/0.0.0.0") == 0 || | |
e28f3e99 | 277 | strcasecmp(t, "0.0.0.0-255.255.255.255") == 0 || strcasecmp(t, "0.0.0.0-0.0.0.0/0") == 0) { |
7764e92d AJ |
278 | |
279 | debugs(28,DBG_CRITICAL, "ERROR: '" << t << "' needs to be replaced by the term 'all'."); | |
280 | debugs(28,DBG_CRITICAL, "SECURITY NOTICE: Overriding config setting. Using 'all' instead."); | |
281 | q->addr1.SetAnyAddr(); | |
282 | q->addr2.SetEmpty(); | |
283 | q->mask.SetAnyAddr(); | |
284 | return q; | |
285 | } | |
286 | ||
7764e92d AJ |
287 | /* Special ACL RHS "ipv4" matches IPv4 Internet |
288 | * A nod to IANA; we include the entire class space in case | |
289 | * they manage to find a way to recover and use it */ | |
290 | if (strcasecmp(t, "ipv4") == 0) { | |
291 | q->mask.SetNoAddr(); | |
292 | q->mask.ApplyMask(0, AF_INET); | |
293 | return q; | |
294 | } | |
295 | ||
cc192b50 | 296 | /* Special ACL RHS "ipv6" matches IPv6-Unicast Internet */ |
297 | if (strcasecmp(t, "ipv6") == 0) { | |
2c1b9590 | 298 | debugs(28, 9, "aclIpParseIpData: magic 'ipv6' found."); |
18a8e998 AJ |
299 | r = q; // save head of the list for result. |
300 | ||
301 | /* 0000::/4 is a mix of localhost and obsolete IPv4-mapping space. Not valid outside this host. */ | |
302 | ||
303 | /* Future global unicast space: 1000::/4 */ | |
304 | q->addr1 = "1000::"; | |
305 | q->mask.SetNoAddr(); | |
306 | q->mask.ApplyMask(4, AF_INET6); | |
307 | ||
308 | /* Current global unicast space: 2000::/4 = (2000::/4 - 3000::/4) */ | |
309 | q->next = new acl_ip_data; | |
310 | q = q->next; | |
311 | q->addr1 = "2000::"; | |
312 | q->mask.SetNoAddr(); | |
313 | q->mask.ApplyMask(3, AF_INET6); | |
314 | ||
315 | /* Future global unicast space: 4000::/2 = (4000::/4 - 7000::/4) */ | |
316 | q->next = new acl_ip_data; | |
317 | q = q->next; | |
318 | q->addr1 = "4000::"; | |
319 | q->mask.SetNoAddr(); | |
320 | q->mask.ApplyMask(2, AF_INET6); | |
321 | ||
322 | /* Future global unicast space: 8000::/2 = (8000::/4 - B000::/4) */ | |
323 | q->next = new acl_ip_data; | |
324 | q = q->next; | |
325 | q->addr1 = "8000::"; | |
326 | q->mask.SetNoAddr(); | |
327 | q->mask.ApplyMask(2, AF_INET6); | |
328 | ||
329 | /* Future global unicast space: C000::/3 = (C000::/4 - D000::/4) */ | |
330 | q->next = new acl_ip_data; | |
331 | q = q->next; | |
332 | q->addr1 = "C000::"; | |
333 | q->mask.SetNoAddr(); | |
334 | q->mask.ApplyMask(3, AF_INET6); | |
335 | ||
336 | /* Future global unicast space: E000::/4 */ | |
337 | q->next = new acl_ip_data; | |
338 | q = q->next; | |
339 | q->addr1 = "E000::"; | |
340 | q->mask.SetNoAddr(); | |
341 | q->mask.ApplyMask(4, AF_INET6); | |
342 | ||
343 | /* F000::/4 is mostly reserved non-unicast. With some exceptions ... */ | |
344 | ||
345 | /* RFC 4193 Unique-Local unicast space: FC00::/7 */ | |
346 | q->next = new acl_ip_data; | |
347 | q = q->next; | |
348 | q->addr1 = "FC00::"; | |
349 | q->mask.SetNoAddr(); | |
350 | q->mask.ApplyMask(7, AF_INET6); | |
351 | ||
352 | /* Link-Local unicast space: FE80::/10 */ | |
353 | q->next = new acl_ip_data; | |
354 | q = q->next; | |
355 | q->addr1 = "FE80::"; | |
356 | q->mask.SetNoAddr(); | |
357 | q->mask.ApplyMask(10, AF_INET6); | |
358 | ||
359 | return r; | |
cc192b50 | 360 | } |
62e76326 | 361 | |
cc192b50 | 362 | // IPv4 |
363 | if (sscanf(t, SCAN_ACL1_4, addr1, addr2, mask) == 3) { | |
2c1b9590 | 364 | debugs(28, 9, "aclIpParseIpData: '" << t << "' matched: SCAN1-v4: " << SCAN_ACL1_4); |
cc192b50 | 365 | iptype=AF_INET; |
366 | } else if (sscanf(t, SCAN_ACL2_4, addr1, addr2, &c) >= 2) { | |
2c1b9590 | 367 | debugs(28, 9, "aclIpParseIpData: '" << t << "' matched: SCAN2-v4: " << SCAN_ACL2_4); |
62e76326 | 368 | mask[0] = '\0'; |
cc192b50 | 369 | iptype=AF_INET; |
370 | } else if (sscanf(t, SCAN_ACL3_4, addr1, mask) == 2) { | |
2c1b9590 | 371 | debugs(28, 9, "aclIpParseIpData: '" << t << "' matched: SCAN3-v4: " << SCAN_ACL3_4); |
62e76326 | 372 | addr2[0] = '\0'; |
cc192b50 | 373 | iptype=AF_INET; |
374 | } else if (sscanf(t, SCAN_ACL4_4, addr1,&c) == 2) { | |
2c1b9590 | 375 | debugs(28, 9, "aclIpParseIpData: '" << t << "' matched: SCAN4-v4: " << SCAN_ACL4_4); |
cc192b50 | 376 | addr2[0] = '\0'; |
377 | mask[0] = '\0'; | |
378 | iptype=AF_INET; | |
379 | ||
380 | // IPv6 | |
381 | } else if (sscanf(t, SCAN_ACL1_6, addr1, addr2, mask) == 3) { | |
d9a200ce | 382 | debugs(28, 9, "aclIpParseIpData: '" << t << "' matched: SCAN1-v6: " << SCAN_ACL1_6); |
cc192b50 | 383 | iptype=AF_INET6; |
384 | } else if (sscanf(t, SCAN_ACL2_6, addr1, addr2, &c) >= 2) { | |
d9a200ce | 385 | debugs(28, 9, "aclIpParseIpData: '" << t << "' matched: SCAN2-v6: " << SCAN_ACL2_6); |
cc192b50 | 386 | mask[0] = '\0'; |
387 | iptype=AF_INET6; | |
388 | } else if (sscanf(t, SCAN_ACL3_6, addr1, mask) == 2) { | |
d9a200ce | 389 | debugs(28, 9, "aclIpParseIpData: '" << t << "' matched: SCAN3-v6: " << SCAN_ACL3_6); |
cc192b50 | 390 | addr2[0] = '\0'; |
391 | iptype=AF_INET6; | |
392 | } else if (sscanf(t, SCAN_ACL4_6, addr1, mask) == 2) { | |
d9a200ce | 393 | debugs(28, 9, "aclIpParseIpData: '" << t << "' matched: SCAN4-v6: " << SCAN_ACL4_6); |
cc192b50 | 394 | addr2[0] = '\0'; |
395 | iptype=AF_INET6; | |
396 | ||
397 | // Neither | |
8000a965 | 398 | } else if (sscanf(t, "%[^/]/%s", addr1, mask) == 2) { |
2c1b9590 | 399 | debugs(28, 9, "aclIpParseIpData: '" << t << "' matched: non-IP pattern: %[^/]/%s"); |
62e76326 | 400 | addr2[0] = '\0'; |
8000a965 | 401 | } else if (sscanf(t, "%s", addr1) == 1) { |
62e76326 | 402 | /* |
27bc2077 | 403 | * Note, must use plain getaddrinfo() here because at startup |
62e76326 | 404 | * ipcache hasn't been initialized |
b7ac5457 | 405 | * TODO: offload this to one of the Ip::Address lookups. |
62e76326 | 406 | */ |
407 | ||
cc192b50 | 408 | debugs(28, 5, "aclIpParseIpData: Lookup Host/IP " << addr1); |
409 | struct addrinfo *hp = NULL, *x = NULL; | |
410 | struct addrinfo hints; | |
b7ac5457 | 411 | Ip::Address *prev_addr = NULL; |
cc192b50 | 412 | |
413 | memset(&hints, 0, sizeof(struct addrinfo)); | |
414 | ||
26ac0430 | 415 | if ( iptype != AF_UNSPEC ) { |
cc192b50 | 416 | hints.ai_flags |= AI_NUMERICHOST; |
417 | } | |
418 | ||
055421ee AJ |
419 | #if 0 |
420 | if (Ip::EnableIpv6&IPV6_SPECIAL_V4MAPPING) | |
421 | hints.ai_flags |= AI_V4MAPPED | AI_ALL; | |
cc192b50 | 422 | #endif |
62e76326 | 423 | |
27bc2077 | 424 | int errcode = getaddrinfo(addr1,NULL,&hints,&hp); |
cc192b50 | 425 | if (hp == NULL) { |
fa84c01d | 426 | debugs(28, DBG_CRITICAL, "aclIpParseIpData: Bad host/IP: '" << addr1 << |
26ac0430 | 427 | "' in '" << t << "', flags=" << hints.ai_flags << |
27bc2077 | 428 | " : (" << errcode << ") " << gai_strerror(errcode) ); |
4b0f5de8 | 429 | self_destruct(); |
cc192b50 | 430 | return NULL; |
62e76326 | 431 | } |
432 | ||
433 | Q = &q; | |
434 | ||
cc192b50 | 435 | for (x = hp; x != NULL;) { |
62e76326 | 436 | if ((r = *Q) == NULL) |
437 | r = *Q = new acl_ip_data; | |
438 | ||
cc192b50 | 439 | /* getaddrinfo given a host has a nasty tendency to return duplicate addr's */ |
440 | /* BUT sorted fortunately, so we can drop most of them easily */ | |
441 | r->addr1 = *x; | |
442 | x = x->ai_next; | |
26ac0430 | 443 | if ( prev_addr && r->addr1 == *prev_addr) { |
cc192b50 | 444 | debugs(28, 3, "aclIpParseIpData: Duplicate host/IP: '" << r->addr1 << "' dropped."); |
445 | delete r; | |
446 | *Q = NULL; | |
447 | continue; | |
26ac0430 | 448 | } else |
cc192b50 | 449 | prev_addr = &r->addr1; |
450 | ||
451 | debugs(28, 3, "aclIpParseIpData: Located host/IP: '" << r->addr1 << "'"); | |
62e76326 | 452 | |
cc192b50 | 453 | r->addr2.SetAnyAddr(); |
454 | r->mask.SetNoAddr(); | |
62e76326 | 455 | |
456 | Q = &r->next; | |
457 | ||
cc192b50 | 458 | debugs(28, 3, "" << addr1 << " --> " << r->addr1 ); |
62e76326 | 459 | } |
460 | ||
78d7484b | 461 | if (*Q != NULL) { |
fa84c01d | 462 | debugs(28, DBG_CRITICAL, "aclIpParseIpData: Bad host/IP: '" << t << "'"); |
4b0f5de8 | 463 | self_destruct(); |
cc192b50 | 464 | return NULL; |
78d7484b | 465 | } |
466 | ||
27bc2077 | 467 | freeaddrinfo(hp); |
cc192b50 | 468 | |
62e76326 | 469 | return q; |
8000a965 | 470 | } |
62e76326 | 471 | |
cc192b50 | 472 | /* ignore IPv6 addresses when built with IPv4-only */ |
055421ee AJ |
473 | if ( iptype == AF_INET6 && !Ip::EnableIpv6) { |
474 | debugs(28, DBG_IMPORTANT, "aclIpParseIpData: IPv6 has not been enabled."); | |
cc192b50 | 475 | return NULL; |
476 | } | |
cc192b50 | 477 | |
8000a965 | 478 | /* Decode addr1 */ |
f8d0a1ef | 479 | if (!*addr1 || !(q->addr1 = addr1)) { |
fa84c01d | 480 | debugs(28, DBG_CRITICAL, "aclIpParseIpData: unknown first address in '" << t << "'"); |
62e76326 | 481 | delete q; |
4b0f5de8 | 482 | self_destruct(); |
62e76326 | 483 | return NULL; |
8000a965 | 484 | } |
62e76326 | 485 | |
8000a965 | 486 | /* Decode addr2 */ |
f8d0a1ef | 487 | if (!*addr2) |
26ac0430 | 488 | q->addr2.SetAnyAddr(); |
f8d0a1ef | 489 | else if (!(q->addr2=addr2) ) { |
fa84c01d | 490 | debugs(28, DBG_CRITICAL, "aclIpParseIpData: unknown second address in '" << t << "'"); |
62e76326 | 491 | delete q; |
94e13b6c | 492 | self_destruct(); |
62e76326 | 493 | return NULL; |
8000a965 | 494 | } |
62e76326 | 495 | |
2503e5e1 | 496 | /* Decode mask (NULL or empty means a exact host mask) */ |
cc192b50 | 497 | if (!DecodeMask(mask, q->mask, iptype)) { |
fa84c01d | 498 | debugs(28, DBG_CRITICAL, "aclParseIpData: unknown netmask '" << mask << "' in '" << t << "'"); |
62e76326 | 499 | delete q; |
4b0f5de8 | 500 | self_destruct(); |
62e76326 | 501 | return NULL; |
8000a965 | 502 | } |
62e76326 | 503 | |
cc192b50 | 504 | changed = 0; |
505 | changed += q->addr1.ApplyMask(q->mask); | |
506 | changed += q->addr2.ApplyMask(q->mask); | |
62e76326 | 507 | |
cc192b50 | 508 | if (changed) |
fa84c01d | 509 | debugs(28, DBG_CRITICAL, "aclIpParseIpData: WARNING: Netmask masks away part of the specified IP in '" << t << "'"); |
62e76326 | 510 | |
2c1b9590 AJ |
511 | debugs(28,9, HERE << "Parsed: " << q->addr1 << "-" << q->addr2 << "/" << q->mask << "(/" << q->mask.GetCIDR() <<")"); |
512 | ||
8000a965 | 513 | /* 1.2.3.4/255.255.255.0 --> 1.2.3.0 */ |
cc192b50 | 514 | /* Same as IPv6 (not so trivial to depict) */ |
8000a965 | 515 | return q; |
516 | } | |
62e76326 | 517 | |
8000a965 | 518 | void |
519 | ACLIP::parse() | |
520 | { | |
521 | char *t = NULL; | |
62e76326 | 522 | |
8000a965 | 523 | while ((t = strtokFile())) { |
62e76326 | 524 | acl_ip_data *q = acl_ip_data::FactoryParse(t); |
525 | ||
526 | while (q != NULL) { | |
18a8e998 | 527 | /* pop each result off the list and add it to the data tree individually */ |
e4ae841b | 528 | acl_ip_data *next_node = q->next; |
18a8e998 | 529 | q->next = NULL; |
62e76326 | 530 | data = data->insert(q, acl_ip_data::NetworkCompare); |
e4ae841b | 531 | q = next_node; |
62e76326 | 532 | } |
8000a965 | 533 | } |
534 | } | |
535 | ||
536 | ACLIP::~ACLIP() | |
537 | { | |
6aae37f6 | 538 | if (data) |
539 | data->destroy(IPSplay::DefaultFree); | |
8000a965 | 540 | } |
541 | ||
542 | wordlist * | |
543 | ACLIP::dump() const | |
544 | { | |
5aeabf95 | 545 | wordlist *w = NULL; |
8000a965 | 546 | data->walk (DumpIpListWalkee, &w); |
547 | return w; | |
548 | } | |
549 | ||
550 | bool | |
4b0f5de8 | 551 | ACLIP::empty () const |
8000a965 | 552 | { |
290eb6b9 | 553 | return data->empty(); |
8000a965 | 554 | } |
555 | ||
556 | int | |
b7ac5457 | 557 | ACLIP::match(Ip::Address &clientip) |
8000a965 | 558 | { |
cc192b50 | 559 | static acl_ip_data ClientAddress; |
8000a965 | 560 | /* |
561 | * aclIpAddrNetworkCompare() takes two acl_ip_data pointers as | |
562 | * arguments, so we must create a fake one for the client's IP | |
cc192b50 | 563 | * address. Since we are scanning for a single IP mask and addr2 |
564 | * MUST be set to empty. | |
8000a965 | 565 | */ |
566 | ClientAddress.addr1 = clientip; | |
cc192b50 | 567 | ClientAddress.addr2.SetEmpty(); |
568 | ClientAddress.mask.SetEmpty(); | |
569 | ||
570 | data = data->splay(&ClientAddress, aclIpAddrNetworkCompare); | |
571 | debugs(28, 3, "aclIpMatchIp: '" << clientip << "' " << (splayLastResult ? "NOT found" : "found")); | |
8000a965 | 572 | return !splayLastResult; |
573 | } | |
574 | ||
cc192b50 | 575 | acl_ip_data::acl_ip_data () :addr1(), addr2(), mask(), next (NULL) {} |
62e76326 | 576 | |
b7ac5457 | 577 | acl_ip_data::acl_ip_data (Ip::Address const &anAddress1, Ip::Address const &anAddress2, Ip::Address const &aMask, acl_ip_data *aNext) : addr1(anAddress1), addr2(anAddress2), mask(aMask), next(aNext) {} |