Skip to content

Latest commit

 

History

2 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 

Repository files navigation

winbox_cdb.py

Personal note. I wrote this little helper during a larger network migration to bulk-edit comments and groups in an unencrypted Winbox 4.1 .cdb file. Out of 100+ saved devices, about a dozen entries needed adjusting — doing that one by one through the Winbox GUI is exhausting, so a small CLI that runs one targeted edit per invocation made the batch tractable. It's a personal tool that turned out useful enough to share; the parser was reverse-engineered from a single sample file, so treat it accordingly.

A small Python CLI to inspect and edit a Winbox 4 connection database (*.cdb).

Targets one connection at a time, looked up by its address (IP or MAC). Supports list, show, set (comment / group) and delete.

No third-party dependencies — uses only the Python 3 standard library.


Usage

python3 winbox_cdb.py <file.cdb> list                                  [--group GROUP]
python3 winbox_cdb.py <file.cdb> show   --addr <IP-or-MAC>
python3 winbox_cdb.py <file.cdb> set    --addr <IP-or-MAC> [--new-addr <IP-or-MAC>] [--user TEXT] [--password TEXT] [--romon-agent <IP>] [--comment TEXT] [--group TEXT]
python3 winbox_cdb.py <file.cdb> delete --addr <IP-or-MAC> [-y]

Examples

# Show all entries.
python3 winbox_cdb.py sample.cdb list

# Show only entries in a specific group.
python3 winbox_cdb.py sample.cdb list --group Rathaus

# Inspect every field of one entry.
python3 winbox_cdb.py sample.cdb show --addr 10.109.9.35

# Update its comment and group in one go.
python3 winbox_cdb.py sample.cdb set --addr 10.109.9.35 \
    --comment "RAT-C-COR-A" --group "RAT-C (Core)"

# Change the address (renumbering / replacing hardware).
# Letters in MAC addresses are upper-cased automatically.
python3 winbox_cdb.py sample.cdb set --addr 10.109.9.35 --new-addr 10.109.9.36
python3 winbox_cdb.py sample.cdb set --addr 10.109.9.36 --new-addr aa:bb:cc:dd:ee:ff

# Rotate credentials.
python3 winbox_cdb.py sample.cdb set --addr 10.109.9.35 --user "operator" --password "newpw"

# Change or clear the RoMON agent (only on entries that already use one).
python3 winbox_cdb.py sample.cdb set --addr 00:15:5D:09:7B:39 --romon-agent 10.109.9.36
python3 winbox_cdb.py sample.cdb set --addr 00:15:5D:09:7B:39 --romon-agent ""

# Clear the comment.
python3 winbox_cdb.py sample.cdb set --addr 10.109.9.35 --comment ""

# Delete an entry (asks for confirmation; pass -y to skip it).
python3 winbox_cdb.py sample.cdb delete --addr 10.109.9.35

Address matching

--addr is matched against the entry's address field (fid 1):

  • IPv4 addresses are matched exactly (10.109.9.35).
  • MAC addresses are matched case-insensitively (AA:BB:… == aa:bb:…).
  • If 0 entries match, the command fails with exit code 2.
  • If more than 1 entry matches, the command fails with exit code 3 and lists them. Every operation targets exactly one entry — there is no bulk mode by design.

--new-addr (on set) accepts IPv4, IPv6 or a MAC address. Input is validated and normalized before writing:

  • MAC letters are upper-cased (aa:bb:cc:dd:ee:ffAA:BB:CC:DD:EE:FF).
  • IPv4 / IPv6 are stored in ipaddress canonical form.
  • Invalid values are rejected before any file is touched.
  • If another entry already has the requested address, set refuses with exit code 3 and points at the colliding entry.

--romon-agent (on set) updates the RoMON Agent field (fid 11):

  • Accepts an IPv4 or IPv6 address. Empty string ("") clears the field.
  • Anything else is rejected before any file is touched.
  • The tool only updates an existing RoMON Agent field; it cannot add the field to records that don't already have one. If a connection has no RoMON Agent yet, configure it once through Winbox first, then this tool can take over the future updates.

Safety guarantees

Every write command (set, delete):

  1. Refuses to run if a <file>.lock is present next to the database.

  2. Manages a single backup <file>.bak next to the database, with the following session-aware policy:

    • If <file>.bak does not exist → a fresh backup is created.
    • If <file>.bak is ≤ 10 minutes old → it is reused, not overwritten. This protects the pre-batch state when several edits are run in quick succession (the first edit captures the safety net, subsequent edits keep that same safety net).
    • If <file>.bak is > 10 minutes old → the command aborts and asks the user to rename or remove the stale backup first. This guards against silently overwriting a known-good backup with a file that may itself have been bricked by an earlier edit.

    Each write reports whether the backup was created or reused.

  3. Writes the new file atomically via <file>.tmp + os.replace.

  4. Re-parses the freshly-written file and verifies that the targeted record really has the new value(s). On any verification failure the backup is restored and the command exits non-zero.

The parser is tested to round-trip the sample database byte-for-byte (check_roundtrip.py sample.cdb), so an unedited write reproduces the input exactly. An edit only changes:

  • the bytes of the affected record body, and
  • the 4-byte size prefix of that record.

All other records remain bit-identical.


File format (short)

file := MAGIC (4B = 0D F0 1D C0)  record*
record := size:u32_le  body[size]
body  := "M2"  kind_header  field*

Two record kinds appear in the wild:

kind bytes (after M2) meaning
05 00 00 00 top-level managed connection
07 00 00 01 sub-entry under a category like IP Services

Inside the body, every string field is one of:

explicit:  <fid:u24_le> 21 <len:u8> <data>
implicit:  09 00 FE   21 <len:u8> <data>     # used for fid 0 (name)

Field IDs known to this tool:

fid meaning
0 session name (implicit framing)
1 address (IP or MAC) — lookup key
2 login
3 password (sometimes encrypted)
4 comment / display label
6 <own> keep-password flag
8 group
11 RoMON agent (IPv4/IPv6, may be empty)
12 Session (e.g. IP Services)

String length is one byte, so --comment / --group values are limited to 254 bytes (UTF-8 encoded). The tool refuses longer values rather than guessing a different encoding.


Limitations

  • No add command. The per-record header contains small-int slots whose meaning isn't fully decoded; creating a fresh record from scratch risks Winbox rejecting or rewriting the file. Cloning an existing record and patching its fields is a safer path if this is ever needed.
  • Only fields with explicit framing can be safely set; fields 4 (comment) and 8 (group) both qualify. The implicit-framing name field (fid 0) is not editable through set.
  • The format was reverse-engineered from one sample file. If you encounter a .cdb that fails the round-trip check, do not edit it with this tool — there's a wrinkle the parser hasn't seen.

Files

  • winbox_cdb.py — the CLI tool (parser + editor in one file).
  • check_roundtrip.py — sanity check: re-serialize and compare to original.
  • sample.cdb — local working copy for development / testing.

License

MIT © 2026 dko-strd

The software is provided "as is", without warranty of any kind. See the LICENSE file for the full text.

About

Targeted CLI to edit unencrypted Winbox 4 .cdb files. Single-entry updates of address, credentials, group, comment or RoMON agent, keyed by IP/MAC.

Resources

Stars

4 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages