]> git.ipfire.org Git - thirdparty/squid.git/blob - src/ACLIP.cc
BUGFIX: max_user_ip was broken: initialising to -1 meant that the ACL appeared
[thirdparty/squid.git] / src / ACLIP.cc
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.
23 *
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.
28 *
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
36 #include "squid.h"
37 #include "ACLIP.h"
38 #include "ACLChecklist.h"
39 #include "MemBuf.h"
40 #include "wordlist.h"
41
42 void *
43 ACLIP::operator new (size_t byteCount)
44 {
45 fatal ("ACLIP::operator new: unused");
46 return (void *)1;
47 }
48
49 void
50 ACLIP::operator delete (void *address)
51 {
52 fatal ("ACLIP::operator delete: unused");
53 }
54
55 void
56 ACLIP::DumpIpListWalkee(acl_ip_data * const & ip, void *state)
57 {
58 MemBuf mb;
59 wordlist **W = static_cast<wordlist **>(state);
60 mb.init();
61 mb.Printf("%s", inet_ntoa(ip->addr1));
62
63 if (ip->addr2.s_addr != any_addr.s_addr)
64 mb.Printf("-%s", inet_ntoa(ip->addr2));
65
66 if (ip->mask.s_addr != no_addr.s_addr)
67 mb.Printf("/%s", inet_ntoa(ip->mask));
68
69 wordlistAdd(W, mb.buf);
70
71 mb.clean();
72 }
73
74 /*
75 * aclIpDataToStr - print/format an acl_ip_data structure for
76 * debugging output.
77 */
78 void
79 acl_ip_data::toStr(char *buf, int len) const
80 {
81 char b1[20];
82 char b2[20];
83 char b3[20];
84 snprintf(b1, 20, "%s", inet_ntoa(addr1));
85
86 if (addr2.s_addr != any_addr.s_addr)
87 snprintf(b2, 20, "-%s", inet_ntoa(addr2));
88 else
89 b2[0] = '\0';
90
91 if (mask.s_addr != no_addr.s_addr)
92 snprintf(b3, 20, "/%s", inet_ntoa(mask));
93 else
94 b3[0] = '\0';
95
96 snprintf(buf, len, "%s%s%s", b1, b2, b3);
97 }
98
99 /*
100 * aclIpAddrNetworkCompare - The guts of the comparison for IP ACLs.
101 * The first argument (a) is a "host" address, i.e. the IP address
102 * of a cache client. The second argument (b) is a "network" address
103 * that might have a subnet and/or range. We mask the host address
104 * bits with the network subnet mask.
105 */
106 /*
107 * aclIpAddrNetworkCompare - The comparison function used for ACL
108 * matching checks. The first argument (a) is a "host" address,
109 * i.e. the IP address of a cache client. The second argument (b)
110 * is an entry in some address-based access control element. This
111 * function is called via ACLIP::match() and the splay library.
112 */
113 int
114 aclIpAddrNetworkCompare(acl_ip_data * const &p, acl_ip_data * const &q)
115 {
116
117 struct IN_ADDR A = p->addr1;
118
119 const struct IN_ADDR B = q->addr1;
120
121 const struct IN_ADDR C = q->addr2;
122 A.s_addr &= q->mask.s_addr; /* apply netmask */
123
124 if (C.s_addr == 0) { /* single address check */
125
126 if (ntohl(A.s_addr) > ntohl(B.s_addr))
127 return 1;
128 else if (ntohl(A.s_addr) < ntohl(B.s_addr))
129 return -1;
130 else
131 return 0;
132 } else { /* range address check */
133
134 if (ntohl(A.s_addr) > ntohl(C.s_addr))
135 return 1;
136 else if (ntohl(A.s_addr) < ntohl(B.s_addr))
137 return -1;
138 else
139 return 0;
140 }
141 }
142
143
144 /*
145 * acl_ip_data::NetworkCompare - Compare two acl_ip_data entries. Strictly
146 * used by the splay insertion routine. It emits a warning if it
147 * detects a "collision" or overlap that would confuse the splay
148 * sorting algorithm. Much like aclDomainCompare.
149 */
150 int
151 acl_ip_data::NetworkCompare(acl_ip_data * const & a, acl_ip_data * const &b)
152 {
153 int ret;
154 ret = aclIpAddrNetworkCompare(b, a);
155
156 if (ret != 0) {
157 ret = aclIpAddrNetworkCompare(a, b);
158 }
159
160 if (ret == 0) {
161 char buf_n1[60];
162 char buf_n2[60];
163 char buf_a[60];
164 b->toStr(buf_n1, 60);
165 a->toStr(buf_n2, 60);
166 a->toStr(buf_a, 60);
167 /* TODO: this warning may display the wrong way around */
168 debug(28, 0) ("WARNING: '%s' is a subnetwork of "
169 "'%s'\n", buf_n1, buf_n2);
170 debug(28, 0) ("WARNING: because of this '%s' is ignored "
171 "to keep splay tree searching predictable\n", buf_a);
172 debug(28, 0) ("WARNING: You should probably remove '%s' "
173 "from the ACL named '%s'\n", buf_n1, AclMatchedName);
174 }
175
176 return ret;
177 }
178
179 /*
180 * Decode a ascii representation (asc) of a IP adress, and place
181 * adress and netmask information in addr and mask.
182 * This function should NOT be called if 'asc' is a hostname!
183 */
184 bool
185
186 acl_ip_data::DecodeMask(const char *asc, struct IN_ADDR *mask)
187 {
188 char junk;
189 int a1 = 0;
190
191 if (!asc || !*asc)
192 {
193 mask->s_addr = htonl(0xFFFFFFFFul);
194 return 1;
195 }
196
197 if (sscanf(asc, "%d%c", &a1, &junk) == 1 && a1 >= 0 && a1 < 33)
198 { /* a significant bits value for a mask */
199 mask->s_addr = a1 ? htonl(0xfffffffful << (32 - a1)) : 0;
200 return 1;
201 }
202
203 /* dotted notation */
204 if (safe_inet_addr(asc, mask))
205 return 1;
206
207 return 0;
208 }
209
210 #define SCAN_ACL1 "%[0123456789.]-%[0123456789.]/%[0123456789.]"
211 #define SCAN_ACL2 "%[0123456789.]-%[0123456789.]%c"
212 #define SCAN_ACL3 "%[0123456789.]/%[0123456789.]"
213
214 acl_ip_data *
215 acl_ip_data::FactoryParse(const char *t)
216 {
217 LOCAL_ARRAY(char, addr2, 256);
218 LOCAL_ARRAY(char, mask, 256);
219 acl_ip_data *r;
220 acl_ip_data **Q;
221 char **x;
222 char c;
223 debug(28, 5) ("aclParseIpData: %s\n", t);
224 acl_ip_data *q = new acl_ip_data;
225
226 if (!strcasecmp(t, "all")) {
227 q->addr1.s_addr = 0;
228 q->addr2.s_addr = 0;
229 q->mask.s_addr = 0;
230 return q;
231 }
232
233 LOCAL_ARRAY(char, addr1, 256);
234
235 if (sscanf(t, SCAN_ACL1, addr1, addr2, mask) == 3) {
236 (void) 0;
237 } else if (sscanf(t, SCAN_ACL2, addr1, addr2, &c) == 2) {
238 mask[0] = '\0';
239 } else if (sscanf(t, SCAN_ACL3, addr1, mask) == 2) {
240 addr2[0] = '\0';
241 } else if (sscanf(t, "%[^/]/%s", addr1, mask) == 2) {
242 addr2[0] = '\0';
243 } else if (sscanf(t, "%s", addr1) == 1) {
244 addr2[0] = '\0';
245 mask[0] = '\0';
246 }
247
248 if (!*addr2) {
249 /*
250 * Note, must use plain gethostbyname() here because at startup
251 * ipcache hasn't been initialized
252 */
253
254 struct hostent *hp;
255
256 if ((hp = gethostbyname(addr1)) == NULL) {
257 debug(28, 0) ("aclParseIpData: Bad host/IP: '%s'\n", t);
258 self_destruct();
259 }
260
261 Q = &q;
262
263 for (x = hp->h_addr_list; x != NULL && *x != NULL; x++) {
264 if ((r = *Q) == NULL)
265 r = *Q = new acl_ip_data;
266
267 xmemcpy(&r->addr1.s_addr, *x, sizeof(r->addr1.s_addr));
268
269 r->addr2.s_addr = 0;
270
271 if (!DecodeMask(mask, &r->mask)) {
272 debug(28, 0) ("aclParseIpData: unknown netmask '%s' in '%s'\n", mask, t);
273 delete r;
274 *Q = NULL;
275 self_destruct();
276 continue;
277 }
278
279
280 Q = &r->next;
281
282 debug(28, 3) ("%s --> %s\n", addr1, inet_ntoa(r->addr1));
283 }
284
285 if (*Q != NULL) {
286 debug(28, 0) ("aclParseIpData: Bad host/IP: '%s'\n", t);
287 self_destruct();
288 }
289
290 return q;
291 }
292
293 /* Decode addr1 */
294 if (!safe_inet_addr(addr1, &q->addr1)) {
295 debug(28, 0) ("aclParseIpData: unknown first address in '%s'\n", t);
296 delete q;
297 self_destruct();
298 return NULL;
299 }
300
301 /* Decode addr2 */
302 if (!safe_inet_addr(addr2, &q->addr2)) {
303 debug(28, 0) ("aclParseIpData: unknown second address in '%s'\n", t);
304 delete q;
305 self_destruct();
306 return NULL;
307 }
308
309 /* Decode mask */
310 if (!DecodeMask(mask, &q->mask)) {
311 debug(28, 0) ("aclParseIpData: unknown netmask '%s' in '%s'\n", mask, t);
312 delete q;
313 self_destruct();
314 return NULL;
315 }
316
317 if ((q->addr1.s_addr & q->mask.s_addr) != q->addr1.s_addr ||
318 (q->addr2.s_addr & q->mask.s_addr) != q->addr2.s_addr)
319 debug(28, 0) ("aclParseIpData: WARNING: Netmask masks away part of the specified IP in '%s'\n", t);
320
321 q->addr1.s_addr &= q->mask.s_addr;
322
323 q->addr2.s_addr &= q->mask.s_addr;
324
325 /* 1.2.3.4/255.255.255.0 --> 1.2.3.0 */
326 return q;
327 }
328
329 void
330 ACLIP::parse()
331 {
332 char *t = NULL;
333
334 while ((t = strtokFile())) {
335 acl_ip_data *q = acl_ip_data::FactoryParse(t);
336
337 while (q != NULL) {
338 data = data->insert(q, acl_ip_data::NetworkCompare);
339 q = q->next;
340 }
341 }
342 }
343
344 ACLIP::~ACLIP()
345 {
346 if (data)
347 data->destroy(IPSplay::DefaultFree);
348 }
349
350 wordlist *
351 ACLIP::dump() const
352 {
353 wordlist *w = NULL;
354 data->walk (DumpIpListWalkee, &w);
355 return w;
356 }
357
358 bool
359 ACLIP::empty () const
360 {
361 return data->empty();
362 }
363
364 int
365
366 ACLIP::match(struct IN_ADDR &clientip)
367 {
368 static acl_ip_data ClientAddress (any_addr, any_addr, no_addr, NULL);
369 /*
370 * aclIpAddrNetworkCompare() takes two acl_ip_data pointers as
371 * arguments, so we must create a fake one for the client's IP
372 * address, and use a /32 netmask. However, the current code
373 * probably only accesses the addr1 element of this argument,
374 * so it might be possible to leave addr2 and mask unset.
375 */
376 ClientAddress.addr1 = clientip;
377 acl_ip_data *ClientAddressPointer = &ClientAddress;
378 data = data->splay(ClientAddressPointer, aclIpAddrNetworkCompare);
379 debug(28, 3) ("aclMatchIp: '%s' %s\n",
380 inet_ntoa(clientip), splayLastResult ? "NOT found" : "found");
381 return !splayLastResult;
382 }
383
384 acl_ip_data::acl_ip_data () :addr1(any_addr), addr2(any_addr), mask (any_addr), next (NULL) {}
385
386 acl_ip_data::acl_ip_data (struct IN_ADDR const &anAddress1, struct IN_ADDR const &anAddress2, struct IN_ADDR const &aMask, acl_ip_data *aNext) : addr1(anAddress1), addr2(anAddress2), mask(aMask), next(aNext){}