1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
|
#!/usr/bin/env python
#
# Replay a communication, splitting data at a certain chunk sizes
# (defaults to 2). This can be used to test reassembly for example.
#
# Copyright (C) 2014 Peter Wu <peter@lekensteyn.nl>
# Usage (assuming a capture file with TCP stream 0 at loopback interface lo)
# dumpcap -i lo -w split.pcapng
# ./replay-chunks.py old-capture.pcapng
# Run ./replay-chunks.py --help for details
import socket
import sys
import os
from argparse import ArgumentParser
from subprocess import PIPE
from subprocess import Popen as _Popen
if hasattr(_Popen, '__exit__'):
Popen = _Popen
else:
class PopenClosable(_Popen):
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, traceback):
self.stdout.close()
Popen = PopenClosable
try:
from subprocess import DEVNULL
except ImportError:
# Python < 3.3 compatibility
DEVNULL = open(os.devnull, 'w')
state = 'init'
def _is_marker(line):
return all(c == '=' for c in line.strip())
def _dumpbytes(data):
return ''.join([
chr(x) if x >= 0x20 and x < 0x7f else '.'
for x in data
])
class FollowParser(object):
def __init__(self, chunk_size=2):
self.state = self.state_find_begin
self.addr = None
self.sock_client = None
self.sock_server = None
self.chunk_size = chunk_size
def add_data(self, data, is_reply):
sock = self.sock_server if is_reply else self.sock_client
othersock = self.sock_client if is_reply else self.sock_server
for i in range(0, len(data), self.chunk_size):
sock.sendall(data[i:i+self.chunk_size])
othersock.recv(self.chunk_size)
print('{}: {}'.format('S->C' if is_reply else 'C->S', _dumpbytes(data)))
def state_find_begin(self, line):
if _is_marker(line):
self.state = self.state_find_node_1
def state_find_node_1(self, line):
if line.startswith('Node 1: '):
host, port = line.split(' ')[-1].rsplit(':', 1)
self.addr = (host, int(port))
self.state = self.state_find_data
self.open_sockets()
def state_find_data(self, line):
if _is_marker(line):
self.state = self.state_done
return
is_reply = line.startswith('\t')
data = bytearray.fromhex(line.strip())
self.add_data(data, is_reply)
def state_done(self, line):
pass
def open_sockets(self):
svr = socket.socket()
try:
svr.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
svr.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
try:
svr.bind(self.addr)
except socket.error as e:
host, port = self.addr
print('bind({0}:{1}) failure: {2}'.format(host, port, e))
self.addr = ('127.9.0.1', port)
print('Falling back to {}:{}'.format(*self.addr))
svr.bind(self.addr)
svr.listen(1)
self.sock_client = socket.socket()
self.sock_client.connect(self.addr)
self.sock_server, remote_addr = svr.accept()
finally:
svr.close()
def feed_data(self, line):
old_state = self.state
self.state(line)
# Return old state and new state
return old_state, self.state
def close(self):
if self.sock_server:
self.sock_server.close()
self.sock_server = None
if self.sock_client:
self.sock_client.close()
self.sock_client = None
def main(tshark_output, chunk_size):
parser = FollowParser(chunk_size=chunk_size)
try:
for line in tshark_output:
old_state, new_state = parser.feed_data(line)
if new_state != old_state and old_state == parser.state_find_node_1:
print('Found server node: {}:{}'.format(*parser.addr))
finally:
parser.close()
parser = ArgumentParser(description='Replay TCP capture')
parser.add_argument('-s', '--chunk-size', type=int, default=2,
help='Maximum size of each chunk (default %(default)d)')
parser.add_argument('file', help='Any capture format recognized by tshark')
if __name__ == '__main__':
_args = parser.parse_args()
_cmd = [
'tshark',
'-r',
_args.file,
'-q',
'-z', 'follow,tcp,raw,0'
]
with Popen(_cmd, stdin=DEVNULL, stdout=PIPE, universal_newlines=True) as p:
main(p.stdout, chunk_size=_args.chunk_size)
|