#!/usr/bin/env python
# -*- coding: utf-8 -*-

from argparse import ArgumentParser
from ipaddress import ip_network
from os.path import join
from socket import gethostname
from sys import version_info

from core.config import CONFIG
from core.logfile import set_logger
from core.protocol import SIPServer
from core.tools import (
    get_public_ip,
    import_plugins,
    mkdir,
    stop_plugins,
)

from twisted.internet import error
from twisted.internet.reactor import listenUDP, run
from twisted.python.log import msg


__VERSION__ = '2.0.1'
__description__ = 'A SIP Honeypot'
__license__ = 'GPLv3'
__uri__ = 'https://gitlab.com/bontchev/siphoney'
__author__ = 'Vesselin Bontchev'
__email__ = 'vbontchev@yahoo.com'


if version_info[0] >= 3:
    def unicode(x):
        return x


def get_options(cfg_options):
    parser = ArgumentParser(description=__description__)

    parser.add_argument('-v', '--version', action='version', version=__VERSION__)
    parser.add_argument('-p', '--port', type=int, default=cfg_options['port'],
                        help='Port to listen on (default: %(default)s)')
    parser.add_argument('-l', '--logfile', type=str, default=cfg_options['logfile'],
                        help='Log file (default: stdout)')
    parser.add_argument('-s', '--sensor', type=str, default=cfg_options['sensor'],
                        help='Sensor name (default: %(default)s)')
    args = parser.parse_args()
    return args


def set_options():
    cfg_options = {}

    cfg_options['port'] = CONFIG.getint('honeypot', 'listen_port', fallback=5060)
    log_name = CONFIG.get('honeypot', 'log_filename', fallback='')
    if log_name:
        logdir = CONFIG.get('honeypot', 'log_path', fallback='log')
        mkdir(logdir)
        cfg_options['logfile'] = join(logdir, log_name)
    else:
        cfg_options['logfile'] = None
    cfg_options['sensor'] = CONFIG.get('honeypot', 'sensor_name', fallback=gethostname())

    args = get_options(cfg_options)

    cfg_options['port'] = args.port
    cfg_options['logfile'] = args.logfile
    cfg_options['sensor'] = args.sensor
    cfg_options['public_ip_url'] = CONFIG.get('honeypot', 'public_ip_url', fallback='https://ident.me')
    cfg_options['report_public_ip'] = CONFIG.getboolean('honeypot', 'report_public_ip', fallback=False)

    pub_ip = get_public_ip(cfg_options['public_ip_url'])
    if pub_ip is None:
        cfg_options['report_public_ip'] = False
        cfg_options['public_ip'] = '127.0.0.1'
    else:
        cfg_options['public_ip'] = pub_ip

    cfg_options['blacklist'] = CONFIG.get('honeypot', 'blacklist', fallback='127.0.0.1,192.168.0.0/16').split(',')

    return cfg_options


def main():
    cfg_options = set_options()

    set_logger(cfg_options)

    msg(__description__ + ' by ' + __author__)

    blacklist = []
    for network in cfg_options['blacklist']:
        try:
            if network:
                ip_network(unicode(network))
                blacklist += [network]
        except ValueError:
            msg('Blacklist element "{}" is not a valid IP address; ignored.'.format(network))
    cfg_options['blacklist'] = blacklist

    cfg_options['output_plugins'] = import_plugins(cfg_options)

    try:
        listenUDP(port=cfg_options['port'], protocol=SIPServer(cfg_options))
    except error.CannotListenError as e:
        msg('Cannot listen on port {}: {}'.format(e.port, e.socketError.strerror))
        stop_plugins(cfg_options)
        return
    run()
    msg('Shutdown requested, exiting...')
    stop_plugins(cfg_options)


if __name__ == '__main__':
    main()
