aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/voicecall-shark
blob: 1312b17c26818402cf50155040c3fd27a160f9a2 (plain)
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
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
#!/usr/bin/env python3

doc = '''voicecall-shark: get grips on voice call MGCP and RTP'''

import pyshark

class LogEntry:
	def __init__(s, p, message, obj):
		s.p = p
		s.message = message
		s.obj = obj


class HasLog:
	def __init__(s, parent=None):
		s.log_entries = []
		s.parents = []
		s.children = []
		if parent is not None:
			s.log_parent(parent)

	def log_child(s, child):
		s.children.append(child)
		child.parents.append(s)

	def log_parent(s, parent):
		parent.log_child(s)

	def log(s, p, message):
		s.log_entries.append(LogEntry(p, message, s))


class MgcpConn(HasLog):
	def __init__(s, crcx_p, endpoint, conn_id=None):
		endpoint.add_conn(s)
		s.conn_id = conn_id
		s.crcx = crcx_p
		s.crcx_ok = None
		s.mdcx = []
		s.dlcx = None
		s.dlcx_ok = None
		super().__init__(endpoint)

	def rx_verb(s, p):
		v = p.mgcp.req_verb
		print("VERB %r" % v)
		if v == 'MDCX':
			s.log(p, 'MDCX')
			s.mdcx.append(p)

		elif v == 'DLCX':
			s.log(p, 'DLCX')
			s.dlcx = p

	def rx_verb_ok(s, p, verb_p):
		verb = verb_p.mgcp.req_verb
		print("VERB OK %r" % verb)
		if verb == 'CRCX':
			s.crcx_ok = p
			s.conn_id = p.mgcp.param_connectionid
			print("CRCX OK %r" % s.conn_id)
		elif verb == 'MDCX':
			s.mdcx.append(p)
		elif verb == 'DLCX':
			s.dlcx_ok = p
	def is_open(s):
		return s.dlcx is None

	def summary(s):
		print('%s-> %s:%s %s/%s' % (s.conn_id, s.ip, s.port, s.payload_type, s.codec))


class MgcpEndpoint(HasLog):
	def __init__(s, name):
		s.name = name
		s.conns = []
		super().__init__()

	def name_is(s, name):
		return s.name == name

	def add_conn(s, mgcp_conn):
		s.conns.append(mgcp_conn)
		mgcp_conn.endpoint = s

	def is_open(s):
		return any(c.is_open() for c in s.conns)

	def get_conn(s, p):
		conn_id = p.mgcp.param_connectionid
		print('get conn_id %r' % conn_id)
		for c in s.conns:
			print('  conn_id %r' % c.conn_id)
			if c.conn_id == conn_id:
				return c
		print('ERROR: unknown conn id %r' % conn_id)
		return None

	def summary(s):
		print(s.name)
		for c in s.conns:
			print(' | %s' % c.summary())

class MgcpTrans:
	def __init__(s, p, obj):
		s.p = p
		s.obj = obj

class CallNode(HasLog):
	def __init__(s, ip_addr=None, port=None, codec=None, payload_type=None):
		s.ip_addr = ip_addr
		s.port = port
		s.codec = codec
		s.payload_type = payload_type
		super().__init__()


class CallEdge(HasLog):
	def __init__(s, nodes=[]):
		s.nodes = nodes
		super().__init__()


class CallLeg(HasLog):
	def __init__(s):
		self.edges = []
		super().__init__()

	def add_edge(s, edge):
		self.edges.append(edge)
		s.log_child(edge)


class Results:
	def __init__(s, call_legs = []):
		s.call_legs = call_legs
		s.mgcp_endpoints = []
		s.mgcp_transactions = []

	def mgcp_trans_new(s, p, obj):
		s.mgcp_transactions.append(MgcpTrans(p, obj))

	def mgcp_trans_res(s, p):
		for t in s.mgcp_transactions:
			if t.p.mgcp.transid == p.mgcp.transid:
				o = t.obj
				s.mgcp_transactions.remove(t)
				return t

	def new_endpoint(s, p):
		ep = MgcpEndpoint(p.mgcp.req_endpoint)
		s.mgcp_endpoints.append(ep)
		return ep

	def find_endpoint(s, endpoint, still_open=False):
		for ep in s.mgcp_endpoints:
			if not ep.name_is(endpoint):
				continue
			if still_open and not ep.is_open():
				continue
			return ep

	def process_mgcp(s, p):
		m = p.mgcp
		print('----')
		print(p.pretty_print())
		print(p.mgcp.field_names)

		if 'req_verb' in m.field_names:
			v = m.req_verb
			ep = None
			ci = None

			if v == 'CRCX':
				# does the endpoint exist?
				if '*' not in m.req_endpoint:
					ep = s.find_endpoint(m.req_endpoint, True)

				if ep is None:
					ep = s.new_endpoint(p)

				ci = MgcpConn(p, ep)
			else:
				ep = s.find_endpoint(m.req_endpoint, True)
				print('VERB ep %r' % ep.name)
				ci = ep.get_conn(p)
				ci.rx_verb(p)

			s.mgcp_trans_new(p, ci)
			return

		if 'rsp' in m.field_names:
			t = s.mgcp_trans_res(p)
			ci = t.obj
			ci.rx_verb_ok(p, t.p)

	def process_cap(s, cap):
		for p in cap:
			if hasattr(p, 'mgcp'):
				s.process_mgcp(p)

	def process_file(s, path):
		print(repr(path))
		cap = pyshark.FileCapture(path)
		s.process_cap(cap)

	def summary(s):

		print('mgcp endpoints: %d' % len(s.mgcp_endpoints))


def parse_args():
	import argparse
	parser = argparse.ArgumentParser(description=doc)
	parser.add_argument('--pcap-file', '-f')
	return parser.parse_args()

if __name__ == '__main__':
	opts = parse_args()

	r = Results()
	r.process_file(opts.pcap_file)
	print(r.summary())