#!/usr/bin/env python
# This file is part of Responder, a network take-over set of tools 
# created and maintained by Laurent Gaffie.
# email: laurent.gaffie@gmail.com
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
import sys
import os
import thread

BASEDIR = os.path.realpath(os.path.join(os.path.dirname(__file__), '..'))
sys.path.insert(0, BASEDIR)

from servers.Browser import WorkstationFingerPrint, RequestType, RAPThisDomain, RapFinger
from SocketServer import UDPServer, ThreadingMixIn, BaseRequestHandler
from threading import Lock
from utils import *

def ParseRoles(data):
	if len(data) != 4:
		return ''

	AllRoles = {
		'Workstation':           (ord(data[0]) >> 0) & 1,
		'Server':                (ord(data[0]) >> 1) & 1,
		'SQL':                   (ord(data[0]) >> 2) & 1,
		'Domain Controller':     (ord(data[0]) >> 3) & 1,
		'Backup Controller':     (ord(data[0]) >> 4) & 1,
		'Time Source':           (ord(data[0]) >> 5) & 1,
		'Apple':                 (ord(data[0]) >> 6) & 1,
		'Novell':                (ord(data[0]) >> 7) & 1,
		'Member':                (ord(data[1]) >> 0) & 1,
		'Print':                 (ord(data[1]) >> 1) & 1,
		'Dialin':                (ord(data[1]) >> 2) & 1,
		'Xenix':                 (ord(data[1]) >> 3) & 1,
		'NT Workstation':        (ord(data[1]) >> 4) & 1,
		'WfW':                   (ord(data[1]) >> 5) & 1,
		'Unused':                (ord(data[1]) >> 6) & 1,
		'NT Server':             (ord(data[1]) >> 7) & 1,
		'Potential Browser':     (ord(data[2]) >> 0) & 1,
		'Backup Browser':        (ord(data[2]) >> 1) & 1,
		'Master Browser':        (ord(data[2]) >> 2) & 1,
		'Domain Master Browser': (ord(data[2]) >> 3) & 1,
		'OSF':                   (ord(data[2]) >> 4) & 1,
		'VMS':                   (ord(data[2]) >> 5) & 1,
		'Windows 95+':           (ord(data[2]) >> 6) & 1,
		'DFS':                   (ord(data[2]) >> 7) & 1,
		'Local':                 (ord(data[3]) >> 6) & 1,
		'Domain Enum':           (ord(data[3]) >> 7) & 1,
	}

	return ', '.join(k for k,v in AllRoles.items() if v == 1)


class BrowserListener(BaseRequestHandler):
	def handle(self):
		data, socket = self.request

		lock = Lock()
		lock.acquire()

		DataOffset    = struct.unpack('<H',data[139:141])[0]
		BrowserPacket = data[82+DataOffset:]
		ReqType       = RequestType(BrowserPacket[0])

		Domain = Decode_Name(data[49:81])
		Name   = Decode_Name(data[15:47])
		Role1  = NBT_NS_Role(data[45:48])
		Role2  = NBT_NS_Role(data[79:82])
		Fprint = WorkstationFingerPrint(data[190:192])
		Roles  = ParseRoles(data[192:196])

		print text("[BROWSER] Request Type : %s" % ReqType)
		print text("[BROWSER] Address      : %s" % self.client_address[0])
		print text("[BROWSER] Domain       : %s" % Domain)
		print text("[BROWSER] Name         : %s" % Name)
		print text("[BROWSER] Main Role    : %s" % Role1)
		print text("[BROWSER] 2nd Role     : %s" % Role2)
		print text("[BROWSER] Fingerprint  : %s" % Fprint)
		print text("[BROWSER] Role List    : %s" % Roles)

		RAPThisDomain(self.client_address[0], Domain)

		lock.release()


class ThreadingUDPServer(ThreadingMixIn, UDPServer):
	def server_bind(self):
		self.allow_reuse_address = 1
		UDPServer.server_bind(self)

def serve_thread_udp_broadcast(host, port, handler):
	try:
		server = ThreadingUDPServer(('', port), handler)
		server.serve_forever()
	except:
		print "Error starting UDP server on port " + str(port) + ", check permissions or other servers running."

if __name__ == "__main__":
	try:
		print "Listening for BROWSER datagrams..."
		thread.start_new(serve_thread_udp_broadcast,('', 138,  BrowserListener))

		while True:
			time.sleep(1)

	except KeyboardInterrupt:
		sys.exit("\r Exiting...")
