#! /usr/bin/env python
#
# HTTPRequest, HTTP classes (C) by Steeve Barbeau
#
# Scrive i siti web visitati all'interno di: /opt/td-config/run/links/mac
# dove mac = {mac1, mac2, ..., macN}
#

import subprocess
import signal
import getopt
import re
import string
import time
import logging
logging.getLogger("scapy.runtime").setLevel(logging.ERROR)
from scapy.all import *

global interface
global www
global init

def getmylocaltime():
        year, mon, mday, hour, mmin, sec = map(str, time.localtime()[:6])

        if len(str(mon)) == 1:
            mon2 = '0' + str(mon)
        else:
            mon2 = str(mon)

        if len(str(mday)) == 1:
            mday2 = '0' + str(mday)
        else:
            mday2 = str(mday)

        if len(str(hour)) == 1:
            hour2 = '0' + str(hour)
        else:
            hour2 = str(hour)

        if len(str(mmin)) == 1:
            mmin2 = '0' + str(mmin)
        else:
            mmin2 = str(mmin)

        if len(str(sec)) == 1:
            sec2 = '0' + str(sec)
        else:
            sec2 = str(sec)

        if mon2 == '01':
            mon2 = 'Gen'
        elif mon2 == '02':
            mon2 = 'Feb'
        elif mon2 == '03':
            mon2 = 'Mar'
        elif mon2 == '04':
            mon2 = 'Apr'
        elif mon2 == '05':
            mon2 = 'May'
        elif mon2 == '06':
            mon2 = 'Jun'
        elif mon2 == '07':
            mon2 = 'Jul'
        elif mon2 == '08':
            mon2 = 'Aug'
        elif mon2 == '09':
            mon2 = 'Sep'
        elif mon2 == '10':
            mon2 = 'Oct'
        elif mon2 == '11':
            mon2 = 'Nov'
        elif mon2 == '12':
            mon2 = 'Dec'

        return mon2 + ' ' + mday2 + ' ' + hour2 + ':' + mmin2 + ':' + sec2

class HTTPrequest(Packet):
	name = "HTTPrequest"
	http_methods = "^(OPTIONS|GET|HEAD|POST|PUT|DELETE|TRACE|CONNECT)"
	fields_desc=[StrField("Method", None, fmt="H"),
				StrField("Host", None, fmt="H"),
				StrField("UserAgent", None, fmt="H"),
				StrField("Accept", None, fmt="H"),
				StrField("AcceptLanguage", None, fmt="H"),
				StrField("AcceptEncoding", None, fmt="H"),
				StrField("AcceptCharset", None, fmt="H"),
				StrField("Referer", None, fmt="H"),
				StrField("Authorization", None, fmt="H"),
				StrField("Expect", None, fmt="H"),
				StrField("From", None, fmt="H"),
				StrField("IfMatch", None, fmt="H"),
				StrField("IfModifiedSince", None, fmt="H"),
				StrField("IfNoneMatch", None, fmt="H"),
				StrField("IfRange", None, fmt="H"),
				StrField("IfUnmodifiedSince", None, fmt="H"),
				StrField("MaxForwards", None, fmt="H"),
				StrField("ProxyAuthorization", None, fmt="H"),
				StrField("Range", None, fmt="H"),
				StrField("TE", None, fmt="H")]


	def do_dissect(self, s):
		fields_rfc = ["Method", "Host", "User-Agent", "Accept", "Accept-Language", "Accept-Encoding", "Accept-Charset", "Referer", "Authorization", "Expect", "From", "If-Match", "If-Modified-Since", "If-None-Match", "If-Range", "If-Unmodified-Since", "Max-Forwards", "Proxy-Authorization", "Range", "TE"]
		
		a=s.split("\r\n")
		obj = self.fields_desc[:]
		obj.reverse()
		fields_rfc.reverse()
		while obj:
			f = obj.pop()
			g = fields_rfc.pop()
			for x in a:
				if(g=="Method"):
					prog=re.compile(self.http_methods)
				else:
					prog=re.compile(g+":", re.IGNORECASE)
				result=prog.search(x)
				if result:
					self.setfieldval(f.name, x+'\r\n')
					a.remove(x)
		return '\r\n'+"".join(a)

