2022-01-23 17:03:22 +00:00
|
|
|
#!/usr/bin/env python3
|
|
|
|
#
|
|
|
|
# mmdb-server is an open source fast API server to lookup IP addresses for their geographic location.
|
|
|
|
#
|
|
|
|
# The server is released under the AGPL version 3 or later.
|
|
|
|
#
|
|
|
|
# Copyright (C) 2022 Alexandre Dulaunoy
|
|
|
|
|
|
|
|
import configparser
|
|
|
|
import time
|
|
|
|
from ipaddress import ip_address
|
2022-01-24 09:33:56 +00:00
|
|
|
import json
|
2022-01-23 17:03:22 +00:00
|
|
|
from wsgiref.simple_server import make_server
|
|
|
|
|
|
|
|
import falcon
|
|
|
|
import maxminddb
|
|
|
|
|
2022-02-06 10:33:17 +00:00
|
|
|
version = "0.5"
|
2022-01-23 17:03:22 +00:00
|
|
|
config = configparser.ConfigParser()
|
2023-10-30 12:17:21 +00:00
|
|
|
config.read('etc/server.conf')
|
2022-01-23 17:03:22 +00:00
|
|
|
mmdb_file = config['global'].get('mmdb_file')
|
|
|
|
pubsub = config['global'].getboolean('lookup_pubsub')
|
2022-01-24 07:59:06 +00:00
|
|
|
port = config['global'].getint('port')
|
2022-01-24 09:33:56 +00:00
|
|
|
country_file = config['global'].get('country_file')
|
|
|
|
|
2022-02-06 10:33:17 +00:00
|
|
|
mmdb_files = mmdb_file.split(",")
|
|
|
|
|
2022-01-24 09:33:56 +00:00
|
|
|
with open(country_file) as j:
|
|
|
|
country_info = json.load(j)
|
|
|
|
|
2022-01-23 17:03:22 +00:00
|
|
|
if pubsub:
|
|
|
|
import redis
|
|
|
|
rdb = redis.Redis(host='127.0.0.1')
|
|
|
|
|
2022-02-06 10:33:17 +00:00
|
|
|
mmdbs = []
|
|
|
|
for mmdb_file in mmdb_files:
|
|
|
|
meta = {}
|
|
|
|
meta['reader'] = maxminddb.open_database(mmdb_file, maxminddb.MODE_MEMORY)
|
|
|
|
meta['description'] = meta['reader'].metadata().description
|
|
|
|
meta['build_db'] = time.strftime(
|
|
|
|
'%Y-%m-%d %H:%M:%S', time.localtime(meta['reader'].metadata().build_epoch)
|
|
|
|
)
|
|
|
|
meta['db_source'] = meta['reader'].metadata().database_type
|
|
|
|
meta['nb_nodes'] = meta['reader'].metadata().node_count
|
|
|
|
mmdbs.append(meta)
|
|
|
|
|
2022-02-06 10:41:57 +00:00
|
|
|
|
2022-01-23 17:03:22 +00:00
|
|
|
def validIPAddress(IP: str) -> bool:
|
|
|
|
try:
|
|
|
|
type(ip_address(IP))
|
|
|
|
return True
|
|
|
|
except ValueError:
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
|
|
def pubLookup(value: str) -> bool:
|
|
|
|
if not pubsub:
|
|
|
|
return False
|
|
|
|
rdb.publish('mmdb-server::lookup', f'{value}')
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
2022-01-24 09:33:56 +00:00
|
|
|
def countryLookup(country: str) -> dict:
|
2022-10-15 08:06:53 +00:00
|
|
|
if country != 'None' or country is not None or country != 'Unknown':
|
2022-10-15 08:13:25 +00:00
|
|
|
if country in country_info:
|
|
|
|
return country_info[country]
|
|
|
|
else:
|
|
|
|
return {}
|
2022-01-24 09:33:56 +00:00
|
|
|
else:
|
|
|
|
return {}
|
|
|
|
|
|
|
|
|
2022-01-23 17:03:22 +00:00
|
|
|
class GeoLookup:
|
|
|
|
def on_get(self, req, resp, value):
|
|
|
|
ret = []
|
|
|
|
ua = req.get_header('User-Agent')
|
|
|
|
ips = req.access_route
|
|
|
|
if not validIPAddress(value):
|
|
|
|
resp.status = falcon.HTTP_422
|
|
|
|
resp.media = "IPv4 or IPv6 address is in an incorrect format. Dotted decimal for IPv4 or textual representation for IPv6 are required."
|
|
|
|
return
|
|
|
|
pubLookup(value=f'{value} via {ips} using {ua}')
|
2022-02-06 10:33:17 +00:00
|
|
|
for mmdb in mmdbs:
|
|
|
|
m = {}
|
|
|
|
georesult = mmdb['reader'].get(value)
|
|
|
|
m = mmdb.copy()
|
|
|
|
del m['reader']
|
|
|
|
georesult['meta'] = m
|
|
|
|
georesult['ip'] = value
|
|
|
|
if georesult['country']['iso_code'] != 'None':
|
|
|
|
georesult['country_info'] = countryLookup(country=georesult['country']['iso_code'])
|
|
|
|
else:
|
|
|
|
georesult['country_info'] = {}
|
|
|
|
ret.append(georesult)
|
2022-01-24 07:51:24 +00:00
|
|
|
resp.media = ret
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
|
class MyGeoLookup:
|
|
|
|
def on_get(self, req, resp):
|
|
|
|
ret = []
|
|
|
|
ips = req.access_route
|
2022-02-06 10:33:17 +00:00
|
|
|
for mmdb in mmdbs:
|
|
|
|
m = {}
|
|
|
|
georesult = mmdb['reader'].get(ips[0])
|
|
|
|
m = mmdb.copy()
|
|
|
|
del m['reader']
|
|
|
|
georesult['meta'] = m
|
|
|
|
georesult['ip'] = ips[0]
|
|
|
|
if georesult['country']['iso_code'] != 'None':
|
|
|
|
georesult['country_info'] = countryLookup(country=georesult['country']['iso_code'])
|
|
|
|
else:
|
|
|
|
georesult['country_info'] = {}
|
|
|
|
ret.append(georesult)
|
2022-01-23 17:03:22 +00:00
|
|
|
resp.media = ret
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
|
app = falcon.App()
|
|
|
|
|
|
|
|
app.add_route('/geolookup/{value}', GeoLookup())
|
2022-01-24 07:51:24 +00:00
|
|
|
app.add_route('/', MyGeoLookup())
|
2022-01-23 17:03:22 +00:00
|
|
|
|
2023-10-30 12:17:21 +00:00
|
|
|
def main():
|
2022-01-24 07:59:06 +00:00
|
|
|
with make_server('', port, app) as httpd:
|
2022-01-24 08:00:16 +00:00
|
|
|
print(f'Serving on port {port}...')
|
2022-01-23 17:03:22 +00:00
|
|
|
httpd.serve_forever()
|
2023-10-30 12:17:21 +00:00
|
|
|
|
|
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
|
main()
|
|
|
|
|