220 lines
8 KiB
Python
220 lines
8 KiB
Python
from hashlib import md5
|
|
from datetime import date, timedelta
|
|
from time import sleep
|
|
from logging import getLogger
|
|
from ipaddress import IPv4Address
|
|
from urllib.error import HTTPError, URLError
|
|
from geoip2.database import Reader
|
|
from tarfile import open as taropen
|
|
from urllib3 import disable_warnings
|
|
from os import stat, remove, makedirs
|
|
from urllib.request import urlretrieve
|
|
from json.decoder import JSONDecodeError
|
|
from os.path import abspath, join, basename, isdir
|
|
from urllib3.exceptions import InsecureRequestWarning
|
|
from requests.exceptions import InvalidSchema, SSLError, ConnectionError, ChunkedEncodingError
|
|
|
|
logger = getLogger()
|
|
|
|
|
|
class GeoIPHandler(object):
|
|
def __init__(self, data_folder, maxmind_license_key):
|
|
self.data_folder = data_folder
|
|
self.maxmind_license_key = maxmind_license_key
|
|
self.dbfile = abspath(join(self.data_folder, 'GeoLite2-City.mmdb'))
|
|
self.logger = getLogger()
|
|
self.reader = None
|
|
self.reader_manager(action='open')
|
|
|
|
self.logger.info('Opening persistent connection to the MaxMind DB...')
|
|
|
|
def reader_manager(self, action=None):
|
|
if action == 'open':
|
|
try:
|
|
self.reader = Reader(self.dbfile)
|
|
except FileNotFoundError:
|
|
self.logger.error("Could not find MaxMind DB! Downloading!")
|
|
result_status = self.download()
|
|
if result_status:
|
|
self.logger.error("Could not download MaxMind DB! You may need to manually install it.")
|
|
exit(1)
|
|
else:
|
|
self.reader = Reader(self.dbfile)
|
|
else:
|
|
self.reader.close()
|
|
|
|
def lookup(self, ipaddress):
|
|
ip = ipaddress
|
|
self.logger.debug('Getting lat/long for Tautulli stream using ip with last octet ending in %s',
|
|
ip.split('.')[-1:][0])
|
|
return self.reader.city(ip)
|
|
|
|
def update(self):
|
|
today = date.today()
|
|
|
|
try:
|
|
dbdate = date.fromtimestamp(stat(self.dbfile).st_mtime)
|
|
db_next_update = date.fromtimestamp(stat(self.dbfile).st_mtime) + timedelta(days=30)
|
|
|
|
except FileNotFoundError:
|
|
self.logger.error("Could not find MaxMind DB as: %s", self.dbfile)
|
|
self.download()
|
|
dbdate = date.fromtimestamp(stat(self.dbfile).st_mtime)
|
|
db_next_update = date.fromtimestamp(stat(self.dbfile).st_mtime) + timedelta(days=30)
|
|
|
|
if db_next_update < today:
|
|
self.logger.info("Newer MaxMind DB available, Updating...")
|
|
self.logger.debug("MaxMind DB date %s, DB updates after: %s, Today: %s",
|
|
dbdate, db_next_update, today)
|
|
self.reader_manager(action='close')
|
|
self.download()
|
|
self.reader_manager(action='open')
|
|
else:
|
|
db_days_update = db_next_update - today
|
|
self.logger.debug("MaxMind DB will update in %s days", abs(db_days_update.days))
|
|
self.logger.debug("MaxMind DB date %s, DB updates after: %s, Today: %s",
|
|
dbdate, db_next_update, today)
|
|
|
|
def download(self):
|
|
tar_dbfile = abspath(join(self.data_folder, 'GeoLite2-City.tar.gz'))
|
|
maxmind_url = ('https://download.maxmind.com/app/geoip_download?edition_id=GeoLite2-City'
|
|
f'&suffix=tar.gz&license_key={self.maxmind_license_key}')
|
|
downloaded = False
|
|
|
|
retry_counter = 0
|
|
|
|
while not downloaded:
|
|
self.logger.info('Downloading GeoLite2 DB from MaxMind...')
|
|
try:
|
|
urlretrieve(maxmind_url, tar_dbfile)
|
|
downloaded = True
|
|
except URLError as e:
|
|
self.logger.error("Problem downloading new MaxMind DB: %s", e)
|
|
result_status = 1
|
|
return result_status
|
|
except HTTPError as e:
|
|
if e.code == 401:
|
|
self.logger.error("Your MaxMind license key is incorect! Check your config: %s", e)
|
|
result_status = 1
|
|
return result_status
|
|
else:
|
|
self.logger.error("Problem downloading new MaxMind DB... Trying again: %s", e)
|
|
sleep(2)
|
|
retry_counter = (retry_counter + 1)
|
|
|
|
if retry_counter >= 3:
|
|
self.logger.error("Retried downloading the new MaxMind DB 3 times and failed... Aborting!")
|
|
result_status = 1
|
|
return result_status
|
|
try:
|
|
remove(self.dbfile)
|
|
except FileNotFoundError:
|
|
self.logger.warning("Cannot remove MaxMind DB as it does not exist!")
|
|
|
|
self.logger.debug("Opening MaxMind tar file : %s", tar_dbfile)
|
|
|
|
tar = taropen(tar_dbfile, 'r:gz')
|
|
|
|
for files in tar.getmembers():
|
|
if 'GeoLite2-City.mmdb' in files.name:
|
|
self.logger.debug('"GeoLite2-City.mmdb" FOUND in tar file')
|
|
files.name = basename(files.name)
|
|
tar.extract(files, self.data_folder)
|
|
self.logger.debug('%s has been extracted to %s', files, self.data_folder)
|
|
tar.close()
|
|
try:
|
|
remove(tar_dbfile)
|
|
self.logger.debug('Removed the MaxMind DB tar file.')
|
|
except FileNotFoundError:
|
|
self.logger.warning("Cannot remove MaxMind DB TAR file as it does not exist!")
|
|
|
|
|
|
def hashit(string):
|
|
encoded = string.encode()
|
|
hashed = md5(encoded).hexdigest()
|
|
|
|
return hashed
|
|
|
|
|
|
def rfc1918_ip_check(ip):
|
|
rfc1918_ip = IPv4Address(ip).is_private
|
|
|
|
return rfc1918_ip
|
|
|
|
|
|
def connection_handler(session, request, verify, as_is_reply=False):
|
|
air = as_is_reply
|
|
s = session
|
|
r = request
|
|
v = verify
|
|
return_json = False
|
|
|
|
disable_warnings(InsecureRequestWarning)
|
|
|
|
try:
|
|
get = s.send(r, verify=v)
|
|
if get.status_code == 401:
|
|
if 'NoSiteContext' in str(get.content):
|
|
logger.info('Your Site is incorrect for %s', r.url)
|
|
elif 'LoginRequired' in str(get.content):
|
|
logger.info('Your login credentials are incorrect for %s', r.url)
|
|
else:
|
|
logger.info('Your api key is incorrect for %s', r.url)
|
|
elif get.status_code == 404:
|
|
logger.info('This url doesnt even resolve: %s', r.url)
|
|
elif get.status_code == 200:
|
|
try:
|
|
return_json = get.json()
|
|
except JSONDecodeError:
|
|
logger.error('No JSON response. Response is: %s', get.text)
|
|
if air:
|
|
return get
|
|
except InvalidSchema:
|
|
logger.error("You added http(s):// in the config file. Don't do that.")
|
|
except SSLError as e:
|
|
logger.error('Either your host is unreachable or you have an SSL issue. : %s', e)
|
|
except ConnectionError as e:
|
|
logger.error('Cannot resolve the url/ip/port. Check connectivity. Error: %s', e)
|
|
except ChunkedEncodingError as e:
|
|
logger.error('Broken connection during request... oops? Error: %s', e)
|
|
|
|
return return_json
|
|
|
|
|
|
def mkdir_p(path):
|
|
templogger = getLogger('temp')
|
|
try:
|
|
if not isdir(path):
|
|
templogger.info('Creating folder %s ', path)
|
|
makedirs(path, exist_ok=True)
|
|
except Exception as e:
|
|
templogger.error('Could not create folder %s : %s ', path, e)
|
|
|
|
|
|
def clean_sid_check(server_id_list, server_type=None):
|
|
t = server_type
|
|
sid_list = server_id_list
|
|
cleaned_list = sid_list.replace(' ', '').split(',')
|
|
valid_sids = []
|
|
for sid in cleaned_list:
|
|
try:
|
|
valid_sids.append(int(sid))
|
|
except ValueError:
|
|
logger.error("%s is not a valid server id number", sid)
|
|
if valid_sids:
|
|
logger.info('%s : %s', t.upper(), valid_sids)
|
|
return valid_sids
|
|
else:
|
|
logger.error('No valid %s', t.upper())
|
|
return False
|
|
|
|
|
|
def boolcheck(var):
|
|
if var.lower() in ['true', 'yes']:
|
|
return True
|
|
else:
|
|
return False
|
|
|
|
|
|
def itemgetter_with_default(**defaults):
|
|
return lambda obj: tuple(obj.get(k, v) for k, v in defaults.items())
|