class HTTP(Packet):
	name="HTTP"
	fields_desc = [StrField("CacheControl", None, fmt="H"),
					StrField("Connection", None, fmt="H"),
					StrField("Date", None, fmt="H"),
					StrField("Pragma", None, fmt="H"),
					StrField("Trailer", None, fmt="H"),
					StrField("TransferEncoding", None, fmt="H"),
					StrField("Upgrade", None, fmt="H"),
					StrField("Via", None, fmt="H"),
					StrField("Warning", None, fmt="H"),
					StrField("KeepAlive", None, fmt="H"),
					StrField("Allow", None, fmt="H"),
					StrField("ContentEncoding", None, fmt="H"),
					StrField("ContentLanguage", None, fmt="H"),
					StrField("ContentLength", None, fmt="H"),
					StrField("ContentLocation", None, fmt="H"),
					StrField("ContentMD5", None, fmt="H"),
					StrField("ContentRange", None, fmt="H"),
					StrField("ContentType", None, fmt="H"),
					StrField("Expires", None, fmt="H"),
					StrField("LastModified", None, fmt="H")]

	def do_dissect(self, s):
		fields_rfc = ["Cache-Control", "Connection", "Date", "Pragma", "Trailer", "Transfer-Encoding", "Upgrade", "Via", "Warning", "Keep-Alive", "Allow", "Content-Encoding", "Content-Language", "Content-Length", "Content-Location", "Content-MD5", "Content-Range", "Content-Type", "Expires", "Last-Modified"]
		
		a=s.split("\r\n")
		obj = self.fields_desc[:]
		obj.reverse()
		fields_rfc.reverse()
		while obj:
			f = obj.pop()
			g = fields_rfc.pop()
			for x in a:
				prog=re.compile(g+":", re.IGNORECASE)
				result=prog.search(x)
				if result:
					self.setfieldval(f.name, x+'\r\n')
					a.remove(x)
		return "\r\n".join(a)
	
	def guess_payload_class(self, payload):
		prog=re.compile("^(OPTIONS|GET|HEAD|POST|PUT|DELETE|TRACE|CONNECT)")
		result=prog.search(payload)
		if result:
			return HTTPrequest
		else:
			prog=re.compile("^HTTP/((0\.9)|(1\.0)|(1\.1))\ [0-9]{3}.*")
			result=prog.search(payload)
			if result:
				return HTTPresponse
		return Packet.guess_payload_class(self, payload)


bind_layers(TCP, HTTP)

def name_usage():
	print " "	
	print "  Web link analyzer usage:"
	print " "
	print "  -i <interface>    : The interface you want to sniff"
	print " "
	sys.exit()

	return

