]>
git.ipfire.org Git - ipfire.org.git/blob - src/backend/geoip.py
9 import tornado
.platform
.caresresolver
11 from . import countries
13 from .decorators
import *
14 from .misc
import Object
16 # These lists are used to block access to the webapp
23 "b.barracudacentral.org",
27 "dnsbl-1.uceprotect.net",
28 "dnsbl-2.uceprotect.net",
29 "dnsbl-3.uceprotect.net",
31 "ix.dnsbl.manitu.net",
38 class Resolver(tornado
.platform
.caresresolver
.CaresResolver
):
39 def initialize(self
, **kwargs
):
43 self
.channel
= pycares
.Channel(sock_state_cb
=self
._sock
_state
_cb
, **kwargs
)
45 async def query(self
, name
, type=pycares
.QUERY_TYPE_A
):
47 fut
= tornado
.gen
.Future()
50 self
.channel
.query(name
, type, lambda result
, error
: fut
.set_result((result
, error
)))
52 # Wait for the response
53 result
, error
= await fut
58 if error
== pycares
.errno
.ARES_ENOTFOUND
:
61 # Ignore responses with no data
62 elif error
== pycares
.errno
.ARES_ENODATA
:
66 "C-Ares returned error %s: %s while resolving %s"
67 % (error
, pycares
.errno
.strerror(error
), name
)
77 return Resolver(tries
=2, timeout
=2, domains
=[])
79 def lookup(self
, address
):
80 return Address(self
.backend
, address
)
82 def guess_address_family(self
, addr
):
88 def get_country(self
, addr
):
89 ret
= self
.get_all(addr
)
94 def get_location(self
, addr
):
95 query
= "SELECT * FROM geoip \
96 WHERE %s BETWEEN start_ip AND end_ip LIMIT 1"
98 return self
.db
.get(query
, addr
)
100 def get_asn(self
, addr
):
101 query
= "SELECT asn FROM geoip_asn \
102 WHERE %s BETWEEN start_ip AND end_ip LIMIT 1"
104 ret
= self
.db
.get(query
, addr
)
109 def get_all(self
, addr
):
110 location
= self
.get_location(addr
)
113 location
["asn"] = self
.get_asn(addr
)
118 "A1" : "Anonymous Proxy",
119 "A2" : "Satellite Provider",
120 "AP" : "Asia/Pacific Region",
124 def get_country_name(self
, code
):
125 return countries
.get_name(code
)
127 async def test_blacklist(self
, address
):
128 address
= self
.lookup(address
)
130 # Determne blacklist status
131 status
= await address
.is_blacklisted()
133 print("Blacklist status for %s: %s" % (address
, status
))
136 class Address(Object
):
137 def init(self
, address
):
138 self
.address
= ipaddress
.ip_address(address
)
141 return "%s" % self
.address
145 if isinstance(self
.address
, ipaddress
.IPv6Address
):
146 return socket
.AF_INET6
147 elif isinstance(self
.address
, ipaddress
.IPv4Address
):
148 return socket
.AF_INET
152 def _make_blacklist_rr(self
, blacklist
):
153 if self
.family
== socket
.AF_INET6
:
154 octets
= list(self
.address
.exploded
.replace(":", ""))
155 elif self
.family
== socket
.AF_INET
:
156 octets
= str(self
.address
).split(".")
158 raise NotImplementedError("Unknown IP protocol")
164 octets
.append(blacklist
)
166 return ".".join(octets
)
168 async def _resolve_blacklist(self
, blacklist
):
171 # Get resource record name
172 rr
= self
._make
_blacklist
_rr
(blacklist
)
174 # Get query type from IP protocol version
175 if self
.family
== socket
.AF_INET6
:
176 type = pycares
.QUERY_TYPE_AAAA
177 elif self
.family
== socket
.AF_INET
:
178 type = pycares
.QUERY_TYPE_A
180 raise NotImplementedError("Unknown IP protocol")
184 res
= await self
.backend
.geoip
.resolver
.query(rr
, type=type)
188 return return_code
, "%s" % e
192 logging
.debug("%s is not blacklisted on %s" % (self
, blacklist
))
193 return return_code
, None
195 # Extract return code from DNS response
197 return_code
= row
.host
200 # If the IP address is on a blacklist, we will try to fetch the TXT record
201 reason
= await self
.backend
.geoip
.resolver
.query(rr
, type=pycares
.QUERY_TYPE_TXT
)
204 logging
.debug("%s is blacklisted on %s: %s" % (self
, blacklist
, reason
or "N/A"))
206 # Take the first reason
209 return return_code
, i
.text
211 # Blocked, but no reason
212 return return_code
, None
214 async def get_blacklists(self
):
215 blacklists
= { bl
: self
._resolve
_blacklist
(bl
) for bl
in BLACKLISTS
}
219 async def is_blacklisted(self
):
220 logging
.debug("Checking if %s is blacklisted..." % self
)
223 blacklists
= { bl
: self
._resolve
_blacklist
(bl
) for bl
in BLOCKLISTS
}
225 # If we are blacklisted on one list, this one is screwed
226 for bl
in blacklists
:
227 code
, message
= await blacklists
[bl
]
229 logging
.debug("Response from %s is: %s (%s)" % (bl
, code
, message
))
231 # Exclude matches on SBLCSS
232 if bl
== "sbl.spamhaus.org" and code
== "127.0.0.3":
235 # Consider the host blocked for any non-zero return code