0
# Multi-Protocol Support
1
2
Support for TCP, UDP, and WebSocket protocols beyond HTTP, enabling comprehensive network traffic analysis and modification for various protocol types. Each protocol provides flow tracking with message-level granularity.
3
4
## Capabilities
5
6
### TCP Flow Management
7
8
Raw TCP connection handling with message-level tracking and content inspection capabilities.
9
10
```python { .api }
11
class TCPFlow(Flow):
12
"""
13
A TCP connection flow tracking raw TCP messages.
14
15
Attributes:
16
- messages: List of TCP messages exchanged
17
- client_conn: Client connection details
18
- server_conn: Server connection details
19
- live: Whether the connection is still active
20
- error: Error information if the flow failed
21
"""
22
messages: List[TCPMessage]
23
client_conn: Client
24
server_conn: Server
25
live: bool
26
error: Optional[Error]
27
28
def __init__(self, client_conn: Client, server_conn: Server) -> None: ...
29
30
class TCPMessage:
31
"""
32
Individual TCP message within a flow.
33
34
Attributes:
35
- from_client: True if message is from client to server
36
- content: Raw message content as bytes
37
- timestamp: Message timestamp
38
"""
39
from_client: bool
40
content: bytes
41
timestamp: float
42
43
def __init__(self, from_client: bool, content: bytes, timestamp: float = 0) -> None: ...
44
45
def __str__(self) -> str: ...
46
def __repr__(self) -> str: ...
47
```
48
49
### UDP Flow Management
50
51
UDP packet tracking with support for connectionless protocol patterns.
52
53
```python { .api }
54
class UDPFlow(Flow):
55
"""
56
A UDP flow tracking individual UDP messages/packets.
57
58
Attributes:
59
- messages: List of UDP messages exchanged
60
- client_conn: Client connection details
61
- server_conn: Server connection details
62
- live: Whether the flow is still active
63
- error: Error information if the flow failed
64
"""
65
messages: List[UDPMessage]
66
client_conn: Client
67
server_conn: Server
68
live: bool
69
error: Optional[Error]
70
71
def __init__(self, client_conn: Client, server_conn: Server) -> None: ...
72
73
class UDPMessage:
74
"""
75
Individual UDP message/packet within a flow.
76
77
Attributes:
78
- from_client: True if message is from client to server
79
- content: UDP packet content as bytes
80
- timestamp: Message timestamp
81
"""
82
from_client: bool
83
content: bytes
84
timestamp: float
85
86
def __init__(self, from_client: bool, content: bytes, timestamp: float = 0) -> None: ...
87
88
def __str__(self) -> str: ...
89
def __repr__(self) -> str: ...
90
```
91
92
### WebSocket Support
93
94
WebSocket message tracking within HTTP flows with support for different message types.
95
96
```python { .api }
97
class WebSocketMessage:
98
"""
99
Individual WebSocket message within an HTTP flow.
100
101
Attributes:
102
- type: WebSocket message type (text, binary, ping, pong, close)
103
- content: Message content as bytes
104
- from_client: True if message is from client to server
105
- timestamp: Message timestamp
106
- killed: Whether the message was killed/blocked
107
"""
108
type: int
109
content: bytes
110
from_client: bool
111
timestamp: float
112
killed: bool
113
114
def __init__(
115
self,
116
type: int,
117
content: bytes,
118
from_client: bool = True,
119
timestamp: float = 0
120
) -> None: ...
121
122
def kill(self) -> None: ...
123
def __str__(self) -> str: ...
124
def __repr__(self) -> str: ...
125
126
class WebSocketData:
127
"""
128
Container for WebSocket messages within an HTTP flow.
129
130
Provides access to all WebSocket messages exchanged after the
131
HTTP upgrade handshake.
132
"""
133
messages: List[WebSocketMessage]
134
135
def __init__(self) -> None: ...
136
137
# Type alias for WebSocket message state
138
WebSocketMessageState = Tuple[int, bool] # (opcode, from_client)
139
```
140
141
### DNS Flow Support
142
143
DNS query and response tracking for DNS-over-HTTPS and transparent DNS proxying.
144
145
```python { .api }
146
class DNSFlow(Flow):
147
"""
148
A DNS query/response flow.
149
150
Attributes:
151
- request: DNS query message
152
- response: DNS response message (None if not yet received)
153
- client_conn: Client connection details
154
- server_conn: Server connection details
155
- live: Whether the flow is still active
156
- error: Error information if the flow failed
157
"""
158
request: DNSMessage
159
response: Optional[DNSMessage]
160
client_conn: Client
161
server_conn: Server
162
live: bool
163
error: Optional[Error]
164
165
def __init__(self, client_conn: Client, server_conn: Server, request: DNSMessage) -> None: ...
166
167
class DNSMessage:
168
"""
169
DNS query or response message.
170
171
Attributes:
172
- query: True if this is a query, False if response
173
- content: Raw DNS message content as bytes
174
- timestamp: Message timestamp
175
"""
176
query: bool
177
content: bytes
178
timestamp: float
179
180
def __init__(self, query: bool, content: bytes, timestamp: float = 0) -> None: ...
181
```
182
183
## Usage Examples
184
185
### TCP Flow Interception
186
187
```python
188
from mitmproxy import tcp
189
190
def tcp_message(flow: tcp.TCPFlow):
191
"""Intercept and modify TCP messages."""
192
message = flow.messages[-1] # Get the latest message
193
194
print(f"TCP message: {len(message.content)} bytes")
195
print(f"Direction: {'client->server' if message.from_client else 'server->client'}")
196
197
# Log raw content (be careful with binary data)
198
try:
199
content_str = message.content.decode('utf-8', errors='replace')
200
print(f"Content preview: {content_str[:100]}...")
201
except:
202
print(f"Binary content: {message.content[:50].hex()}...")
203
204
# Modify content
205
if message.from_client and b"password" in message.content:
206
# Replace password in client messages
207
message.content = message.content.replace(b"password", b"********")
208
209
# Block specific content
210
if b"BLOCKED_COMMAND" in message.content:
211
# Kill the message to prevent forwarding
212
flow.kill()
213
214
def tcp_start(flow: tcp.TCPFlow):
215
"""Handle TCP flow start."""
216
print(f"TCP connection: {flow.client_conn.address} -> {flow.server_conn.address}")
217
218
# Could implement protocol detection here
219
# by examining the first few bytes
220
221
def tcp_end(flow: tcp.TCPFlow):
222
"""Handle TCP flow completion."""
223
total_messages = len(flow.messages)
224
total_bytes = sum(len(msg.content) for msg in flow.messages)
225
226
print(f"TCP flow completed: {total_messages} messages, {total_bytes} bytes")
227
```
228
229
### UDP Packet Analysis
230
231
```python
232
from mitmproxy import udp
233
import struct
234
235
def udp_message(flow: udp.UDPFlow):
236
"""Analyze UDP packets."""
237
message = flow.messages[-1]
238
239
print(f"UDP packet: {len(message.content)} bytes")
240
print(f"Direction: {'client->server' if message.from_client else 'server->client'}")
241
242
# Example: Parse DNS queries (assuming DNS over UDP)
243
if flow.server_conn.address[1] == 53: # DNS port
244
try:
245
# Simple DNS header parsing
246
if len(message.content) >= 12:
247
header = struct.unpack('!HHHHHH', message.content[:12])
248
transaction_id, flags, questions, answers, authority, additional = header
249
250
print(f"DNS Transaction ID: {transaction_id}")
251
print(f"Questions: {questions}, Answers: {answers}")
252
253
# Could parse questions/answers here
254
255
except struct.error:
256
print("Invalid DNS packet format")
257
258
# Modify UDP content
259
if message.from_client and b"blocked.com" in message.content:
260
# Replace blocked domain
261
message.content = message.content.replace(b"blocked.com", b"allowed.com")
262
263
def udp_start(flow: udp.UDPFlow):
264
"""Handle UDP flow start."""
265
print(f"UDP flow: {flow.client_conn.address} -> {flow.server_conn.address}")
266
267
def udp_end(flow: udp.UDPFlow):
268
"""Handle UDP flow completion."""
269
print(f"UDP flow ended: {len(flow.messages)} packets")
270
```
271
272
### WebSocket Message Handling
273
274
```python
275
from mitmproxy import http, websocket
276
import json
277
278
def websocket_message(flow: http.HTTPFlow):
279
"""Handle WebSocket messages within HTTP flows."""
280
if not hasattr(flow, 'websocket') or not flow.websocket:
281
return
282
283
message = flow.websocket.messages[-1]
284
285
print(f"WebSocket message type: {message.type}")
286
print(f"Direction: {'client->server' if message.from_client else 'server->client'}")
287
print(f"Content length: {len(message.content)} bytes")
288
289
# Handle different message types
290
if message.type == 1: # Text message
291
try:
292
text = message.content.decode('utf-8')
293
print(f"Text message: {text[:100]}...")
294
295
# Try to parse as JSON
296
try:
297
data = json.loads(text)
298
print(f"JSON data: {data}")
299
300
# Modify JSON messages
301
if message.from_client and "command" in data:
302
if data["command"] == "delete_all":
303
# Block dangerous commands
304
message.kill()
305
return
306
307
# Add metadata to commands
308
data["proxy_timestamp"] = message.timestamp
309
message.content = json.dumps(data).encode('utf-8')
310
311
except json.JSONDecodeError:
312
print("Not JSON format")
313
314
except UnicodeDecodeError:
315
print("Not valid UTF-8 text")
316
317
elif message.type == 2: # Binary message
318
print(f"Binary message: {message.content[:50].hex()}...")
319
320
# Could handle binary protocols here
321
322
elif message.type == 8: # Close frame
323
print("WebSocket close frame")
324
325
elif message.type in (9, 10): # Ping/Pong
326
print(f"WebSocket {'ping' if message.type == 9 else 'pong'}")
327
328
def websocket_start(flow: http.HTTPFlow):
329
"""Handle WebSocket handshake completion."""
330
print(f"WebSocket connection established: {flow.request.url}")
331
332
# Initialize WebSocket data container if needed
333
if not hasattr(flow, 'websocket'):
334
flow.websocket = websocket.WebSocketData()
335
336
def websocket_end(flow: http.HTTPFlow):
337
"""Handle WebSocket connection termination."""
338
if hasattr(flow, 'websocket') and flow.websocket:
339
message_count = len(flow.websocket.messages)
340
print(f"WebSocket connection closed: {message_count} messages exchanged")
341
```
342
343
### Protocol Detection and Routing
344
345
```python
346
from mitmproxy import tcp, flow
347
348
def tcp_message(flow: tcp.TCPFlow):
349
"""Implement protocol detection for TCP flows."""
350
if len(flow.messages) == 1: # First message
351
message = flow.messages[0]
352
353
# Simple protocol detection based on first bytes
354
if message.content.startswith(b'GET ') or message.content.startswith(b'POST '):
355
print("Detected HTTP over TCP (should have been handled as HTTP)")
356
357
elif message.content.startswith(b'\x16\x03'): # TLS handshake
358
print("Detected TLS handshake")
359
360
elif len(message.content) >= 4:
361
# Check for common protocol signatures
362
first_bytes = message.content[:4]
363
364
if first_bytes == b'SSH-':
365
print("Detected SSH protocol")
366
elif first_bytes.startswith(b'\x00\x00'):
367
print("Possible binary protocol")
368
else:
369
print(f"Unknown protocol, first bytes: {first_bytes.hex()}")
370
371
def next_layer(nextlayer):
372
"""Implement custom protocol detection logic."""
373
# This would be called before TCP flow creation
374
# to determine if the connection should be handled differently
375
376
client_hello = nextlayer.data_client()
377
378
if client_hello:
379
# Examine initial data to determine protocol
380
if client_hello.startswith(b'GET ') or client_hello.startswith(b'POST '):
381
# Should be handled as HTTP
382
nextlayer.layer = HttpLayer(nextlayer.context)
383
384
elif client_hello.startswith(b'\x16\x03'):
385
# TLS handshake - continue with TLS layer
386
nextlayer.layer = TlsLayer(nextlayer.context)
387
388
else:
389
# Default to TCP for unknown protocols
390
nextlayer.layer = TcpLayer(nextlayer.context)
391
```
392
393
### Multi-Protocol Flow Analysis
394
395
```python
396
from mitmproxy import http, tcp, udp
397
398
class ProtocolStats:
399
def __init__(self):
400
self.http_flows = 0
401
self.tcp_flows = 0
402
self.udp_flows = 0
403
self.websocket_messages = 0
404
self.total_bytes = 0
405
406
def log_stats(self):
407
print(f"Protocol Statistics:")
408
print(f" HTTP flows: {self.http_flows}")
409
print(f" TCP flows: {self.tcp_flows}")
410
print(f" UDP flows: {self.udp_flows}")
411
print(f" WebSocket messages: {self.websocket_messages}")
412
print(f" Total bytes: {self.total_bytes}")
413
414
stats = ProtocolStats()
415
416
def response(flow: http.HTTPFlow):
417
"""Track HTTP flow statistics."""
418
stats.http_flows += 1
419
if flow.request.content:
420
stats.total_bytes += len(flow.request.content)
421
if flow.response and flow.response.content:
422
stats.total_bytes += len(flow.response.content)
423
424
def tcp_end(flow: tcp.TCPFlow):
425
"""Track TCP flow statistics."""
426
stats.tcp_flows += 1
427
for message in flow.messages:
428
stats.total_bytes += len(message.content)
429
430
def udp_end(flow: udp.UDPFlow):
431
"""Track UDP flow statistics."""
432
stats.udp_flows += 1
433
for message in flow.messages:
434
stats.total_bytes += len(message.content)
435
436
def websocket_message(flow: http.HTTPFlow):
437
"""Track WebSocket message statistics."""
438
if hasattr(flow, 'websocket') and flow.websocket:
439
stats.websocket_messages += 1
440
message = flow.websocket.messages[-1]
441
stats.total_bytes += len(message.content)
442
443
def done():
444
"""Print final statistics when mitmproxy shuts down."""
445
stats.log_stats()
446
```