diff options
author | Luo Jinghua <sunmoon1997@gmail.com> | 2011-11-27 09:45:51 +0800 |
---|---|---|
committer | Luo Jinghua <sunmoon1997@gmail.com> | 2011-11-27 09:45:51 +0800 |
commit | 06e88b17406634e305eae249c925729af24e1509 (patch) | |
tree | 554efaf44744ab967002f241102510ce458f56c9 | |
parent | 3750d1933b328af0e81e9cdc2f15e1607f08478e (diff) |
tools: Added the tcplogviewer
-rwxr-xr-x | osframework/source/tools/tcplogviewer.py | 829 |
1 files changed, 829 insertions, 0 deletions
diff --git a/osframework/source/tools/tcplogviewer.py b/osframework/source/tools/tcplogviewer.py new file mode 100755 index 0000000..57066a9 --- /dev/null +++ b/osframework/source/tools/tcplogviewer.py @@ -0,0 +1,829 @@ +#!/usr/bin/env python +from Tkinter import * +from tkFileDialog import asksaveasfile, askdirectory + +import os +import select +import socket +import struct +import random +import time +import traceback + +from Tkinter import * +import os + +class NothingToReadException(Exception): + pass + +def hexdump(data): + length = len(data) + width = 16 + for idx in range(length): + mod = idx % width + rest = width - mod -1 + s_offset = idx - mod + e_offset = idx + # offset + if ( 0 == mod ): + print "0x%08x" % idx, + # hex dump + print hex(ord(data[idx]))[2:].zfill(2), + # padding dump + if ( idx == len(data)-1 ): + for k in range(rest): + print " ", + # char dump + if ( mod == (width-1) or idx == len(data)-1 ): + print "\t", + #print "from %d to %d" % (s_off, e_off) + for j in range(s_offset, e_offset+1): + if ( ord(data[j]) < 31 and ord(data[j]) > 126 ): + sys.stdout.write(data[j]) + else: + sys.stdout.write(".") + print "\n", + idx = idx+1 + +def currentTimeMillis(): + """Current system time in milliseconds""" + return long(time.time() * 1000) + + +class ServiceInfo(object): + def __init__(self, name, desc, type, address, port): + self.name = name + self.desc = desc + self.type = type + self.address = address + self.port = port + self.cookie = 0 + self.index = -1 + self.maxService = 0 + + def __eq__(self, rhs): + return self.name == rhs.name and self.desc == rhs.name and \ + self.type == rhs.type and self.address == rhs.address and \ + self.port == rhs.port + + def __cmp__(self, rhs): + if type(other) != ServiceInfo: + raise TypeError("ServiceInfo cannot be compared to %s"% str(type(other))) + return cmp(self.index, rhs.index) + + def __hash__(sefl): + hc = hash(self.name) + hc ^= hash(self.desc) + hc ^= hash(self.type) + hc ^= hash(self.address) + hc ^= hash(self.port) + return hc + + def __repr__(self): + return str((self.name, self.type, self.address, self.port)) + +class ServiceProvider(object): + def __init__(self, sm, addr): + self.sm = sm + self.addr = addr + self.name = '' + self.services = {} + self.cookie = 0 + self.pendingServices = [] + self.timestamp = 0 + self.updateTS = 0 + + def setCookie(self, cookie): + if self.cookie != cookie: + self.sm.sendQueryInfo(self.addr) + self.sm.sendQuery(self.addr) + self.cookie = cookie + + def getAddr(self): + return addr + + def getServices(self): + return self.services + + def servicesChanged(self): + old_services = self.services + self.services = {} + for si in self.pendingServices: + si.provider = self + self.services[si.name] = si + self.pendingServices = [] + self.sm.servicesChanged(self, old_services) + + def parseDict(self, msg, start, end = None): + if end is None: + end = len(msg) + + d = {} + while start < end: + l = struct.unpack('>H', msg[start:start + 2])[0] + key = msg[start + 2:start + 2 + l] + start += 2 + l + #print 'key:', key + + l = struct.unpack('>H', msg[start:start + 2])[0] + value = msg[start + 2:start + 2 + l] + start += 2 + l + #print 'value:', value + + d[key] = value + return d + + def parseService(self, msg): + #hexdump(msg) + #print + + start = 16 + maxService = struct.unpack('>I', msg[start:start + 4])[0] + if not maxService: + return None + start += 4 + + index = struct.unpack('>I', msg[start:start + 4])[0] + start += 4 + + d = self.parseDict(msg, start) + + name = d['name'] + desc = d['desc'] + srvtype = d['type'] + address = d['addr'] + port = d['port'] + + si = ServiceInfo(name, desc, srvtype, address, port) + si.index = index + si.maxService = maxService + return si + + def processInfoPacket(self, msg, tag, seqno): + cookie = struct.unpack('>I', msg[12:16])[0] + if self.cookie != cookie: + return + start = 16 + # skip the service count + start += 4 + d = self.parseDict(msg, start) + if 'name' in d: + name = d['name'] + if name != self.name: + self.name = name + self.sm.providerInfoChanged(self) + + def processPacket(self, msg, tag, seqno): + if tag == 'QRRP': + cookie = struct.unpack('>I', msg[12:16]) + service = self.parseService(msg) + if service: + service.cookie = cookie + if self.pendingServices: + ps = self.pendingServices[0] + if ps.cookie != cookie: + self.pendingServices.clear() + for si in self.pendingServices: + if si.index == service.index: + return + self.pendingServices.append(service) + if service.maxService == len(self.pendingServices): + self.servicesChanged() + elif service.maxService == len(self.pendingServices) - 1: + self.sm.sendQueryInfo(self.addr) + else: + self.pendingServices = [] + self.servicesChanged() + elif tag == 'QIRP': + self.processInfoPacket(msg, tag, seqno) + + def setTimestamp(self, timestamp): + self.timestamp = timestamp + + def isExpired(self, timestamp): + if timestamp - self.timestamp > 5000: + return True + return False + + def update(self, current): + if current - self.updateTS > 10000: + self.sm.sendQuery(self.addr) + self.sm.sendQueryInfo(self.addr) + self.updateTS = current + +class ServiceManager(object): + def __init__(self): + self.providers = {} + self.timestamp = 0 #currentTimeMillis() + self.sock = None + self.pkseq = 0 + self.addr = '224.0.0.251' + self.port = 11053 + self.callbacks = {} + + def start(self): + self.close() + self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + self.sock.setsockopt(socket.SOL_IP, socket.IP_MULTICAST_TTL, 255) + self.sock.setsockopt(socket.SOL_IP, socket.IP_MULTICAST_LOOP, 1) + try: + self.sock.bind(('', 0)) + except: + traceback.print_exc(); + self.sock.setsockopt(socket.SOL_IP, socket.IP_MULTICAST_IF, + socket.inet_aton('0.0.0.0')) + self.sock.setsockopt(socket.SOL_IP, socket.IP_ADD_MEMBERSHIP, + socket.inet_aton(self.addr) + socket.inet_aton('0.0.0.0')) + self.pkseq = 0 + + def close(self): + try: + self.sock.close(); + except: + self.sock = None + + def getProviders(self): + return self.providers + + def bind(self, event, callback, replace = False): + valid_events = ( + '<providersAdded>', + '<providersRemoved>', + '<providerInfoChanged>', + '<servicesChanged>', + ) + if event not in valid_events: + raise Exception, 'No such event' + if not event in self.callbacks: + self.callbacks[event] = [] + callbacks = self.callbacks[event] + if replace: + callbacks.clear() + callbacks.append(callback) + + def emitEvents(self, event, *args, **kwargs): + if not event in self.callbacks: + return + callbacks = self.callbacks[event] + try: + for callback in callbacks: + callback(*args, **kwargs) + except: + traceback.print_exc() + + def providersAdded(self, providers): + self.emitEvents('<providersAdded>', providers) + + ## print 'Provider added', providers + + def providersRemoved(self, providers): + self.emitEvents('<providersRemoved>', providers) + + ## print 'Provider removed', providers + + def providerInfoChanged(self, provider): + self.emitEvents('<providerInfoChanged>', provider) + + def servicesChanged(self, provider, oldServices = {}): + self.emitEvents('<servicesChanged>', provider, oldServices) + + ## print provider.addr, 'servicesChanged()' + ## services = provider.getServices() + ## print 'Number of services:', len(services) + ## i = 0 + ## keys = services.keys() + ## keys.sort() + ## for key in keys: + ## si = services[key] + ## print '%3d) %s: %s %s:%s' % (i, si.name, si.type, si.address, si.port) + ## i += 1 + ## print + + def send(self, packet, addr = None): + #print 'Sending a packet' + self.pkseq += 1 + if addr is None: + addr = (self.addr, self.port) + self.sock.sendto(packet, 0, addr) + + def broadcastEcho(self): + # 4b tag 'ECHO' + # 4b seqno + # 4b payload length 0 + pk = 'ECHO' + pk += struct.pack('>I', self.pkseq) + pk += struct.pack('>I', 0) + for i in range(10): + addr = (self.addr, self.port + i) + self.send(pk, addr) + + def sendQueryInfo(self, addr): + #print 'Sending a query info packet to', addr + #print + + # 4b tag 'QRIF' + # 4b seqno + # 4b payload length 0 + pk = 'QRIF' + pk += struct.pack('>I', self.pkseq) + pk += struct.pack('>I', 0) + self.send(pk, addr) + + def sendQuery(self, addr): + #print 'Sending query packet to', addr + #print + + # 4b tag 'QURY' + # 4b seqno + # 4b payload length 0 + pk = 'QURY' + pk += struct.pack('>I', self.pkseq) + pk += struct.pack('>I', 0) + self.send(pk, addr) + + def processEchoReplyPacket(self, msg, tag, seqno, addr): + (cookie,) = struct.unpack('>I', msg[12:16]) + #print 'cookie:', cookie, addr, len(msg) + changed = False + if addr not in self.providers: + self.providers[addr] = ServiceProvider(self, addr) + self.providers[addr].setTimestamp(currentTimeMillis()) + changed = True + self.providers[addr].setCookie(cookie) + if changed: + self.providersAdded([self.providers[addr]]) + + def processQueryReplyPacket(self, msg, tag, seqno, addr): + #print 'Received query reply packet from', addr + if addr in self.providers: + self.providers[addr].processPacket(msg, tag, seqno) + + def processQueryInfoReplyPacket(self, msg, tag, seqno, addr): + #print 'Received query reply packet from', addr + if addr in self.providers: + self.providers[addr].processPacket(msg, tag, seqno) + + def processPacket(self, msg, addr): + if addr in self.providers: + self.providers[addr].setTimestamp(currentTimeMillis()) + + tag = msg[0:4] + (seqno,) = struct.unpack('>I', msg[4:8]) + #print tag, seqno, addr, len(msg) + if tag == 'ECRP': + self.processEchoReplyPacket(msg, tag, seqno, addr) + elif tag == 'QRRP': + self.processQueryReplyPacket(msg, tag, seqno, addr) + elif tag == 'QIRP': + self.processQueryInfoReplyPacket(msg, tag, seqno, addr) + else: + print 'Unhandled packet', tag, seqno + + def processPackets(self): + while self.sock: + ret = select.select([self.sock], [], [], 0) + if not ret[0]: + return + msg, addr = self.sock.recvfrom(512) + self.processPacket(msg, addr) + + def update(self): + if not self.sock: + return + current = currentTimeMillis() + if current - self.timestamp > 2000: + self.broadcastEcho() + self.timestamp = current + self.processPackets() + + ### removed expired providers + expired = [] + for addr in self.providers: + provider = self.providers[addr] + if provider.isExpired(current): + expired.append(addr) + + providers = [] + for addr in expired: + providers.append(self.providers.pop(addr)) + + if providers: + self.providersRemoved(providers) + + for addr in self.providers: + provider = self.providers[addr] + provider.update(current) + +class Dialog(Toplevel): + def __init__(self, parent, title = None): + Toplevel.__init__(self, parent) + self.transient(parent) + + if title: + self.title(title) + + self.parent = parent + self.result = None + + body = Frame(self) + self.initial_focus = self.body(body) + body.pack(padx=5, pady=5) + + self.buttonbox() + self.grab_set() + + if not self.initial_focus: + self.initial_focus = self + + self.protocol("WM_DELETE_WINDOW", self.cancel) + self.geometry("+%d+%d" % (parent.winfo_rootx() + 50, + parent.winfo_rooty() + 50)) + + self.initial_focus.focus_set() + + def run(self): + self.wait_window(self) + return self.result + + # + # construction hooks + def body(self, master): + # create dialog body. return widget that should have + # initial focus. this method should be overridden + pass + + def buttonbox(self): + # add standard button box. override if you don't want the + # standard buttons + box = Frame(self) + + w = Button(box, text="OK", width=10, command=self.ok, default=ACTIVE) + w.pack(side=LEFT, padx=5, pady=5) + w = Button(box, text="Cancel", width=10, command=self.cancel) + w.pack(side=LEFT, padx=5, pady=5) + + self.bind("<Return>", self.ok) + self.bind("<Escape>", self.cancel) + + box.pack() + + # + # standard button semantics + def ok(self, event=None): + if not self.validate(): + self.initial_focus.focus_set() # put focus back + return + + self.withdraw() + self.update_idletasks() + + self.apply() + + self.cancel() + + def cancel(self, event=None): + # put focus back to the parent window + self.parent.focus_set() + self.destroy() + + # + # command hooks + def validate(self): + return 1 # override + + def apply(self): + pass # override + +class ButtonCallback(object): + def __init__(self, callback, *args): + self.callback = callback + self.args = args + + def __call__(self): + return self.callback(*self.args) + +class Application(Frame): + def __init__(self, master = None): + Frame.__init__(self, master) + self.lastSockAddr = () + self.sock = None + self.listen = False + self.after_id = -1 + self.pack(fill = BOTH, expand = 1) + self.createWidgets() + #self.createSocket() + self.serviceManager = ServiceManager() + self.serviceManager.start() + self.after(100, self.updateService) + self.logServers = [] + self.serviceManager.bind('<providersAdded>', self.providersAdded) + self.serviceManager.bind('<providersRemoved>', self.providersRemoved) + self.serviceManager.bind('<providerInfoChanged>', self.providerInfoChanged) + self.serviceManager.bind('<servicesChanged>', self.servicesChanged) + self.syncServerMenu() + + def __del__(self): + self.serviceManager.close() + + def createWidgets(self): + ### top frame + frame = LabelFrame(self, text = 'Configuration:') + frame.pack(side = TOP, padx = 4, pady = 4, fill = X) + + label = Label(frame, text = 'Server address:') + label.grid(column = 0, row = 0) + entry = Entry(frame) + entry.insert(END, 'localhost') + entry.grid(column = 1, row = 0) + self.addr_entry = entry + + label = Label(frame, text = 'Server port:') + label.grid(column = 2, row = 0) + entry = Entry(frame) + entry.insert(END, '11035') + entry.grid(column = 3, row = 0) + self.port_entry = entry + + menuBtn = Button(frame, text = 'Select a server', + command = self.selectServer) + menuBtn.grid(column = 4, row = 0) + self.menuBtn = menuBtn + + menu = Menu(self.master, tearoff = 0) + self.serverMenu = menu + + buttons = [ + ['Start/Stop', 'Clear', 'Save to...'], + ] + + frame = LabelFrame(self, text = 'Controll:') + frame.pack(side = TOP, padx = 4, pady = 4, fill = X) + + self.buttons = {} + i = 0 + for row in buttons: + j = 0 + for btn in row: + if btn: + button = Button(frame, text = btn, + command = ButtonCallback(self.buttonPressed, btn), + width = 8) + self.buttons[btn] = button + else: + button = Button(frame, text = " ", width = 8) + button.config(state = DISABLED) + button.grid(column = j, row = i) + j += 1 + i += 1 + + frame = LabelFrame(self, text = "Log:") + frame.pack(side = BOTTOM, padx = 4, pady = 4, fill = BOTH, expand = 1) + + txt = Text(frame, bg = "white") + txt.pack(side = LEFT, padx = 4, pady = 4, fill = BOTH, expand = 1) + self.logText = txt + + scrollbar = Scrollbar(frame) + scrollbar.pack(side = RIGHT, fill = Y) + scrollbar.config(command = txt.yview) + txt.config(yscrollcommand = scrollbar.set) + self.txtScrollbar = scrollbar + self.logText.yview(END) + + def createSocket(self): + host, port = self.getAddress() + if self.sock and self.lastSockAddr == (host, port): + return + + self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + #self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + try: + self.sock.connect((host, port)) + except: + traceback.print_exc() + self.sock.close() + self.sock = None + self.listen = False + self.appendLog('Failed to onnect to %s:%d\n' % (str(host), port)) + self.after_id = -1 + return + + self.after_id = self.after(10, self.updateLog) + self.appendLog('Connected to %s:%d\n' % (str(host), port)) + + def updateService(self): + try: + self.serviceManager.update() + except: + traceback.print_exc() + self.after(100, self.updateService) + + def getAddress(self): + host = self.addr_entry.get() + port = self.port_entry.get() + if not host: + host = 'localhost' + if not port: + port = '11035' + return (host, int(port)) + + def appendLog(self, msg): + self.logText.config(state = NORMAL) + self.logText.insert(END, msg) + self.logText.config(state = DISABLED) + if self.txtScrollbar.get()[1] == 1.0: + self.logText.yview(END) + + def getLevelName(self, level): + level_map = { + 0 : 'DEBUG', + 1 : 'INFO', + 2 : 'WARN', + 3 : 'ERROR' + } + + if level in level_map: + return level_map[level] + return 'UNKNOWN' + + def read(self, sock, pklen): + payload = '' + while len(payload) != pklen: + ret = sock.recv(pklen - len(payload)) + if not ret: + raise NothingToReadException, 'Nothing to read' + payload += ret + return payload + + def readLogPacket(self, sock, payload): + pid, timestamp, level, taglen, bodylen = struct.unpack(">IIHHI", payload[0:16]) + start = 4 + 4 + 2 + 2 + 4 + tag = payload[start:start + taglen] + msg = payload[start + taglen:] + levelName = self.getLevelName(level) + peer = sock.getpeername() + self.appendLog('%s:%s: %d %d.%03d %s %s: %s\n' % \ + (peer[0], peer[1], pid, timestamp / 1000, timestamp % 1000, + levelName, tag, msg)) + + def readPacket(self, sock): + import struct + pktag = self.read(sock, 4) + pklenstr = self.read(sock, 4) + pklen = struct.unpack('>I', pklenstr)[0] + assert (pklen > 8) + payload = self.read(sock, pklen - 8) + if pktag == 'LGBD': + self.readLogPacket(sock, payload) + + def closeLog(self): + if self.sock: + self.sock.close() + self.sock = None + self.listen = False + self.appendLog('Connection closed\n') + + def updateLog(self): + count = 0 + while self.sock and count < 50: + count += 1 + ret = select.select([self.sock], [], [self.sock], 0) + if not ret[0] and not ret[1] and not ret[2]: + break + if ret and ret[2]: + self.closeLog() + elif ret and ret[0]: + try: + self.readPacket(self.sock) + except: + traceback.print_exc() + self.closeLog() + + + if self.listen: + self.after_id = self.after(10, self.updateLog) + else: + self.after_id = -1 + + def filterService(self, services): + for s in services: + si = services[s] + if si.name == 'sexytcplog' and si.type == 'tcp': + return si + return None + + def providersAdded(self, providers): + for provider in providers: + print 'ProvidersAdded:', provider.addr + srv = self.filterService(provider.getServices()) + if srv and srv not in self.logServers: + self.logServers.append(srv) + print + + self.syncServerMenu() + + def providersRemoved(self, providers): + for provider in providers: + print 'ProvidersRemoved:', provider.addr + srv = self.filterService(provider.getServices()) + if srv and srv in self.logServers: + self.logServers.remove(srv) + self.syncServerMenu() + + def providerInfoChanged(self, provider): + self.syncServerMenu() + + def servicesChanged(self, provider, oldServices): + print 'ServicesChanged:', provider.addr + + srv = self.filterService(oldServices) + if srv: + self.logServers.remove(srv) + srv = self.filterService(provider.getServices()) + if srv: + self.logServers.append(srv) + + print self.logServers + print + + self.syncServerMenu() + + def buttonPressed(self, btn): + if btn == 'Clear': + self.clearLog() + elif btn == 'Start/Stop': + self.startLog() + else: + self.saveLog() + + def startLog(self): + self.listen = not self.listen + if self.sock: + try: + self.sock.shutdown(2) + except: + pass + try: + self.sock.close() + except Exception, e: + pass + self.sock = None + if self.listen: + self.createSocket() + else: + self.after_cancel(self.after_id) + self.after_id = -1 + self.appendLog("Stopped\n") + + def clearLog(self): + self.logText.config(state = NORMAL) + self.logText.delete(1.0, END) + self.logText.config(state = DISABLED) + + def saveLog(self): + f = asksaveasfile() + if not f: + return + + log = self.logText.get('0.0', END) + f.write(log) + f.close() + + def syncServerMenu(self): + if not self.logServers: + self.serverMenu.unpost() + self.serverMenu.delete(0, END) + self.menuBtn.config(state = DISABLED) + else: + self.menuBtn.config(state = NORMAL) + self.serverMenu.delete(0, END) + self.serverMenu.add_command(label = 'Cancel') + for srv in self.logServers: + name = srv.provider.name or 'Unknown' + addr = srv.provider.addr[0] + port = srv.port + text = '%s (%s:%s)' % (name, str(addr), str(port)) + self.serverMenu.add_command(label = text, + command = + ButtonCallback(self.menuSelectServer, srv)); + + def menuSelectServer(self, srv): + if not srv: + return + + addr = srv.provider.addr[0] + port = srv.port + self.addr_entry.delete(0, END) + self.addr_entry.insert(0, addr) + self.port_entry.delete(0, END) + self.port_entry.insert(0, port) + + def selectServer(self): + self.serverMenu.unpost() + self.syncServerMenu() + self.serverMenu.post(self.menuBtn.winfo_rootx(), + self.menuBtn.winfo_rooty()) + +app = Application() +app.master.title("Log viewer for SexyFramework") +app.mainloop() |