def get_os(user):
	os = ""

	if string.find(user, "Ubuntu") != -1:
		os = "Ubuntu"
	elif string.find(user, "Android 1.0") != -1:
		os = "Android 1.0"
        elif string.find(user, "Android 1.1") != -1:
                os = "Android 1.1"
        elif string.find(user, "Android 1.5") != -1:
                os = "Android 1.5"
        elif string.find(user, "Android 1.6") != -1:
                os = "Android 1.6"
	elif string.find(user, "Android 1") != -1:
                os = "Android 1.x"
        elif string.find(user, "Android 2.0.1") != -1:
                os = "Android 2.0.1"
        elif string.find(user, "Android 2.0") != -1:
                os = "Android 2.0"
        elif string.find(user, "Android 2.1") != -1:
                os = "Android 2.1"
	elif string.find(user, "Android 2.2.1") != -1:
                os = "Android 2.2.1"
        elif string.find(user, "Android 2.2.2") != -1:
                os = "Android 2.2.2"
        elif string.find(user, "Android 2.2.3") != -1:
                os = "Android 2.2.3"
        elif string.find(user, "Android 2.2") != -1:
                os = "Android 2.2"
        elif string.find(user, "Android 2.3.1") != -1:
                os = "Android 2.3.1"
        elif string.find(user, "Android 2.3.2") != -1:
                os = "Android 2.3.2"
        elif string.find(user, "Android 2.3.3") != -1:
                os = "Android 2.3.3"
        elif string.find(user, "Android 2.3.4") != -1:
                os = "Android 2.3.4"
        elif string.find(user, "Android 2.3.5") != -1:
                os = "Android 2.3.5"
        elif string.find(user, "Android 2.3.6") != -1:
                os = "Android 2.3.6"
        elif string.find(user, "Android 2.3.7") != -1:
                os = "Android 2.3.7"
        elif string.find(user, "Android 2.3") != -1:
                os = "Android 2.3"
	elif string.find(user, "Android 2") != -1:
                os = "Android 2.x"
        elif string.find(user, "Android 3.0") != -1:
                os = "Android 3.0"
        elif string.find(user, "Android 3.1") != -1:
                os = "Android 3.1"
        elif string.find(user, "Android 3.2.1") != -1:
                os = "Android 3.2.1"
        elif string.find(user, "Android 3.2.2") != -1:
                os = "Android 3.2.2"
        elif string.find(user, "Android 3.2.3") != -1:
                os = "Android 3.2.3"
        elif string.find(user, "Android 3.2.4") != -1:
                os = "Android 3.2.4"
        elif string.find(user, "Android 3.2.5") != -1:
                os = "Android 3.2.5"
        elif string.find(user, "Android 3.2.6") != -1:
                os = "Android 3.2.6"
        elif string.find(user, "Android 3.2") != -1:
                os = "Android 3.2"
	elif string.find(user, "Android 3") != -1:
                os = "Android 3.x"
        elif string.find(user, "Android 4.0.1") != -1:
                os = "Android 4.0.1"
        elif string.find(user, "Android 4.0.2") != -1:
                os = "Android 4.0.2"
	elif string.find(user, "Android 4.0.3") != -1:
                os = "Android 4.0.3"
        elif string.find(user, "Android 4.0.4") != -1:
                os = "Android 4.0.4"
        elif string.find(user, "Android 4.0") != -1:
                os = "Android 4.0"
        elif string.find(user, "Android 4.1.1") != -1:
                os = "Android 4.1.1"
        elif string.find(user, "Android 4.1.2") != -1:
                os = "Android 4.1.2"
	elif string.find(user, "Android 4.1") != -1:
                os = "Android 4.1"
        elif string.find(user, "Android 4.2.1") != -1:
                os = "Android 4.2.1"
        elif string.find(user, "Android 4.2.2") != -1:
                os = "Android 4.2.2"
        elif string.find(user, "Android 4.2") != -1:
                os = "Android 4.2"
        elif string.find(user, "Android 4.3.1") != -1:
                os = "Android 4.3.1"
        elif string.find(user, "Android 4.3") != -1:
                os = "Android 4.3"
        elif string.find(user, "Android 4.4.1") != -1:
                os = "Android 4.4.1"
	elif string.find(user, "Android 4.4.2") != -1:
                os = "Android 4.4.2"
	elif string.find(user, "Android 4.4.3") != -1:
                os = "Android 4.4.3"
	elif string.find(user, "Android 4.4.4") != -1:
                os = "Android 4.4.4"
        elif string.find(user, "Android 4.4") != -1:
                os = "Android 4.4"
        elif string.find(user, "Android 4") != -1:
                os = "Android 4.x"
	elif string.find(user, "Android 5.0.2") != -1:
                os = "Android 5.0.2"
	elif string.find(user, "Android 5.0.1") != -1:
                os = "Android 5.0.1"
	elif string.find(user, "Android 5.0") != -1:
                os = "Android 5.0"
	elif string.find(user, "Android 5") != -1:
                os = "Android 5.x"
	elif string.find(user, "Android") != -1:
		os = "Android"
	elif string.find(user, "Linux") != -1 or string.find(user, "gnu-linux") != -1 or string.find(user, "linux-gnu") != -1:
		os = "Linux"
	elif string.find(user, "BlackBerry") != -1 or string.find(user, "Blackberry") != -1:
		os = "Blackberry"
	elif string.find(user, "iPhone") != -1:
		os = "iPhone"
	elif string.find(user, "iPad") != -1:
		os = "iPad"
	elif string.find(user, "iPod") != -1:
		os = "iPod"
	elif string.find(user, "OS X 10.10") != -1 or string.find(user, "OS X 10_10") != -1:
                os = "OS X 10.10"
	elif string.find(user, "OS X 10.9") != -1 or string.find(user, "OS X 10_9") != -1:
                os = "OS X 10.9"
	elif string.find(user, "Mac OS X 10.8") != -1 or string.find(user, "OS X 10_8") != -1:
                os = "OS X 10.8"
	elif string.find(user, "Mac OS X 10.7") != -1 or string.find(user, "OS X 10_7") != -1:
                os = "OS X 10.7"
	elif string.find(user, "Mac OS X 10.6") != -1 or string.find(user, "OS X 10_6") != -1:
                os = "OS X 10.6"
	elif string.find(user, "Mac OS X 10.5") != -1 or string.find(user, "OS X 10_5") != -1:
                os = "OS X 10.5"
	elif string.find(user, "Mac OS X 10.4") != -1 or string.find(user, "OS X 10_4") != -1:
                os = "OS X 10.4"
	elif string.find(user, "Mac OS X 10.3") != -1 or string.find(user, "OS X 10_3") != -1:
                os = "OS X 10.3"
	elif string.find(user, "Mac OS X 10.2") != -1 or string.find(user, "OS X 10_2") != -1:
                os = "OS X 10.2"
	elif string.find(user, "Mac OS X 10.1") != -1 or string.find(user, "OS X 10_1") != -1:
                os = "OS X 10.1"
	elif string.find(user, "Mac OS X 10.0") != -1 or string.find(user, "OS X 10_0") != -1:
                os = "OS X 10.0"
	elif string.find(user, "Mac OS X") != -1:
                os = "Mac OS X"
	elif string.find(user, "OS X") != -1:
		os = "OS X"
	elif string.find(user, "Mac") != -1:
		os = "Mac OS"
	elif string.find(user, "WindowsCE") != -1:
		os = "Windows CE"
	elif string.find(user, "Windows Phone") != -1:
		os = "Windows Phone"
	elif string.find(user, "Win3.11") != -1:
		os = "Windows 3.11"
	elif string.find(user, "WinNT3.51") != -1:
		os = "Windows NT 3.11"
	elif string.find(user, "WinNT4.0") != -1:
		os = "Windows NT 4.0"
	elif string.find(user, "Win95") != -1:
		os = "Windows 95"
	elif string.find(user, "Win98") != -1:
		os = "Windows 98"
	elif string.find(user, "Windows NT 5.0") != -1:
		os = "Windows 2000"
	elif string.find(user, "Win 9x 4.90") != -1 or string.find(user, "Windows 98") != -1:
		os = "Windows Millennium"
	elif string.find(user, "Windows NT 5.01") != -1:
		os = "Windows 2000"
	elif string.find(user, "Windows NT 5.1") != -1:
		os = "Windows XP"
	elif string.find(user, "Windows NT 5.2") != -1:
		os = "Windows Server 2003/XP x64 Edition"
	elif string.find(user, "Windows NT 6.0") != -1:
		os = "Windows Vista"
	elif string.find(user, "Windows NT 6.1") != -1:
		os = "Windows 7"
	elif string.find(user, "Windows NT 6.2") != -1:
		os = "Windows 8"
	elif string.find(user, "Windows NT 6.3") != -1:
		os = "Windows 8.1"
	elif string.find(user, "FreeBSD") != -1:
		os = "FreeBSD"
	elif string.find(user, "OpenBSD") != -1:
		os = "OpenBSD"
	elif string.find(user, "NetBSD") != -1:
		os = "NetBSD"
	elif string.find(user, "IRIX") != -1:
		os = "IRIX"
	elif string.find(user, "SunOS") != -1:
		os = "SunOS"
	elif string.find(user, "BeOS") != -1:
		os = "BeOS"
	else:
		os = "Unknown"

	return os

