netarch/ip.py

768 lines
23 KiB
Python
Raw Normal View History

2018-07-09 17:14:19 -06:00
#! /usr/bin/python3
2007-11-12 21:11:25 -07:00
2008-07-08 17:52:28 -06:00
## IP resequencing + protocol reversing skeleton
2018-07-09 17:14:19 -06:00
## 2008, 2018 Neale Pickett
2008-07-08 17:52:28 -06:00
2007-12-12 11:07:49 -07:00
import struct
import socket
import warnings
2008-01-10 17:53:11 -07:00
import heapq
import time
2018-07-18 13:25:24 -06:00
import io
2011-07-02 15:04:55 -06:00
try:
import pcap
except ImportError:
2018-07-10 12:13:07 -06:00
warnings.warn("Using slow pure-python pcap library")
2020-09-21 14:00:59 -06:00
from . import py_pcap as pcap
import os
import cgi
2018-07-16 14:07:31 -06:00
import urllib.parse
2020-09-21 14:00:59 -06:00
from .hexdump import hexdump
from .trilobytes import TriloBytes
def unpack(fmt, buf):
"""Unpack buf based on fmt, return the remainder."""
size = struct.calcsize(fmt)
vals = struct.unpack(fmt, bytes(buf[:size]))
return vals + (buf[size:],)
2007-12-12 11:07:49 -07:00
def unpack_nybbles(byte):
return (byte >> 4, byte & 0x0F)
2011-07-02 15:04:55 -06:00
transfers = os.environ.get('TRANSFERS', 'transfers')
IP = 0x0800
ARP = 0x0806
VLAN = 0x8100
ICMP = 1
2007-12-30 23:30:23 -07:00
TCP = 6
UDP = 17
def str_of_eth(d):
return ':'.join([('%02x' % ord(x)) for x in d])
2007-12-12 11:07:49 -07:00
class Frame:
2008-07-14 17:26:02 -06:00
"""Turn an ethernet frame into relevant parts"""
def __init__(self, pkt):
((self.time, self.time_usec, _), frame) = pkt
2007-12-12 12:22:20 -07:00
# Ethernet
2007-12-12 11:07:49 -07:00
(self.eth_dhost,
self.eth_shost,
self.eth_type,
p) = unpack('!6s6sH', frame)
if self.eth_type == VLAN:
_, self.eth_type, p = unpack('!HH', p)
if self.eth_type == ARP:
2008-07-14 17:26:02 -06:00
# ARP
self.name, self.protocol = ('ARP', ARP)
2008-07-14 17:26:02 -06:00
(self.ar_hrd,
self.ar_pro,
self.ar_hln,
self.ar_pln,
self.ar_op,
self.ar_sha,
self.ar_sip,
self.ar_tha,
self.ar_tip,
p) = unpack('!HHBBH6si6si', p)
self.saddr = self.ar_sip
self.daddr = self.ar_tip
elif self.eth_type == IP:
2008-07-14 17:26:02 -06:00
# IP
(self.ihlvers,
self.tos,
self.tot_len,
self.id,
2008-07-14 17:26:02 -06:00
self.frag_off,
self.ttl,
self.protocol,
self.check,
self.saddr,
self.daddr,
p) = unpack("!BBHHHBBHii", p)
if self.protocol == TCP:
self.name = 'TCP/IP'
(self.sport,
self.dport,
self.seq,
self.ack,
x2off,
self.flags,
self.win,
self.sum,
self.urp,
p) = unpack("!HHLLBBHHH", p)
(self.off, th_x2) = unpack_nybbles(x2off)
opt_length = self.off * 4
self.options, p = p[:opt_length - 20], p[opt_length - 20:]
self.payload = p[:self.tot_len - opt_length - 20]
elif self.protocol == UDP:
self.name = 'UDP/IP'
(self.sport,
self.dport,
self.ulen,
self.sum,
p) = unpack("!HHHH", p)
self.payload = p[:self.ulen - 8]
elif self.protocol == ICMP:
self.name = 'ICMP/IP'
self.sport = self.dport = None
(self.type,
self.code,
self.cheksum,
self.id,
self.seq,
p) = unpack('!BBHHH', p)
self.payload = p[:self.tot_len - 8]
else:
self.name = 'IP Protocol %d' % self.protocol
self.sport = self.dport = None
self.payload = p
# Nice formatting
self.src = (self.saddr, self.sport)
self.dst = (self.daddr, self.dport)
# This hash is the same for both sides of the transaction
self.hash = (self.saddr ^ (self.sport or 0)
^ self.daddr ^ (self.dport or 0))
else:
2008-07-14 17:26:02 -06:00
self.name = 'Ethernet type %d' % self.eth_type
2008-07-21 17:52:35 -06:00
self.protocol = None
2007-12-12 11:07:49 -07:00
2007-12-12 12:22:20 -07:00
def get_src_addr(self):
2018-07-10 12:13:07 -06:00
if not hasattr(self, "_src_addr"):
saddr = struct.pack('!i', self.saddr)
self._src_addr = socket.inet_ntoa(saddr)
return self._src_addr
2007-12-12 12:22:20 -07:00
src_addr = property(get_src_addr)
def get_dst_addr(self):
2018-07-10 12:13:07 -06:00
if not hasattr(self, "_dst_addr"):
daddr = struct.pack('!i', self.daddr)
self._dst_addr = socket.inet_ntoa(daddr)
return self._dst_addr
2007-12-12 12:22:20 -07:00
dst_addr = property(get_dst_addr)
2007-12-12 11:07:49 -07:00
def __repr__(self):
2020-09-21 14:00:59 -06:00
if self.eth_type == ARP:
return '<Frame %s %s(%s) -> %s(%s)>' % (self.name,
str_of_eth(self.ar_sha),
self.src_addr,
str_of_eth(self.ar_tha),
self.dst_addr)
else:
return ('<Frame %s %s:%r(%08x) -> %s:%r(%08x) length %d>' %
(self.name,
self.src_addr, self.sport, self.seq,
self.dst_addr, self.dport, self.ack,
len(self.payload)))
2008-07-14 17:26:02 -06:00
2009-08-18 12:22:53 -06:00
class TCP_Recreate:
2011-07-02 15:04:55 -06:00
closed = True
def __init__(self, pcap, src, dst, timestamp):
2009-08-18 12:22:53 -06:00
self.pcap = pcap
self.src = (socket.inet_aton(src[0]), src[1])
self.dst = (socket.inet_aton(dst[0]), dst[1])
self.sid = self.did = 0
2011-07-02 15:04:55 -06:00
self.sseq = self.dseq = 1
2009-08-18 12:22:53 -06:00
self.lastts = 0
self.write_header()
2011-07-02 15:04:55 -06:00
self.handshake(timestamp)
2009-08-18 12:22:53 -06:00
def write_header(self):
p = '\0\0\0\0\0\0\0\0\0\0\0\0\xfe\xed'
self.pcap.write(((0,0,len(p)), p))
def packet(self, cli, payload, flags=0):
if cli:
sip, sport = self.src
dip, dport = self.dst
id = self.sid
self.sid += 1
seq = self.sseq
self.sseq += len(payload)
if flags & (SYN|FIN):
self.sseq += 1
ack = self.dseq
else:
sip, sport = self.dst
dip, dport = self.src
id = self.did
self.did += 1
seq = self.dseq
self.dseq += len(payload)
if flags & (SYN|FIN):
self.dseq += 1
ack = self.sseq
2011-07-02 15:04:55 -06:00
if not (flags & ACK):
ack = 0
2009-08-18 12:22:53 -06:00
ethhdr = struct.pack('!6s6sH',
'\x11\x11\x11\x11\x11\x11',
'\x22\x22\x22\x22\x22\x22',
IP)
iphdr = struct.pack('!BBHHHBBH4s4s',
0x45, # Version, Header length/32
0, # Differentiated services / ECN
40+len(payload), # total size
id,
0x4000, # Don't fragment, no fragment offset
6, # TTL
TCP, # Protocol
0, # Header checksum
sip,
dip)
2013-01-30 15:08:04 -07:00
shorts = struct.unpack('!HHHHHHHHHH', iphdr)
2011-07-02 15:04:55 -06:00
shsum = sum(shorts)
2013-01-30 15:08:04 -07:00
ipsum = struct.pack('!H', ((shsum + (shsum >> 16)) & 0xffff) ^ 0xffff)
2009-08-18 12:22:53 -06:00
iphdr = iphdr[:10] + ipsum + iphdr[12:]
tcphdr = struct.pack('!HHLLBBHHH',
sport,
dport,
seq, # Sequence number
ack, # Acknowledgement number
0x50, # Data offset
flags, # Flags
2011-07-02 15:04:55 -06:00
0xff00, # Window size
2009-08-18 12:22:53 -06:00
0, # Checksum
0) # Urgent pointer
2018-07-10 12:13:07 -06:00
return ethhdr + iphdr + tcphdr + bytes(payload)
2009-08-18 12:22:53 -06:00
def write_pkt(self, timestamp, cli, payload, flags=0):
p = self.packet(cli, payload, flags)
frame = (timestamp + (len(p),), p)
self.pcap.write(frame)
self.lastts = timestamp
def write(self, timestamp, cli, data):
2011-07-02 15:04:55 -06:00
while data:
d, data = data[:0xff00], data[0xff00:]
self.write_pkt(timestamp, cli, d, ACK)
2009-08-18 12:22:53 -06:00
def handshake(self, timestamp):
self.write_pkt(timestamp, True, '', SYN)
self.write_pkt(timestamp, False, '', SYN|ACK)
#self.write_pkt(timestamp, True, '', ACK)
def close(self):
self.write_pkt(self.lastts, True, '', FIN|ACK)
self.write_pkt(self.lastts, False, '', FIN|ACK)
self.write_pkt(self.lastts, True, '', ACK)
def __del__(self):
if not self.closed:
self.close()
2008-01-18 19:09:09 -07:00
2008-01-01 18:55:24 -07:00
FIN = 1
SYN = 2
RST = 4
PSH = 8
ACK = 16
2008-01-10 17:53:11 -07:00
class TCP_Resequence:
2007-12-30 23:30:23 -07:00
"""TCP session resequencer.
>>> p = pcap.open('whatever.pcap')
2008-01-10 17:53:11 -07:00
>>> s = TCP_Resequence()
2007-12-30 23:30:23 -07:00
>>> while True:
... pkt = p.read()
... if not pkt:
... break
... f = Frame(pkt)
... r = s.handle(f)
... if r:
... print ('chunk', r)
2007-11-12 21:11:25 -07:00
This returns things in sequence. So you get both sides of the
conversation in the order that they happened.
Doesn't (yet) handle fragments or dropped packets. Does handle out
of order packets.
"""
def __init__(self):
2007-11-12 21:11:25 -07:00
self.cli = None
self.srv = None
2008-07-25 17:38:30 -06:00
self.lastack = [None, None]
self.first = None
2007-11-12 21:11:25 -07:00
self.pending = [{}, {}]
self.closed = [False, False]
self.midstream = False
self.hash = 0
self.handle = self.handle_handshake
2011-07-02 15:04:55 -06:00
def bundle_pending(self, xdi, pkt, seq):
"""Bundle up any pending packets.
Called when a packet comes from a new direction, this is the thing responsible for
replaying TCP as a back-and-forth conversation.
"""
pending = self.pending[xdi]
# Get a sorted list of sequence numbers
2018-07-10 12:13:07 -06:00
keys = sorted(pending)
2011-07-02 15:04:55 -06:00
# Build up return value
2018-07-10 12:13:07 -06:00
gs = TriloBytes()
2011-07-02 15:04:55 -06:00
if keys:
2018-07-10 12:13:07 -06:00
first = pending[keys[0]]
2011-07-02 15:04:55 -06:00
else:
2018-07-10 12:13:07 -06:00
first = None
2011-07-02 15:04:55 -06:00
# Fill in gs with our frames
for key in keys:
if key >= pkt.ack:
# In the future
break
frame = pending[key]
if key > seq:
# Dropped frame(s)
2018-07-10 12:13:07 -06:00
dropped = key - seq
if dropped > 6000:
print("Gosh, %d dropped octets sure is a lot!" % (dropped))
gs += [None] * dropped
2011-07-02 15:04:55 -06:00
seq = key
if key == seq:
# Default
2018-07-10 12:13:07 -06:00
gs += frame.payload
2011-07-02 15:04:55 -06:00
seq += len(frame.payload)
del pending[key]
elif key < seq:
# Hopefully just a retransmit. Anyway we've already
# claimed to have data (or a drop) for this packet.
del pending[key]
if frame.flags & (FIN):
seq += 1
if frame.flags & (FIN | ACK) == FIN | ACK:
self.closed[xdi] = True
if self.closed == [True, True]:
self.handle = self.handle_drop
if seq != pkt.ack:
# Drop at the end
2018-07-10 12:13:07 -06:00
dropped = pkt.ack - seq
if dropped > 6000:
2018-07-09 17:14:19 -06:00
print('Large drop at end of session!')
print(' %s' % ((pkt, pkt.time),))
print(' %x %x' % (pkt.ack, seq))
2018-07-10 12:13:07 -06:00
gs += [None] * dropped
2011-07-02 15:04:55 -06:00
2018-07-10 12:13:07 -06:00
return (xdi, first, gs)
2011-07-02 15:04:55 -06:00
def handle_handshake(self, pkt):
if not self.first:
self.first = pkt
self.hash = pkt.hash
2008-01-01 18:55:24 -07:00
if pkt.flags == SYN:
self.cli, self.srv = pkt.src, pkt.dst
2008-01-01 18:55:24 -07:00
elif pkt.flags == (SYN | ACK):
2008-07-21 17:52:35 -06:00
#assert (pkt.src == (self.srv or pkt.src))
self.cli, self.srv = pkt.dst, pkt.src
self.lastack = [pkt.seq + 1, pkt.ack]
self.handle_packet(pkt)
2008-01-01 18:55:24 -07:00
elif pkt.flags == ACK:
2008-07-21 17:52:35 -06:00
#assert (pkt.src == (self.cli or pkt.src))
self.cli, self.srv = pkt.src, pkt.dst
self.lastack = [pkt.ack, pkt.seq]
self.handle = self.handle_packet
self.handle(pkt)
else:
2008-01-01 14:47:06 -07:00
# In the middle of a session, do the best we can
warnings.warn('Starting mid-stream')
self.midstream = True
2008-01-01 14:47:06 -07:00
self.cli, self.srv = pkt.src, pkt.dst
self.lastack = [pkt.ack, pkt.seq]
2008-01-01 14:47:06 -07:00
self.handle = self.handle_packet
self.handle(pkt)
2011-07-02 15:04:55 -06:00
def handle_packet(self, pkt):
2008-01-01 14:47:06 -07:00
# Which way is this going? 0 == from client
idx = int(pkt.src == self.srv)
xdi = 1 - idx
2011-07-02 15:04:55 -06:00
if pkt.flags & RST:
# Handle RST before wonky sequence numbers screw up algorithm
self.closed = [True, True]
self.handle = self.handle_drop
return self.bundle_pending(xdi, pkt, self.lastack[idx])
else:
# Stick it into pending
self.pending[idx][pkt.seq] = pkt
# Does this ACK after the last output sequence number?
seq = self.lastack[idx]
self.lastack[idx] = pkt.ack
if pkt.ack > seq:
return self.bundle_pending(xdi, pkt, seq)
def handle_drop(self, pkt):
2007-11-12 21:11:25 -07:00
"""Warn about any unhandled packets"""
2009-06-03 11:14:54 -06:00
if pkt.flags & SYN:
# Re-using ports!
self.__init__()
return self.handle(pkt)
2008-01-01 18:55:24 -07:00
if pkt.payload:
warnings.warn('Spurious frame after shutdown: %r %d' % (pkt, pkt.flags))
2009-06-03 11:14:54 -06:00
hexdump(pkt.payload)
2007-11-12 21:11:25 -07:00
class Dispatch:
def __init__(self, *filenames):
self.pcs = {}
2007-12-30 23:30:23 -07:00
self.sessions = {}
self.tops = []
self.last = None
2008-01-10 17:53:11 -07:00
for fn in filenames:
self.open(fn)
2008-01-10 17:53:11 -07:00
def open(self, filename, literal=False):
if not literal:
parts = filename.split(':::')
fn = parts[0]
2018-07-10 12:13:07 -06:00
fd = open(fn, "rb")
pc = pcap.open(fd)
if len(parts) > 1:
pos = int(parts[1])
fd.seek(pos)
self._read(pc, fn, fd)
else:
2018-07-10 12:13:07 -06:00
fd = open(filename, "rb")
pc = pcap.open(fd)
self._read(pc, filename, fd)
2008-01-10 17:53:11 -07:00
def _read(self, pc, filename, fd):
pos = fd.tell()
2008-08-21 08:58:32 -06:00
f = pc.read()
if f:
heapq.heappush(self.tops, (f, pc, filename, fd, pos))
2008-01-10 17:53:11 -07:00
def __iter__(self):
while self.tops:
2008-08-21 08:58:32 -06:00
f, pc, filename, fd, pos = heapq.heappop(self.tops)
if not self.last:
self.last = (filename, pos)
2008-08-21 08:58:32 -06:00
frame = Frame(f)
if frame.protocol == TCP:
# compute TCP session hash
tcp_sess = self.sessions.get(frame.hash)
if not tcp_sess:
tcp_sess = TCP_Resequence()
self.sessions[frame.hash] = tcp_sess
ret = tcp_sess.handle(frame)
if ret:
yield frame.hash, ret
self.last = None
self._read(pc, filename, fd)
2007-12-11 17:20:54 -07:00
##
## Binary protocol stuff
##
2007-12-11 17:20:54 -07:00
2008-07-08 17:52:28 -06:00
class NeedMoreData(Exception):
pass
2018-07-09 17:14:19 -06:00
class Packet:
"""Base class for a packet from a binary protocol.
2007-12-11 17:20:54 -07:00
This is a base class for making protocol reverse-engineering easier.
"""
opcodes = {}
def __init__(self, session, firstframe=None):
self.session = session
self.firstframe = firstframe
self.opcode = None
self.opcode_desc = None
self.parts = []
self.params = {}
self.payload = None
2009-08-18 12:22:53 -06:00
self.subpackets = []
def __repr__(self):
r = '<%s packet opcode=%s' % (self.__class__.__name__, self.opcode)
if self.opcode_desc:
r += '(%s)' % self.opcode_desc
keys = self.params.keys()
keys.sort()
for k in keys:
r += ' %s=%s' % (k, self.params[k])
r += '>'
return r
2007-12-11 17:20:54 -07:00
## Dict methods
def __setitem__(self, k, v):
self.params[k] = v
def __getitem__(self, k):
return self.params[k]
def __contains__(self, k):
return k in self.params
def __iter__(self):
return self.params.__iter__()
def keys(self):
return self.params.keys()
##
def assert_in(self, a, *b):
if len(b) == 1:
assert a == b[0], ('%r != %r' % (a, b[0]))
else:
assert a in b, ('%r not in %r' % (a, b))
def show(self):
2018-07-09 17:14:19 -06:00
print('%s %3s: %s' % (self.__class__.__name__,
self.opcode,
2018-07-09 17:14:19 -06:00
self.opcode_desc))
if self.firstframe:
2018-07-09 17:14:19 -06:00
print(' %s:%d -> %s:%d (%s.%06dZ)' % (self.firstframe.src_addr,
2008-08-21 08:58:32 -06:00
self.firstframe.sport,
self.firstframe.dst_addr,
self.firstframe.dport,
time.strftime('%Y-%m-%dT%T', time.gmtime(self.firstframe.time)),
2018-07-09 17:14:19 -06:00
self.firstframe.time_usec))
if self.parts:
dl = len(self.parts[-1])
p = []
2018-07-18 13:25:24 -06:00
xp = []
for x in self.parts[:-1]:
if x == dl:
p.append('%3d!' % x)
2018-07-18 13:25:24 -06:00
xp.append('%3x!' % x)
else:
p.append('%3d' % x)
2018-07-18 13:25:24 -06:00
xp.append('%3x' % x)
print(' parts: (%s) +%d octets' % (','.join(p), dl))
print(' 0xparts: (%s) +%x octets' % (','.join(xp), dl))
2018-07-10 12:13:07 -06:00
keys = sorted(self.params)
for k in keys:
2018-07-09 17:14:19 -06:00
print(' %12s: %s' % (k, self.params[k]))
2009-08-18 12:22:53 -06:00
if self.subpackets:
for p in self.subpackets:
p.show()
elif self.payload:
try:
2018-07-10 12:13:07 -06:00
hexdump(self.payload)
except AttributeError:
2018-07-09 17:14:19 -06:00
print(' payload: %r' % self.payload)
def decode(self, data):
"""Decode a chunk of data (possibly a TriloBytes).
Anything returned is not part of this packet and will be passed
in to a subsequent packet.
"""
self.parts = [data]
2008-07-21 17:52:35 -06:00
self.payload = data
return False
def handle(self, data):
"""Handle data from a Session class."""
data = self.decode(data)
2018-07-09 17:14:19 -06:00
if self.opcode != None:
2008-07-08 17:52:28 -06:00
try:
f = getattr(self, 'opcode_%s' % self.opcode)
except AttributeError:
f = self.opcode_unknown
if not self.opcode_desc and f.__doc__:
self.opcode_desc = f.__doc__.split('\n')[0]
f()
return data
2008-07-08 17:52:28 -06:00
def opcode_unknown(self):
"""Unknown opcode"""
2008-07-25 17:38:30 -06:00
raise AttributeError('Opcode %s unknown' % self.opcode)
2008-07-08 17:52:28 -06:00
class Session:
"""Base class for a binary protocol session."""
# Override this, duh
Packet = Packet
def __init__(self, frame):
self.firstframe = frame
self.lastframe = [None, None]
2011-07-02 15:04:55 -06:00
self.basename = os.path.join(transfers, frame.src_addr)
self.basename2 = os.path.join(transfers, frame.dst_addr)
2008-07-08 17:52:28 -06:00
self.pending = {}
self.count = 0
2008-07-25 17:38:30 -06:00
for d in (self.basename, self.basename2):
try:
os.makedirs(d)
except OSError:
pass
self.setup()
def setup(self):
"""Set things up."""
pass
2008-07-08 17:52:28 -06:00
2018-07-10 12:13:07 -06:00
def handle(self, is_srv, frame, data, lastpos):
"""Handle a data burst.
@param is_srv Is this from the server?
@param frame A frame associated with this packet, or None if it's all drops
2018-07-10 12:13:07 -06:00
@param data A TriloBytes of the data
@param lastpos Last position in the source file, for debugging
"""
if frame:
self.lastframe[is_srv] = frame
frame = self.lastframe[is_srv]
self.lastpos = lastpos
2008-07-08 17:52:28 -06:00
try:
saddr = frame.saddr
try:
2018-07-10 12:13:07 -06:00
(f, buf) = self.pending.pop(saddr)
except KeyError:
f = frame
2018-07-10 12:13:07 -06:00
buf = TriloBytes()
buf += data
try:
2018-07-10 12:13:07 -06:00
while buf:
p = self.Packet(self, f)
2018-07-10 12:13:07 -06:00
buf = p.handle(buf)
self.process(p)
except NeedMoreData:
2018-07-10 12:13:07 -06:00
self.pending[saddr] = (f, buf)
self.count += 1
except:
2018-07-09 17:14:19 -06:00
print('Lastpos: %r' % (lastpos,))
raise
def process(self, packet):
"""Process a packet.
When you first start out, this probably does exactly what you
want: print out packets as they come in. As you progress you'll
probably want to override it with something more sophisticated.
That will of course vary wildly between protocols.
"""
packet.show()
2008-07-08 17:52:28 -06:00
def done(self):
"""Called when all packets have been handled"""
return
2008-07-25 17:38:30 -06:00
def open_out(self, fn):
frame = self.firstframe
fn = '%d-%s~%d-%s~%d---%s' % (frame.time,
frame.src_addr, frame.sport,
frame.dst_addr, frame.dport,
2018-07-16 14:07:31 -06:00
urllib.parse.quote(fn, ''))
2008-07-25 17:38:30 -06:00
fullfn = os.path.join(self.basename, fn)
fullfn2 = os.path.join(self.basename2, fn)
2018-07-09 17:14:19 -06:00
print(' writing %s' % (fn,))
2018-07-16 14:07:31 -06:00
fd = open(fullfn, 'wb')
try:
2008-07-25 17:38:30 -06:00
os.unlink(fullfn2)
except OSError:
pass
2008-07-25 17:38:30 -06:00
os.link(fullfn, fullfn2)
return fd
2008-07-08 17:52:28 -06:00
def handle_packets(self, collection):
"""Handle a collection of packets"""
for chunk in resequence(collection):
self.handle(chunk)
self.done()
2008-07-21 17:52:35 -06:00
class HtmlSession(Session):
def __init__(self, frame):
Session.__init__(self, frame)
2018-07-18 13:25:24 -06:00
fd = self.open_out('session.html')
fbuf = io.BufferedWriter(fd, 1024)
self.sessfd = io.TextIOWrapper(fbuf, encoding="utf-8")
self.sessfd.write('''<?xml version="1.0" encoding="UTF-8"?>
2008-07-21 17:52:35 -06:00
<!DOCTYPE html
PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<title>%s</title>
<style type="text/css">
2011-07-02 15:04:55 -06:00
.time { float: right; margin-left: 1em; font-size: 75%%; }
.server { background-color: white; color: black; }
.client { background-color: #884; color: white; }
2008-07-21 17:52:35 -06:00
</style>
</head>
<body>
''' % self.__class__.__name__)
self.sessfd.write('<h1>%s</h1>\n' % self.__class__.__name__)
self.sessfd.write('<pre>')
2008-07-21 17:52:35 -06:00
self.srv = None
def __del__(self):
self.sessfd.write('</pre></body></html>')
2008-07-21 17:52:35 -06:00
def log(self, frame, payload, escape=True):
if escape:
2008-07-25 17:38:30 -06:00
p = cgi.escape(str(payload))
else:
p = payload
if not self.srv:
self.srv = frame.saddr
2008-07-21 17:52:35 -06:00
if frame.saddr == self.srv:
cls = 'server'
else:
cls = 'client'
2011-07-02 15:04:55 -06:00
if False:
self.sessfd.write('<span class="%s" title="%s(%s)">' % (cls, time.ctime(frame.time), frame.time))
else:
ts = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(frame.time))
self.sessfd.write('<span class="time %s">%s</span><span class="%s">' % (cls, ts, cls))
self.sessfd.write(p.replace('\r\n', '\n'))
self.sessfd.write('</span>')
2011-07-02 15:04:55 -06:00