#!/usr/bin/env python

from __future__ import print_function
import pcapy
# import os
import sys
import subprocess  # use commandline
import requests    # whois
import re
from netaddr import valid_ipv4, valid_mac
import os
# import platform  # socket alternative to get hostname
import socket

"""
[kevin@Tardis test]$ ./pmap5.py -p test2.pcap -d

sudo tcpdump -s 0 -i en1 -w test.pcap
-s 0 will set the capture byte to its maximum i.e. 65535 and will not truncate
-i en1 captures Ethernet interface
-w test.pcap will create that pcap file

tcpdump -qns 0 -X -r osx.pcap

[kevin@Tardis tmp]$ sudo tcpdump -w osx.pcap
tcpdump: data link type PKTAP
tcpdump: listening on pktap, link-type PKTAP (Packet Tap), capture size 65535 bytes
^C4414 packets captured
4416 packets received by filter
0 packets dropped by kernel

"""


def checkSudo():
    return os.geteuid() == 0


# def command(cmd):
#     ans = subprocess.Popen([cmd], stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True).communicate()[0]
#     return ans


class Commands(object):
    """
    Unfortunately the extremely simple/useful commands was depreciated in favor
    of the complex/confusing subprocess ... this aims to simplify.
    """
    def command(self, cmd):
        ans = subprocess.Popen([cmd], stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True).communicate()[0]
        return ans


class WhoIs(object):
    """
    Updated
    """
    record = {}

    def __init__(self, ip):
        if not valid_ipv4(ip):
            print('Error: the IPv4 address {} is invalid'.format(ip))
            return
        rec = requests.get('http://whois.arin.net/rest/ip/{}.txt'.format(ip))
        if rec.status_code != 200:
            print('Error')
            return
        ans = {}
        r = re.compile(r"\s\s+")
        b = rec.text.split('\n')
        for l in b:
            if l and l[0] != '#':
                l = r.sub('', l)
                a = l.split(':')
                # print a
                ans[a[0]] = a[1]
        self.record = ans  # remove?
        self.CIDR = ans['CIDR']
        self.NetName = ans['NetName']
        self.NetRange = ans['NetRange']
        self.Organization = ans['Organization']
        self.Updated = ans['Updated']
        # return None


class GetHostName(Commands):
    name = None
    def __init__(self, ip):
        """Use the avahi (zeroconfig) tools or dig to find a host name given an
        ip address.

        in: ip
        out: string w/ host name or 'unknown' if the host name couldn't be found
        """
        # handle invalid ip address
        if not valid_ipv4(ip):
            print('Error: the IPv4 address {} is invalid'.format(ip))
            return

        # handle a localhost ip address
        if ip == '127.0.0.1' or ip == 'localhost':
            # self.name = platform.node()
            self.name = socket.gethostname()
            return

        # ok, now do more complex stuff
        name = 'unknown'
        if sys.platform == 'linux' or sys.platform == 'linux2':
            name = self.command("avahi-resolve-address {} | awk '{print $2}'".format(ip)).rstrip().rstrip('.')
        elif sys.platform == 'darwin':
            name = self.command('dig +short -x {} -p 5353 @224.0.0.251'.format(ip)).rstrip().rstrip('.')

        # detect any remaining errors
        if name.find('connection timed out') >= 0:
            name = 'unknown'
        elif name == '':
            name = 'unknown'

        self.name = name

    # def cmdLine(self, cmd):
    #     return subprocess.Popen([cmd], stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True).communicate()[0]


class CapturePackets(object):
    """
    todo
    """
    def __init__(self, iface, filename='test.pcap', pcFilter=None, num_packets=3000):
        # list all the network devices
        # print pcapy.findalldevs()

        max_bytes = 1024
        promiscuous = False
        read_timeout = 100  # in milliseconds
        pc = pcapy.open_live(iface, max_bytes, promiscuous, read_timeout)
        if pcFilter: pc.setfilter(pcFilter)
        self.dumper = pc.dump_open(filename)
        pc.loop(num_packets, self.recv_pkts)  # capture packets

    # callback for received packets
    def recv_pkts(self, hdr, data):
        try:
            # print data
            self.dumper.dump(hdr, data)
        except KeyboardInterrupt:  # probably show throw error instead
            exit('keyboard exit')
        except:
            exit('crap ... something went wrong')

    def run(self):
        pass
        # max_bytes = 1024
        # promiscuous = False
        # read_timeout = 100  # in milliseconds
        # pc = pcapy.open_live(iface, max_bytes, promiscuous, read_timeout)
        # if filter: pc.setfilter(filter)
        # self.dumper = pc.dump_open(filename)
        # pc.loop(num_packets, self.recv_pkts)  # capture packets


class MacLookup(object):
    def __init__(self, mac, full=False):
        self.vendor = self.get(mac, full)

    def get(self, mac, full):
        """
        json response from www.macvendorlookup.com:

        {u'addressL1': u'1 Infinite Loop',
        u'addressL2': u'',
        u'addressL3': u'Cupertino CA 95014',
        u'company': u'Apple',
        u'country': u'UNITED STATES',
        u'endDec': u'202412195315711',
        u'endHex': u'B817C2FFFFFF',
        u'startDec': u'202412178538496',
        u'startHex': u'B817C2000000',
        u'type': u'MA-L'}
        """
        unknown = {'company': 'unknown'}
        if not valid_mac(mac):
            print('Error: the mac addr {} is not valid'.format(mac))
            return

        try:
            r = requests.get('http://www.macvendorlookup.com/api/v2/' + mac)
        except requests.exceptions.HTTPError as e:
            print ("HTTPError:", e.message)
            return unknown

        if r.status_code == 204:  # no content found, bad MAC addr
            print ('ERROR: Bad MAC addr:', mac)
            return unknown
        elif r.headers['content-type'] != 'application/json':
            print ('ERROR: Wrong content type:', r.headers['content-type'])
            return unknown

        a = {}

        try:
            if full: a = r.json()[0]
            else: a['company'] = r.json()[0]['company']
            # print 'GOOD:',r.status_code,r.headers,r.ok,r.text,r.reason
        except:
            print ('ERROR:', r.status_code, r.headers, r.ok, r.text, r.reason)
            a = unknown

        return a