Ip.cc
Go to the documentation of this file.
1/*
2 * Copyright (C) 1996-2023 The Squid Software Foundation and contributors
3 *
4 * Squid software is distributed under GPLv2+ license and includes
5 * contributions from numerous individuals and organizations.
6 * Please see the COPYING and CONTRIBUTORS files for details.
7 */
8
9/* DEBUG: section 28 Access Control */
10
11#include "squid.h"
12#include "acl/Checklist.h"
13#include "acl/Ip.h"
14#include "cache_cf.h"
15#include "ConfigParser.h"
16#include "debug/Stream.h"
17#include "ip/tools.h"
18#include "MemBuf.h"
19#include "wordlist.h"
20
21void *
22ACLIP::operator new (size_t)
23{
24 fatal ("ACLIP::operator new: unused");
25 return (void *)1;
26}
27
28void
29ACLIP::operator delete (void *)
30{
31 fatal ("ACLIP::operator delete: unused");
32}
33
40void
41acl_ip_data::toStr(char *buf, int len) const
42{
43 char *b1 = buf;
44 char *b2 = nullptr;
45 char *b3 = nullptr;
46 int rlen = 0;
47
48 addr1.toStr(b1, len - rlen );
49 rlen = strlen(buf);
50 b2 = buf + rlen;
51
52 if (!addr2.isAnyAddr()) {
53 b2[0] = '-';
54 ++rlen;
55 addr2.toStr(&(b2[1]), len - rlen );
56 rlen = strlen(buf);
57 } else
58 b2[0] = '\0';
59
60 b3 = buf + rlen;
61
62 if (!mask.isNoAddr()) {
63 b3[0] = '/';
64 ++rlen;
65 int cidr = mask.cidr() - (addr1.isIPv4()?96:0);
66 snprintf(&(b3[1]), (len-rlen), "%u", (unsigned int)(cidr<0?0:cidr) );
67 } else
68 b3[0] = '\0';
69}
70
71SBuf
73{
74 const int bufsz = MAX_IPSTRLEN*2+6;
75 static char tmpbuf[ bufsz ];
76 toStr(tmpbuf,bufsz);
77 return SBuf(tmpbuf);
78}
79
80/*
81 * aclIpAddrNetworkCompare - The guts of the comparison for IP ACLs
82 * matching checks. The first argument (p) is a "host" address,
83 * i.e. the IP address of a cache client. The second argument (q)
84 * is an entry in some address-based access control element. This
85 * function is called via ACLIP::match() and the splay library.
86 */
87static int
89{
90 Ip::Address A = p->addr1;
91
92 /* apply netmask */
93 A.applyMask(q->mask);
94
95 debugs(28,9, "aclIpAddrNetworkCompare: compare: " << p->addr1 << "/" << q->mask << " (" << A << ") vs " <<
96 q->addr1 << "-" << q->addr2 << "/" << q->mask);
97
98 if (q->addr2.isAnyAddr()) { /* single address check */
99
100 return A.matchIPAddr( q->addr1 );
101
102 } else { /* range address check */
103
104 if ( (A >= q->addr1) && (A <= q->addr2) )
105 return 0; /* valid. inside range. */
106 else
107 return A.matchIPAddr( q->addr1 ); /* outside of range, 'less than' */
108 }
109}
110
111/*
112 * acl_ip_data::NetworkCompare - Compare two acl_ip_data entries. Strictly
113 * used by the splay insertion routine. It emits a warning if it
114 * detects a "collision" or overlap that would confuse the splay
115 * sorting algorithm. Much like aclDomainCompare.
116 * The first argument (p) is a "host" address, i.e. the IP address of a cache client.
117 * The second argument (b) is a "network" address that might have a subnet and/or range.
118 * We mask the host address bits with the network subnet mask.
119 */
120int
122{
123 int ret;
124 bool bina = true;
125 ret = aclIpAddrNetworkCompare(b, a);
126
127 if (ret != 0) {
128 bina = false;
129 ret = aclIpAddrNetworkCompare(a, b);
130 }
131
132 if (ret == 0) {
133 char buf_n1[3*(MAX_IPSTRLEN+1)];
134 char buf_n2[3*(MAX_IPSTRLEN+1)];
135 if (bina) {
136 b->toStr(buf_n1, 3*(MAX_IPSTRLEN+1));
137 a->toStr(buf_n2, 3*(MAX_IPSTRLEN+1));
138 } else {
139 a->toStr(buf_n1, 3*(MAX_IPSTRLEN+1));
140 b->toStr(buf_n2, 3*(MAX_IPSTRLEN+1));
141 }
142 debugs(28, DBG_CRITICAL, "WARNING: (" << (bina?'B':'A') << ") '" << buf_n1 << "' is a subnetwork of (" << (bina?'A':'B') << ") '" << buf_n2 << "'");
143 debugs(28, DBG_CRITICAL, "WARNING: because of this '" << (bina?buf_n2:buf_n1) << "' is ignored to keep splay tree searching predictable");
144 debugs(28, DBG_CRITICAL, "WARNING: You should probably remove '" << buf_n1 << "' from the ACL named '" << AclMatchedName << "'");
145 }
146
147 return ret;
148}
149
155bool
156acl_ip_data::DecodeMask(const char *asc, Ip::Address &mask, int ctype)
157{
158 char junk;
159 int a1 = 0;
160
161 /* default is a mask that doesn't change any IP */
162 mask.setNoAddr();
163
164 if (!asc || !*asc) {
165 return true;
166 }
167
168 /* An int mask 128, 32 */
169 if ((sscanf(asc, "%d%c", &a1, &junk)==1) &&
170 (a1 <= 128) && (a1 >= 0)
171 ) {
172 return mask.applyMask(a1, ctype);
173 }
174
175 /* dotted notation */
176 /* assignment returns true if asc contained an IP address as text */
177 if ((mask = asc)) {
178 /* HACK: IPv4 netmasks don't cleanly map to IPv6 masks. */
179 debugs(28, DBG_CRITICAL, "WARNING: Netmasks are deprecated. Please use CIDR masks instead.");
180 if (mask.isIPv4()) {
181 /* locate what CIDR mask was _probably_ meant to be in its native protocol format. */
182 /* this will completely crap out with a security fail-open if the admin is playing mask tricks */
183 /* however, that's their fault, and we do warn. see bug 2601 for the effects if we don't do this. */
184 unsigned int m = mask.cidr();
185 debugs(28, DBG_CRITICAL, "WARNING: IPv4 netmasks are particularly nasty when used to compare IPv6 to IPv4 ranges.");
186 debugs(28, DBG_CRITICAL, "WARNING: For now we will assume you meant to write /" << m);
187 /* reset the mask completely, and crop to the CIDR boundary back properly. */
188 mask.setNoAddr();
189 return mask.applyMask(m,AF_INET);
190 }
191 return true;
192 }
193
194 return false;
195}
196
197/* Handle either type of address, IPv6 will be discarded with a warning if disabled */
198#define SCAN_ACL1_6 "%[0123456789ABCDEFabcdef:]-%[0123456789ABCDEFabcdef:]/%[0123456789]"
199#define SCAN_ACL2_6 "%[0123456789ABCDEFabcdef:]-%[0123456789ABCDEFabcdef:]%c"
200#define SCAN_ACL3_6 "%[0123456789ABCDEFabcdef:]/%[0123456789]"
201#define SCAN_ACL4_6 "%[0123456789ABCDEFabcdef:]/%c"
202/* We DO need to know which is which though, for proper CIDR masking. */
203#define SCAN_ACL1_4 "%[0123456789.]-%[0123456789.]/%[0123456789.]"
204#define SCAN_ACL2_4 "%[0123456789.]-%[0123456789.]%c"
205#define SCAN_ACL3_4 "%[0123456789.]/%[0123456789.]"
206#define SCAN_ACL4_4 "%[0123456789.]/%c"
207
210{
211 LOCAL_ARRAY(char, addr1, 256);
212 LOCAL_ARRAY(char, addr2, 256);
213 LOCAL_ARRAY(char, mask, 256);
214 acl_ip_data *r = nullptr;
215 acl_ip_data **Q = nullptr;
216 Ip::Address temp;
217 char c;
218 unsigned int changed;
219 acl_ip_data *q = new acl_ip_data;
220 int iptype = AF_UNSPEC;
221
222 debugs(28, 5, "aclIpParseIpData: " << t);
223
224 /* Special ACL RHS "all" matches entire Internet */
225 if (strcmp(t, "all") == 0) {
226 debugs(28, 9, "aclIpParseIpData: magic 'all' found.");
227 q->addr1.setAnyAddr();
228 q->addr2.setEmpty();
229 q->mask.setAnyAddr();
230 return q;
231 }
232
233 /* Detect some old broken strings equivalent to 'all'.
234 * treat them nicely. But be loud until its fixed. */
235 if (strcmp(t, "0/0") == 0 || strcmp(t, "0.0.0.0/0") == 0 || strcmp(t, "0.0.0.0/0.0.0.0") == 0 ||
236 strcmp(t, "0.0.0.0-255.255.255.255") == 0 || strcmp(t, "0.0.0.0-0.0.0.0/0") == 0) {
237
238 debugs(28,DBG_CRITICAL, "ERROR: '" << t << "' needs to be replaced by the term 'all'.");
239 debugs(28,DBG_CRITICAL, "SECURITY NOTICE: Overriding config setting. Using 'all' instead.");
240 q->addr1.setAnyAddr();
241 q->addr2.setEmpty();
242 q->mask.setAnyAddr();
243 return q;
244 }
245
246 /* Special ACL RHS "ipv4" matches IPv4 Internet
247 * A nod to IANA; we include the entire class space in case
248 * they manage to find a way to recover and use it */
249 if (strcmp(t, "ipv4") == 0) {
250 q->mask.setNoAddr();
251 q->mask.applyMask(0, AF_INET);
252 return q;
253 }
254
255 /* Special ACL RHS "ipv6" matches IPv6-Unicast Internet */
256 if (strcmp(t, "ipv6") == 0) {
257 debugs(28, 9, "aclIpParseIpData: magic 'ipv6' found.");
258 r = q; // save head of the list for result.
259
260 /* 0000::/4 is a mix of localhost and obsolete IPv4-mapping space. Not valid outside this host. */
261
262 /* Future global unicast space: 1000::/4 */
263 q->addr1 = "1000::";
264 q->mask.setNoAddr();
265 q->mask.applyMask(4, AF_INET6);
266
267 /* Current global unicast space: 2000::/4 = (2000::/4 - 3000::/4) */
268 q->next = new acl_ip_data;
269 q = q->next;
270 q->addr1 = "2000::";
271 q->mask.setNoAddr();
272 q->mask.applyMask(3, AF_INET6);
273
274 /* Future global unicast space: 4000::/2 = (4000::/4 - 7000::/4) */
275 q->next = new acl_ip_data;
276 q = q->next;
277 q->addr1 = "4000::";
278 q->mask.setNoAddr();
279 q->mask.applyMask(2, AF_INET6);
280
281 /* Future global unicast space: 8000::/2 = (8000::/4 - B000::/4) */
282 q->next = new acl_ip_data;
283 q = q->next;
284 q->addr1 = "8000::";
285 q->mask.setNoAddr();
286 q->mask.applyMask(2, AF_INET6);
287
288 /* Future global unicast space: C000::/3 = (C000::/4 - D000::/4) */
289 q->next = new acl_ip_data;
290 q = q->next;
291 q->addr1 = "C000::";
292 q->mask.setNoAddr();
293 q->mask.applyMask(3, AF_INET6);
294
295 /* Future global unicast space: E000::/4 */
296 q->next = new acl_ip_data;
297 q = q->next;
298 q->addr1 = "E000::";
299 q->mask.setNoAddr();
300 q->mask.applyMask(4, AF_INET6);
301
302 /* F000::/4 is mostly reserved non-unicast. With some exceptions ... */
303
304 /* RFC 4193 Unique-Local unicast space: FC00::/7 */
305 q->next = new acl_ip_data;
306 q = q->next;
307 q->addr1 = "FC00::";
308 q->mask.setNoAddr();
309 q->mask.applyMask(7, AF_INET6);
310
311 /* Link-Local unicast space: FE80::/10 */
312 q->next = new acl_ip_data;
313 q = q->next;
314 q->addr1 = "FE80::";
315 q->mask.setNoAddr();
316 q->mask.applyMask(10, AF_INET6);
317
318 return r;
319 }
320
321// IPv4
322 if (sscanf(t, SCAN_ACL1_4, addr1, addr2, mask) == 3) {
323 debugs(28, 9, "aclIpParseIpData: '" << t << "' matched: SCAN1-v4: " << SCAN_ACL1_4);
324 iptype=AF_INET;
325 } else if (sscanf(t, SCAN_ACL2_4, addr1, addr2, &c) >= 2) {
326 debugs(28, 9, "aclIpParseIpData: '" << t << "' matched: SCAN2-v4: " << SCAN_ACL2_4);
327 mask[0] = '\0';
328 iptype=AF_INET;
329 } else if (sscanf(t, SCAN_ACL3_4, addr1, mask) == 2) {
330 debugs(28, 9, "aclIpParseIpData: '" << t << "' matched: SCAN3-v4: " << SCAN_ACL3_4);
331 addr2[0] = '\0';
332 iptype=AF_INET;
333 } else if (sscanf(t, SCAN_ACL4_4, addr1,&c) == 2) {
334 debugs(28, 9, "aclIpParseIpData: '" << t << "' matched: SCAN4-v4: " << SCAN_ACL4_4);
335 addr2[0] = '\0';
336 mask[0] = '\0';
337 iptype=AF_INET;
338
339// IPv6
340 } else if (sscanf(t, SCAN_ACL1_6, addr1, addr2, mask) == 3) {
341 debugs(28, 9, "aclIpParseIpData: '" << t << "' matched: SCAN1-v6: " << SCAN_ACL1_6);
342 iptype=AF_INET6;
343 } else if (sscanf(t, SCAN_ACL2_6, addr1, addr2, &c) >= 2) {
344 debugs(28, 9, "aclIpParseIpData: '" << t << "' matched: SCAN2-v6: " << SCAN_ACL2_6);
345 mask[0] = '\0';
346 iptype=AF_INET6;
347 } else if (sscanf(t, SCAN_ACL3_6, addr1, mask) == 2) {
348 debugs(28, 9, "aclIpParseIpData: '" << t << "' matched: SCAN3-v6: " << SCAN_ACL3_6);
349 addr2[0] = '\0';
350 iptype=AF_INET6;
351 } else if (sscanf(t, SCAN_ACL4_6, addr1, mask) == 2) {
352 debugs(28, 9, "aclIpParseIpData: '" << t << "' matched: SCAN4-v6: " << SCAN_ACL4_6);
353 addr2[0] = '\0';
354 iptype=AF_INET6;
355
356// Neither
357 } else if (sscanf(t, "%[^/]/%s", addr1, mask) == 2) {
358 debugs(28, 9, "aclIpParseIpData: '" << t << "' matched: non-IP pattern: %[^/]/%s");
359 addr2[0] = '\0';
360 } else if (sscanf(t, "%s", addr1) == 1) {
361 /*
362 * Note, must use plain getaddrinfo() here because at startup
363 * ipcache hasn't been initialized
364 * TODO: offload this to one of the Ip::Address lookups.
365 */
366
367 debugs(28, 5, "aclIpParseIpData: Lookup Host/IP " << addr1);
368 struct addrinfo *hp = nullptr, *x = nullptr;
369 struct addrinfo hints;
370 Ip::Address *prev_addr = nullptr;
371
372 memset(&hints, 0, sizeof(struct addrinfo));
373
374 int errcode = getaddrinfo(addr1,nullptr,&hints,&hp);
375 if (hp == nullptr) {
376 delete q;
377 if (strcmp(addr1, "::1") == 0) {
378 debugs(28, DBG_IMPORTANT, "aclIpParseIpData: IPv6 has not been enabled in host DNS resolver.");
379 } else {
380 debugs(28, DBG_CRITICAL, "ERROR: aclIpParseIpData: Bad host/IP: '" << addr1 <<
381 "' in '" << t << "', flags=" << hints.ai_flags <<
382 " : (" << errcode << ") " << gai_strerror(errcode) );
384 }
385 return nullptr;
386 }
387
388 Q = &q;
389
390 for (x = hp; x != nullptr;) {
391 if ((r = *Q) == nullptr)
392 r = *Q = new acl_ip_data;
393
394 /* getaddrinfo given a host has a nasty tendency to return duplicate addr's */
395 /* BUT sorted fortunately, so we can drop most of them easily */
396 r->addr1 = *x;
397 x = x->ai_next;
398 if ( prev_addr && r->addr1 == *prev_addr) {
399 debugs(28, 3, "aclIpParseIpData: Duplicate host/IP: '" << r->addr1 << "' dropped.");
400 delete r;
401 *Q = nullptr;
402 continue;
403 } else
404 prev_addr = &r->addr1;
405
406 debugs(28, 3, "aclIpParseIpData: Located host/IP: '" << r->addr1 << "'");
407
408 r->addr2.setAnyAddr();
409 r->mask.setNoAddr();
410
411 Q = &r->next;
412
413 debugs(28, 3, "" << addr1 << " --> " << r->addr1 );
414 }
415
416 freeaddrinfo(hp);
417
418 if (*Q != nullptr) {
419 debugs(28, DBG_CRITICAL, "ERROR: aclIpParseIpData: Bad host/IP: '" << t << "'");
421 return nullptr;
422 }
423
424 return q;
425 }
426
427 /* ignore IPv6 addresses when built with IPv4-only */
428 if ( iptype == AF_INET6 && !Ip::EnableIpv6) {
429 debugs(28, DBG_IMPORTANT, "aclIpParseIpData: IPv6 has not been enabled.");
430 delete q;
431 return nullptr;
432 }
433
434 /* Decode addr1 */
435 if (!*addr1 || !(q->addr1 = addr1)) {
436 debugs(28, DBG_CRITICAL, "ERROR: aclIpParseIpData: unknown first address in '" << t << "'");
437 delete q;
439 return nullptr;
440 }
441
442 /* Decode addr2 */
443 if (!*addr2)
444 q->addr2.setAnyAddr();
445 else if (!(q->addr2=addr2) ) {
446 debugs(28, DBG_CRITICAL, "ERROR: aclIpParseIpData: unknown second address in '" << t << "'");
447 delete q;
449 return nullptr;
450 }
451
452 /* Decode mask (NULL or empty means a exact host mask) */
453 if (!DecodeMask(mask, q->mask, iptype)) {
454 debugs(28, DBG_CRITICAL, "ERROR: aclParseIpData: unknown netmask '" << mask << "' in '" << t << "'");
455 delete q;
457 return nullptr;
458 }
459
460 changed = 0;
461 changed += q->addr1.applyMask(q->mask);
462 changed += q->addr2.applyMask(q->mask);
463
464 if (changed)
465 debugs(28, DBG_CRITICAL, "WARNING: aclIpParseIpData: Netmask masks away part of the specified IP in '" << t << "'");
466
467 debugs(28,9, "Parsed: " << q->addr1 << "-" << q->addr2 << "/" << q->mask << "(/" << q->mask.cidr() <<")");
468
469 /* 1.2.3.4/255.255.255.0 --> 1.2.3.0 */
470 /* Same as IPv6 (not so trivial to depict) */
471 return q;
472}
473
474void
476{
477 if (data == nullptr)
478 data = new IPSplay();
479
480 while (char *t = ConfigParser::strtokFile()) {
482
483 while (q != nullptr) {
484 /* pop each result off the list and add it to the data tree individually */
485 acl_ip_data *next_node = q->next;
486 q->next = nullptr;
489 q = next_node;
490 }
491 }
492}
493
495{
496 if (data) {
497 data->destroy();
498 delete data;
499 }
500}
501
504 void operator() (acl_ip_data * const & ip) {
505 contents.push_back(ip->toSBuf());
506 }
507};
508
511{
512 IpAclDumpVisitor visitor;
513 data->visit(visitor);
514 return visitor.contents;
515}
516
517bool
519{
520 return data->empty();
521}
522
523int
524ACLIP::match(const Ip::Address &clientip)
525{
526 static acl_ip_data ClientAddress;
527 /*
528 * aclIpAddrNetworkCompare() takes two acl_ip_data pointers as
529 * arguments, so we must create a fake one for the client's IP
530 * address. Since we are scanning for a single IP mask and addr2
531 * MUST be set to empty.
532 */
533 ClientAddress.addr1 = clientip;
534 ClientAddress.addr2.setEmpty();
535 ClientAddress.mask.setEmpty();
536
537 const acl_ip_data * const * result = data->find(&ClientAddress, aclIpAddrNetworkCompare);
538 debugs(28, 3, "aclIpMatchIp: '" << clientip << "' " << (result ? "found" : "NOT found"));
539 return (result != nullptr);
540}
541
542acl_ip_data::acl_ip_data() :addr1(), addr2(), mask(), next (nullptr) {}
543
544acl_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) {}
545
#define SCAN_ACL2_6
Definition: Ip.cc:199
#define SCAN_ACL1_4
Definition: Ip.cc:203
#define SCAN_ACL2_4
Definition: Ip.cc:204
#define SCAN_ACL4_6
Definition: Ip.cc:201
#define SCAN_ACL3_4
Definition: Ip.cc:205
#define SCAN_ACL1_6
Definition: Ip.cc:198
#define SCAN_ACL4_4
Definition: Ip.cc:206
static int aclIpAddrNetworkCompare(acl_ip_data *const &p, acl_ip_data *const &q)
Definition: Ip.cc:88
#define SCAN_ACL3_6
Definition: Ip.cc:200
void self_destruct(void)
Definition: cache_cf.cc:277
SBufList dump() const override
Definition: Ip.cc:510
Splay< acl_ip_data * > IPSplay
Definition: Ip.h:53
void parse() override
parses node representation in squid.conf; dies on failures
Definition: Ip.cc:475
~ACLIP() override
Definition: Ip.cc:494
bool empty() const override
Definition: Ip.cc:518
IPSplay * data
Definition: Ip.h:65
int match(ACLChecklist *checklist) override=0
Matches the actual data in checklist against this ACL.
static char * strtokFile()
Definition: ConfigParser.cc:65
char * toStr(char *buf, const unsigned int blen, int force=AF_UNSPEC) const
Definition: Address.cc:792
void setEmpty()
Fast reset of the stored content to what would be after default constructor.
Definition: Address.cc:184
bool isIPv4() const
Definition: Address.cc:158
bool isNoAddr() const
Definition: Address.cc:284
bool isAnyAddr() const
Definition: Address.cc:170
void setNoAddr()
Definition: Address.cc:292
int applyMask(const Address &mask)
Definition: Address.cc:87
int cidr() const
Definition: Address.cc:44
void setAnyAddr()
NOTE: Does NOT clear the Port stored. Only the Address and Type.
Definition: Address.cc:177
Definition: SBuf.h:94
Value const * find(FindValue const &, int(*compare)(FindValue const &a, Value const &b)) const
bool empty() const
Definition: splay.h:76
void insert(Value const &, SPLAYCMP *compare)
Definition: splay.h:318
void visit(ValueVisitor &) const
left-to-right visit of all stored Values
void destroy(SPLAYFREE *=DefaultFree)
Definition: splay.h:365
Definition: Ip.h:18
Ip::Address mask
Definition: Ip.h:35
acl_ip_data()
Definition: Ip.cc:542
SBuf toSBuf() const
Definition: Ip.cc:72
static bool DecodeMask(const char *asc, Ip::Address &mask, int string_format_type)
Definition: Ip.cc:156
static int NetworkCompare(acl_ip_data *const &a, acl_ip_data *const &b)
Definition: Ip.cc:121
Ip::Address addr1
Definition: Ip.h:31
static acl_ip_data * FactoryParse(char const *)
Definition: Ip.cc:209
void toStr(char *buf, int len) const
Definition: Ip.cc:41
acl_ip_data * next
Definition: Ip.h:37
Ip::Address addr2
Definition: Ip.h:33
#define DBG_IMPORTANT
Definition: Stream.h:38
#define debugs(SECTION, LEVEL, CONTENT)
Definition: Stream.h:194
#define DBG_CRITICAL
Definition: Stream.h:37
void fatal(const char *message)
Definition: fatal.cc:28
const char * AclMatchedName
Definition: Acl.cc:29
#define MAX_IPSTRLEN
Length of buffer that needs to be allocated to old a null-terminated IP-string.
Definition: forward.h:25
static uint32 A
Definition: md4.c:43
std::list< SBuf > SBufList
Definition: forward.h:23
#define LOCAL_ARRAY(type, name, size)
Definition: squid.h:68
SBufList contents
Definition: Ip.cc:503
void operator()(acl_ip_data *const &ip)
Definition: Ip.cc:504
int EnableIpv6
Whether IPv6 is supported and type of support.
Definition: tools.h:25
int const char size_t
Definition: stub_liblog.cc:83

 

Introduction

Documentation

Support

Miscellaneous

Web Site Translations

Mirrors