Skip to content

contrib/mysql: add classic protocol support#4967

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

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

Conversation

@pablogonzalezpe

@pablogonzalezpe pablogonzalezpe commented Apr 13, 2026

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
  • packet definitions for text resultset metadata and text rows for explicit use

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

Scope

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

Automatic server-side stream inference for resultset metadata and text rows is intentionally left out of this PR for now.

Not implemented in this first version:

  • TLS-encrypted MySQL payloads after SSLRequest
  • compression
  • binary resultsets
  • advanced server-side stream inference for resultset metadata and rows
  • 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
  • explicit resultset packet definitions
  • text rows with multiple columns and NULL
  • stream framing with deprecated EOF / OK terminators
  • COM_STMT_PREPARE_OK
  • multi-packet streams
  • dispatcher fallbacks and stream completeness

Result:

  • UTScapy: 19/19 passing

Lint / typing

Validated locally with:

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

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, common handshake/auth/query/OK/ERR/EOF packets from these captures were successfully parsed. Advanced server-side resultset metadata / row inference is intentionally left out of this PR.

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 the omitted server-side resultset inference should come in a follow-up PR

Comment thread scapy/contrib/mysql.py Outdated
Comment thread scapy/contrib/mysql.py
@codecov

codecov Bot commented Apr 14, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 98.47909% with 4 lines in your changes missing coverage. Please review.
✅ Project coverage is 80.38%. Comparing base (66ef96a) to head (6b78bd5).
⚠️ Report is 40 commits behind head on master.

Files with missing lines Patch % Lines
scapy/contrib/mysql.py 98.47% 4 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #4967      +/-   ##
==========================================
+ Coverage   80.31%   80.38%   +0.06%     
==========================================
  Files         381      385       +4     
  Lines       93630    95889    +2259     
==========================================
+ Hits        75202    77082    +1880     
- Misses      18428    18807     +379     
Files with missing lines Coverage Δ
scapy/contrib/mysql.py 98.47% <98.47%> (ø)

... and 54 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?

@gpotter2 gpotter2 left a comment

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.

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 Outdated
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

gpotter2 commented May 9, 2026

Copy link
Copy Markdown
Member

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

@pablogonzalezpe

Copy link
Copy Markdown
Author

Hi, just a small follow-up on this PR.

I believe the review comments were addressed in the latest updates, but I am happy to make further adjustments if needed.

@polybassa

Copy link
Copy Markdown
Contributor

Perfect, thanks!

@polybassa polybassa self-assigned this Jun 10, 2026
Comment thread scapy/contrib/mysql.py Outdated

# Track enough server-side state to force metadata/resultset packet types
# during TCP stream reassembly.
def _mysql_server_cls(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Could you please rework this code or move it to a dedicated PR.

All above parts of the PR look fine and would be ready to merge.

@pablogonzalezpe

pablogonzalezpe commented Jun 11, 2026

Copy link
Copy Markdown
Author

I reduced the scope of the server-side stream inference in this PR to keep it focused on the mergeable MVP discussed in review. The advanced heuristics for automatically inferring resultset metadata and text rows from server streams are now out of scope for this PR. The module still provides framing, handshake/auth support, query packets, common OK/ERR/EOF responses, and the packet definitions for explicit use. I also updated the PR description and reran UTScapy, flake8, and mypy after the change. If it makes sense, I can keep the omitted server-side resultset inference for a separate follow-up PR.

@polybassa

Copy link
Copy Markdown
Contributor

Thanks! Feel free to open a new PR for the additional features.

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