or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

addons.mdcommands.mdconfiguration.mdconnections.mdcontent.mdflow-io.mdhttp-flows.mdindex.mdprotocols.md

protocols.mddocs/

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

```