Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions botgate.md
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,8 @@ https://www.ip2location.com/free/visitor-blocker

When downloading, select **"Apache 2.0 - 2.3 .htaccess deny"** as the output format. Other formats offered on that page (Apache 2.4, Nginx, CIDR, iptables, etc.) are not recognized by BotGate's parser.

Choose **IPv4** lists. BotGate listens on IPv4 only, so IPv6 ranges in a geo file are skipped with a warning.

IP2Location recommends refreshing these lists monthly, since IP allocations change over time.

### Performance
Expand Down
9 changes: 9 additions & 0 deletions botgate_proxy.py
Original file line number Diff line number Diff line change
Expand Up @@ -415,6 +415,7 @@ def load_geo_file(path):
ends) for fast binary-search containment checks. Non-'deny from'
lines (comments, <Limit>, order/allow directives) are ignored."""
ranges = []
skipped_ipv6 = 0
try:
with open(path, "r", errors="ignore") as f:
for line in f:
Expand All @@ -427,9 +428,17 @@ def load_geo_file(path):
except ValueError:
log.warning(f"Skipping malformed entry in {path}: {cidr!r}")
continue
if net.version != 4:
# Listeners are IPv4-only, and an IPv6 range does not fit in
# the array below (OverflowError at startup).
skipped_ipv6 += 1
continue
ranges.append((int(net.network_address), int(net.broadcast_address)))
except OSError as e:
log.warning(f"Could not read geo file {path}: {e}")
if skipped_ipv6:
log.warning(f"Skipped {skipped_ipv6} IPv6 range(s) in {path}: "
f"BotGate listens on IPv4 only.")

ranges.sort()
starts = array.array("L", (r[0] for r in ranges))
Expand Down
28 changes: 28 additions & 0 deletions tests/test_geo.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
"""Geo blocklist loading."""
from pathlib import Path
import sys
import tempfile
import unittest

sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
import botgate_proxy as botgate


class GeoFileTests(unittest.TestCase):
def test_ipv6_ranges_are_skipped_not_fatal(self):
with tempfile.TemporaryDirectory() as directory:
path = Path(directory) / "mixed.txt"
path.write_text("deny from 203.0.113.0/24\n"
"deny from 2001:db8::/32\n"
"deny from 198.51.100.0/24\n")
with self.assertLogs(botgate.log, level="WARNING") as logs:
starts, ends = botgate.load_geo_file(str(path))
self.assertEqual(len(starts), 2)
for ip in ["203.0.113.7", "198.51.100.200"]:
self.assertTrue(botgate.geo_range_contains(int(botgate.ipaddress.ip_address(ip)), starts, ends), ip)
self.assertFalse(botgate.geo_range_contains(int(botgate.ipaddress.ip_address("192.0.2.1")), starts, ends))
self.assertTrue(any("1 IPv6 range" in line for line in logs.output), logs.output)


if __name__ == "__main__":
unittest.main()
Loading