Skip to content

contrib/mysql: add classic protocol support#4967

Open
pablogonzalezpe wants to merge 8 commits into
secdev:masterfrom
pablogonzalezpe:contrib-mysql
Open

contrib/mysql: add classic protocol support#4967
pablogonzalezpe wants to merge 8 commits into
secdev:masterfrom
pablogonzalezpe:contrib-mysql

Conversation

@pablogonzalezpe
Copy link
Copy Markdown

Summary

This PR adds a new scapy.contrib.mysql module implementing support for the MySQL classic protocol over TCP.

The current scope covers a first usable subset of the protocol for dissection and packet building, including:

  • packet framing (payload_length + sequence_id)
  • Protocol::HandshakeV10
  • Protocol::SSLRequest
  • Protocol::HandshakeResponse41
  • OldAuthSwitchRequest
  • AuthSwitchRequest
  • AuthSwitchResponse
  • AuthMoreData
  • OK_Packet
  • ERR_Packet
  • EOF_Packet
  • COM_QUERY
  • COM_STMT_PREPARE_OK
  • text resultsets:
    • column count
    • column definitions
    • text rows
    • EOF / OK terminators
  • prepared statement metadata flows
  • some legacy field-list style flows seen in real captures

Regression tests are added in test/contrib/mysql.uts.

Scope

This PR is intentionally limited to a usable MVP for the classic protocol.

Not implemented in this first version:

  • TLS-encrypted MySQL payloads after SSLRequest
  • compression
  • binary resultsets
  • full command coverage
  • full authentication plugin coverage
  • full prepared statement execution support

Validation

UTScapy

Added regression tests for:

  • handshake packets
  • auth switch and auth more data packets
  • OK / ERR / EOF packets
  • COM_QUERY
  • text resultsets
  • resultsets with multiple columns and NULL
  • resultsets terminated with OK when CLIENT_DEPRECATE_EOF applies
  • COM_STMT_PREPARE_OK metadata flows
  • legacy field-list style flows
  • multi-packet streams

Result:

  • UTScapy: 15/15 passing

Lint / typing

Validated locally with:

  • tox -e flake8 on Windows: OK
  • tox -e flake8 on WSL Ubuntu / Python 3.9: OK
  • tox -e mypy on Windows: OK

On WSL Ubuntu / Python 3.9, tox -e mypy reports existing repository-level typing issues outside this module.

Real PCAP validation

The contrib was also tested against these public MySQL captures:

  1. umitproject/packet-manipulator/audits/pcap-tests/mysql.pcap
  2. colinnewell/pcap2mysql-log/test/captures/big-data.pcap
  3. arkime/tests/pcap/mysql-allow.pcap

In local validation, the MySQL messages in these 3 captures were successfully parsed with the current implementation.

Notes

This PR follows the earlier discussion in #4954.

I tried to keep names close to the MySQL protocol naming where possible, while avoiding collisions with Scapy internals when needed.

Feedback is welcome on:

  • whether this is the right MVP boundary for scapy.contrib
  • whether any naming should be adjusted further
  • whether prepared statement metadata support should stay in this PR or be split out

Comment thread scapy/contrib/mysql.py Outdated
Comment thread scapy/contrib/mysql.py
@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 14, 2026

Codecov Report

❌ Patch coverage is 95.54455% with 18 lines in your changes missing coverage. Please review.
✅ Project coverage is 80.35%. Comparing base (66ef96a) to head (69e2ce5).
⚠️ Report is 18 commits behind head on master.

Files with missing lines Patch % Lines
scapy/contrib/mysql.py 95.54% 18 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #4967      +/-   ##
==========================================
+ Coverage   80.31%   80.35%   +0.03%     
==========================================
  Files         381      384       +3     
  Lines       93630    95107    +1477     
==========================================
+ Hits        75202    76420    +1218     
- Misses      18428    18687     +259     
Files with missing lines Coverage Δ
scapy/contrib/mysql.py 95.54% <95.54%> (ø)

... and 40 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Comment thread scapy/contrib/mysql.py Outdated
Comment thread scapy/contrib/mysql.py Outdated
@polybassa
Copy link
Copy Markdown
Contributor

Please try to increase the test coverage, since this looks like AI-generated code.

@pablogonzalezpe
Copy link
Copy Markdown
Author

Thanks, that is fair feedback.

I will add more tests to exercise the currently uncovered branches in \scapy.contrib.mysql. The current version already includes UTScapy regression tests and validation against real MySQL pcaps, but I agree that the patch coverage can be improved further.

@pablogonzalezpe
Copy link
Copy Markdown
Author

I added more UTScapy tests to exercise uncovered branches, including auth-response variants, helper edge cases, fallback/error paths, and incomplete stream reassembly.

Comment thread scapy/contrib/mysql.py Outdated
Comment thread scapy/contrib/mysql.py Outdated
Comment thread scapy/contrib/mysql.py Outdated
Comment thread scapy/contrib/mysql.py Outdated
@polybassa
Copy link
Copy Markdown
Contributor

Please fix the unit test.

@pablogonzalezpe
Copy link
Copy Markdown
Author

Thanks, fixed.

I reproduced the failure with \conf.debug_dissector=True. The fallback test was using a short EOF-like server payload (\ e00), which could raise while being parsed as an incomplete EOF packet in strict dissection mode.

I changed the test to use an unambiguous unknown server payload for the Raw fallback case and re-ran the MySQL UTScapy tests locally, including the -K scanner\ run and strict dissection mode.

@polybassa polybassa marked this pull request as ready for review April 21, 2026 14:10
@polybassa
Copy link
Copy Markdown
Contributor

@gpotter2 What do you think?

Copy link
Copy Markdown
Member

@gpotter2 gpotter2 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a good start but the code needs a cleaning up phase. It has way too many functions that are used only once and sometimes not that useful, which looks very AI-ish. It probably can be fixed easily though.

Comment thread scapy/contrib/mysql.py Outdated
Comment thread scapy/contrib/mysql.py Outdated
Comment thread scapy/contrib/mysql.py
if len(remain) < 4:
return None
payload_length = struct.unpack("<I", remain[:3] + b"\x00")[0]
payload = remain[4:4 + payload_length]
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am a bit confused by this code. It calls 3 or 4 functions once that all iterate through a list of stuff. Couldn't this be merged in a single iteration?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I simplified that part as well and collapsed the server-side state handling into a more direct single-pass dispatch flow.

Comment thread scapy/contrib/mysql.py Outdated
Comment thread scapy/contrib/mysql.py Outdated
@pablogonzalezpe
Copy link
Copy Markdown
Author

Thanks for the feedback. I cleaned up the module structure to reduce one-off helpers and make the stream dispatch/reassembly logic more direct and easier to follow, without changing the supported protocol scope.

I re-ran the MySQL UTScapy tests, strict dissection mode, flake8, mypy, the WSL CI-like runs, and the real-PCAP smoke tests used during development. Everything is still passing.

@gpotter2
Copy link
Copy Markdown
Member

gpotter2 commented May 9, 2026

Thanks for the update. There are still comments you haven't addressed :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants