2
* Copyright (c) 1997, 1998, 1999, 2000, 2001, 2002, 2003
3
* Inferno Nettverk A/S, Norway. All rights reserved.
5
* Redistribution and use in source and binary forms, with or without
6
* modification, are permitted provided that the following conditions
8
* 1. The above copyright notice, this list of conditions and the following
9
* disclaimer must appear in all copies of the software, derivative works
10
* or modified versions, and any portions thereof, aswell as in all
11
* supporting documentation.
12
* 2. All advertising materials mentioning features or use of this software
13
* must display the following acknowledgement:
14
* This product includes software developed by
15
* Inferno Nettverk A/S, Norway.
16
* 3. The name of the author may not be used to endorse or promote products
17
* derived from this software without specific prior written permission.
19
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
23
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
24
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
* Inferno Nettverk A/S requests users of this software to return to
32
* Software Distribution Coordinator or sdc@inet.no
33
* Inferno Nettverk A/S
39
* any improvements or extensions that they make and grant Inferno Nettverk A/S
40
* the rights to redistribute these changes.
46
static const char rcsid[] =
47
"$Id: addressmatch.c,v 1.8 2003/07/01 13:21:25 michaels Exp $";
52
addrisinlist __P((const struct in_addr *addr, const struct in_addr *mask,
53
const struct in_addr **list));
55
* Compares "addr" bitwise anded with "mask" against each element in
56
* "list" bitwise anded with "mask". "list" is NULL terminated.
58
* If "list" contains a element matching "addr" and "mask": true
63
addrareeq __P((const struct in_addr *addr, const struct in_addr *mask,
64
const struct in_addr *against));
66
* Compares "addr" bitwise anded with "mask" against "against" bitwise
69
* If "against" matches "addr" and "mask": true
74
hostisinlist __P((const char *host, const char **list));
76
* Compares "host" against each element in "list", which is NULL
78
* Note that if "host" starts with a dot, it will match "list" if the
79
* last part of "list" matches the part after the dot in "host".
81
* If "list" contains a element matching "host": true
86
hostareeq __P((const char *domain, const char *remotedomain));
88
* Compares the rulegiven domain "domain" against "remotedomain".
89
* Note that if "domain" starts with a dot, it will match
90
* "remotedomain" if the last part of "remotedomain" matches
91
* the part after the dot in "domain".
101
addressmatch(rule, address, protocol, alias)
102
const struct ruleaddress_t *rule;
103
const struct sockshost_t *address;
107
const char *function = "addressmatch()";
108
struct hostent *hostent;
110
int matched, doresolve;
111
char rstring[MAXRULEADDRSTRING], astring[MAXSOCKSHOSTSTRING];
113
slog(LOG_DEBUG, "%s: %s, %s, %s, %d",
115
ruleaddress2string(rule, rstring, sizeof(rstring)),
116
sockshost2string(address, astring, sizeof(astring)),
117
protocol2string(protocol),
120
/* test port first since we always have all info needed for that locally. */
123
ruleport = rule->port.tcp;
127
ruleport = rule->port.udp;
134
switch (rule->operator) {
139
if (address->port == ruleport)
144
if (address->port != ruleport)
149
if (ntohs(address->port) >= ntohs(ruleport))
154
if (ntohs(address->port) <= ntohs(ruleport))
159
if (ntohs(address->port) > ntohs(ruleport))
164
if (ntohs(address->port) < ntohs(ruleport))
169
if (ntohs(address->port) >= ntohs(ruleport)
170
&& ntohs(address->port) <= ntohs(rule->portend))
175
SERRX(rule->operator);
178
/* only needed for client really... */
179
switch (sockscf.resolveprotocol) {
180
case RESOLVEPROTOCOL_TCP:
181
case RESOLVEPROTOCOL_UDP:
185
case RESOLVEPROTOCOL_FAKE:
190
SERRX(sockscf.resolveprotocol);
194
* The hard work begins.
198
if (rule->atype == SOCKS_ADDR_IPV4 && address->atype == SOCKS_ADDR_DOMAIN) {
200
* match(rule.ipaddress, address.hostname)
201
* resolve address to ipaddress(es) and try to match each
202
* resolved IP address against rule.
203
* rule isin address->ipaddress(es)
210
/* LINTED pointer casts may be troublesome */
211
if ((hostent = gethostbyname(address->addr.domain)) == NULL) {
214
slog(LOG_DEBUG, "%s: gethostbyname(%s): %s",
215
function, strcheck(name = str2vis(address->addr.domain,
216
strlen(address->addr.domain))), hstrerror(h_errno));
221
matched = addrisinlist(&rule->addr.ipv4.ip, &rule->addr.ipv4.mask,
222
(const struct in_addr **)hostent->h_addr_list);
224
else if (rule->atype == SOCKS_ADDR_IPV4
225
&& address->atype == SOCKS_ADDR_IPV4) {
227
* match(rule.ipaddress, address.ipaddress)
228
* try first a simple comparison, address against rule.
230
if (!(matched = addrareeq(&rule->addr.ipv4.ip, &rule->addr.ipv4.mask,
231
&address->addr.ipv4))) {
233
* Didn't match. If alias is set, try to resolve address
234
* to hostname(s), the hostname back to IP address(es) and
235
* then match those IP address(es) against rule.
236
* rule isin address->hostname(s)->ipaddress(es)
247
/* LINTED pointer casts may be troublesome */
248
if ((hostent = gethostbyaddr((const char *)&address->addr.ipv4,
249
sizeof(address->addr.ipv4), AF_INET)) == NULL) {
250
slog(LOG_DEBUG, "%s: %s: %s",
251
function, inet_ntoa(address->addr.ipv4), hstrerror(h_errno));
255
if ((hostent = hostentdup(hostent)) == NULL) {
256
swarnx("%s: hostentdup()", function);
260
nexthost = hostent->h_name;
263
struct hostent *iphostent;
265
/* iphostent = address->hostname(s)->ipaddress(es) */
266
if ((iphostent = gethostbyname(nexthost)) == NULL) {
267
slog(LOG_DEBUG, "%s: gethostbyname(%s): %s",
268
function, nexthost, hstrerror(h_errno));
272
/* rule isin address->hostname(s)->ipaddress(es) */
273
if (addrisinlist(&rule->addr.ipv4.ip, &rule->addr.ipv4.mask,
274
(const struct in_addr **)iphostent->h_addr_list)) {
278
} while (hostent->h_aliases != NULL
279
&& (nexthost = hostent->h_aliases[i++]) != NULL);
281
hostentfree(hostent);
288
else if (rule->atype == SOCKS_ADDR_DOMAIN
289
&& address->atype == SOCKS_ADDR_DOMAIN) {
291
* match(rule.hostname, address.hostname)
292
* Try simple match first.
294
* If no go and rule is a hostname rather than a domain,
295
* resolve both rule and address to IP address(es) and compare
296
* each IP address of resolved rule against each IP address of
298
* rule->ipaddress(es) isin address->ipaddress(es)
302
if (hostareeq(rule->addr.domain, address->addr.domain))
304
else if (doresolve && *rule->addr.domain != '.') {
305
struct hostent *addresshostent;
309
if ((hostent = gethostbyname(rule->addr.domain)) == NULL) {
310
slog(LOG_DEBUG, "%s: gethostbyname(%s): %s",
311
function, rule->addr.domain, hstrerror(h_errno));
315
if ((hostent = hostentdup(hostent)) == NULL) {
316
swarnx("%s: hostentdup()", function);
320
if ((addresshostent = gethostbyname(address->addr.domain)) == NULL) {
321
slog(LOG_DEBUG, "%s: gethostbyname(%s): %s",
322
function, address->addr.domain, hstrerror(h_errno));
323
hostentfree(hostent);
328
* rule->ipaddress(es) isin address->ipaddress(es)
331
if (hostent->h_addr_list == NULL) {
332
hostentfree(hostent);
336
mask.s_addr = htonl(0xffffffff);
337
for (i = 0; !matched && hostent->h_addr_list[i] != NULL; ++i)
338
/* LINTED pointer casts may be troublesome */
340
= addrisinlist((const struct in_addr *)hostent->h_addr_list[i],
341
&mask, (const struct in_addr **)addresshostent->h_addr_list);
343
hostentfree(hostent);
349
else if (rule->atype == SOCKS_ADDR_DOMAIN
350
&& address->atype == SOCKS_ADDR_IPV4) {
352
* match(rule.hostname, address.ipaddress)
353
* If rule is not a domain, try resolving rule to IP address(es)
354
* and match against address.
355
* address isin rule->ipaddress
358
* If no match, resolve address to hostname(s) and match each
360
* rule isin address->hostname
363
* If still no match and alias is set, resolve all IP addresses
364
* of all hostname(s) resolved from address back to hostname(s)
365
* and match them against rule.
366
* rule isin address->hostname->ipaddress->hostname
373
if (*rule->addr.domain != '.') {
374
/* address isin rule->ipaddress */
377
if ((hostent = gethostbyname(rule->addr.domain)) == NULL) {
378
slog(LOG_DEBUG, "%s: gethostbyname(%s): %s",
379
function, rule->addr.domain, hstrerror(h_errno));
383
mask.s_addr = htonl(0xffffffff);
384
matched = addrisinlist(&address->addr.ipv4, &mask,
385
(const struct in_addr **)hostent->h_addr_list);
389
/* rule isin address->hostname */
391
/* LINTED pointer casts may be troublesome */
392
if ((hostent = gethostbyaddr((const char *)&address->addr.ipv4,
393
sizeof(address->addr.ipv4), AF_INET)) == NULL) {
394
slog(LOG_DEBUG, "%s: gethostbyaddr(%s): %s",
395
function, inet_ntoa(address->addr.ipv4), hstrerror(h_errno));
399
if (hostareeq(rule->addr.domain, hostent->h_name)
400
|| hostisinlist(rule->addr.domain, (const char **)hostent->h_aliases))
404
if (!matched && alias) {
406
* rule isin address->hostname->ipaddress->hostname.
407
* hostent is already address->hostname due to above.
412
if ((hostent = hostentdup(hostent)) == NULL) {
413
swarnx("%s: hostentdup()", function);
417
nexthost = hostent->h_name;
421
struct hostent *host;
423
/* host; address->hostname->ipaddress */
424
if ((host = gethostbyname(nexthost)) == NULL) {
425
slog(LOG_DEBUG, "%s: gethostbyname(%s): %s",
426
function, nexthost, hstrerror(h_errno));
430
if ((host = hostentdup(host)) == NULL) {
431
swarnx("%s: hostentdup()", function);
435
/* LINTED pointer casts may be troublesome */
437
host->h_addr_list != NULL && host->h_addr_list[ii] != NULL;
441
/* ip; address->hostname->ipaddress->hostname */
442
if ((ip = gethostbyaddr(host->h_addr_list[ii],
443
sizeof(struct in_addr), AF_INET)) == NULL) {
444
/* LINTED pointer casts may be troublesome */
445
slog(LOG_DEBUG, "%s: gethostbyaddr(%s): %s",
446
function, inet_ntoa(*(struct in_addr *)host->h_addr_list[ii]),
451
if (hostareeq(rule->addr.domain, ip->h_name)
452
|| hostisinlist(rule->addr.domain,
453
(const char **)ip->h_aliases)) {
460
} while (!matched && hostent->h_aliases != NULL
461
&& (nexthost = hostent->h_aliases[i++]) != NULL);
463
hostentfree(hostent);
468
else if (rule->atype == SOCKS_ADDR_IFNAME) {
470
* Like ipaddress, just call it for each IP address of interface.
472
* match(rule.ifname2ipaddress, address)
478
while (!matched && ifname2sockaddr(rule->addr.ifname, i++, &sa)
480
struct ruleaddress_t ruleaddr;
482
ruleaddr = *rule; /* identical except addr field. */
483
ruleaddr.atype = SOCKS_ADDR_IPV4;
484
/* LINTED pointer casts may be troublesome */
485
ruleaddr.addr.ipv4.ip = TOIN(&sa)->sin_addr;
486
ruleaddr.addr.ipv4.mask.s_addr = htonl(0xffffffff);
488
matched = addressmatch(&ruleaddr, address, protocol, alias);
498
addrisinlist(addr, mask, list)
499
const struct in_addr *addr;
500
const struct in_addr *mask;
501
const struct in_addr **list;
507
while (*list != NULL)
508
if (addrareeq(addr, mask, *list))
516
addrareeq(addr, mask, against)
517
const struct in_addr *addr;
518
const struct in_addr *mask;
519
const struct in_addr *against;
522
if ((addr->s_addr & mask->s_addr) == (against->s_addr & mask->s_addr))
528
hostisinlist(host, list)
536
while (*list != NULL)
537
if (hostareeq(host, *list))
545
hostareeq(domain, remotedomain)
547
const char *remotedomain;
549
const int domainlen = strlen(domain);
550
const int remotedomainlen = strlen(remotedomain);
552
if (*domain == '.') { /* match everything ending in domain */
553
if (domainlen - 1 > remotedomainlen)
554
return 0; /* address to compare against too short, can't match. */
555
return strcasecmp(domain + 1,
556
remotedomain + (remotedomainlen - (domainlen - 1))) == 0;
558
else /* need exact match. */
559
return strcasecmp(domain, remotedomain) == 0;