def get_browser(user, os):
	browser = ""

	if string.find(user, "MSIE 6.0") != -1:
		browser = "Internet Explorer 6"
	elif string.find(user, "MSIE 7.0") != -1:
		browser = "Internet Explorer 7"
	elif string.find(user, "MSIE 8.0") != -1:
		browser = "Internet Explorer 8"
	elif string.find(user, "MSIE 9.0") != -1:
		browser = "Internet Explorer 9"
	elif string.find(user, "MSIE 10.0") != -1:
		browser = "Internet Explorer 10"
	elif string.find(user, "rv:11.0") != -1:
		browser = "Internet Explorer 11" 
	elif string.find(user, "Firefox") != -1:
		browser = "Firefox"
	elif string.find(user, "Seamonkey") != -1:
		browser = "Seamonkey"
	elif string.find(user, "Chrome") != -1:
		browser = "Chrome"
	elif string.find(user, "Chromium") != -1:
		browser = "Chromium"
	elif string.find(user, "Safari") != -1 and string.find(os, "Android") == -1 and string.find(os, "Blackberry") == -1:
		browser = "Safari"
	elif string.find(user, "Opera") != -1:
		browser = "Opera"
	elif string.find(user, "Avant") != -1:
		browser = "Avant Browser"
	elif string.find(user, "MultiZilla") != -1:
		browser = "MultiZilla"
	elif string.find(user, "OmniWeb") != -1:
		browser = "OmniWeb"
	elif string.find(user, "iCab") != -1:
		browser = "iCab"
	elif string.find(user, "KDE") != -1 or string.find(user, "Konqueror") != -1:
		browser = "Konqueror"
	elif string.find(user, "Camino") != -1:
		browser = "Camino"
	elif string.find(user, "Netscape") != -1:
                browser = "Netscape"
	elif string.find(user, "Wget") != -1 or string.find(user, "wget") != -1:
                browser = "Wget"
	elif string.find(user, "Mozilla") != -1:
		if string.find(os, "Android") != -1:
			browser = 'Internet browser'
		elif string.find(os, "Blackberry") != -1:
			browser = 'Blackberry browser'
		else:
			browser = "Mozilla"
	else:
		if string.find(os, "Android") != -1:
                        browser = 'Internet browser'
                elif string.find(os, "Blackberry") != -1:
                        browser = 'Blackberry browser'
                else:
		    	browser = "Unknown"

	return browser

def link_analyzer(pkt):
	global init
	global www

	source_mac = ""
	payload = ""
	host = ""
	user = ""

	try:
		tcp_dport = pkt.getlayer(TCP).dport
	except:
		return

	if tcp_dport == "http" or tcp_dport == "https":
		try:
			source_mac = pkt.getlayer(Ether).dst
		except:
			return
	else:
		try:
			source_mac = pkt.getlayer(Ether).src
		except:
			return

	try:
		payload = pkt.getlayer(HTTP)
	except:
		return

	try:
		hostg = payload.getlayer(HTTPrequest).Host
		host = hostg[6:len(hostg) - 2]
		userg = payload.getlayer(HTTPrequest).UserAgent
		user = userg[12:len(userg) - 2]
	except:
		return

	if host == None:
		return

	try:
		referer = payload.getlayer(HTTPrequest).Referer
		rindex = string.find(referer, '//')
		referer = referer[rindex + 2:len(referer)]
		rindex2 = string.find(referer, '/')
		referer = referer[0:rindex2]
	except:
		referer = host
		pass

	source_mac = source_mac.upper()

	os = get_os(user)
	browser = get_browser(user, os)
	
	strprint = source_mac + " " +  host + " (OS: " + os + " - BROWSER: " + browser + " - REFERER: " + referer + ")"
	strfile = getmylocaltime() + '!' + host + '\n' 

	if init == 0:
		www = host
		f = open(source_mac, 'a')
                print "[     links][00000000] Added", strprint
                f.write(strfile)
                f.close()
	else:
		if www != host:
			f = open(source_mac, 'a')
			print "[     links][00000000] Added", strprint
			f.write(strfile)
			f.close()
			www = host

	year, mon, mday, hour, mmin, sec = map(str, time.localtime()[:6])
	entry = source_mac + '!' + os + '!' + browser + '!' + referer + '!' + hour + ':' + mmin + ':' + sec + '\n'

	if init == 0:
		init = init + 1
		f = open('linfo', 'w')
		f.write(entry)
		f.close()
	else:
		f = open('linfo.tmp', 'a')

		state = 0
		for line in open('linfo').readlines():
			macin = string.find(line, '!')
			mac = line[0:macin]
			if mac == source_mac:
				state = 1
				f.write(entry)
			else:
				f.write(line)

		if state == 0:
			f.write(entry)

		f.close()
		subprocess.call('mv linfo.tmp linfo', shell=True)

	return

def signal_handler(signum, frame):
	global interface

	print "[     links][00000000] Signal caught."
	print "[     links][00000000] Sniffing on", interface, "off"
	print "[     links][00000000]", interface, "promiscue mode off"
	subprocess.call(['ifconfig', interface, '-promisc'])

	sys.exit()

	return

def main():
	global interface
	global init

	init = 0

	try:
		opts, args = getopt.getopt(sys.argv[1:],"i:")
	except:
		name_usage()
		sys.exit(1)

	interface = None

	for o, a in opts:
		if o == '-i':
			interface = a

	if args or not interface:
		name_usage()
		sys.exit(1)

	try:
		subprocess.call('rm -Rf /opt/td-config/run/links 2> /dev/null', shell=True)
	except:
		pass

	try:
		os.makedirs('/opt/td-config/run/links/')
	except:
		pass

	os.chdir('/opt/td-config/run/links/')

	signal.signal(signal.SIGINT, signal_handler)
	signal.signal(signal.SIGTERM, signal_handler)

	print "[     links][00000000]", interface, "promiscue mode on"
	subprocess.call(["ifconfig", interface, 'promisc'])

	print "[     links][00000000] Sniffing on", interface, "on"

	try:
	    sniff(iface=interface, filter="port 80 or port 443", store=0, prn=link_analyzer)
	except:
	    return

	return

if __name__ == "__main__":
	main()
