Skip to content

Releases: rickybryce/ethernetgateway

ethernetgateway v0.8.0

Choose a tag to compare

@github-actions github-actions released this 26 Jul 23:44

Added

  • CP/M emulator — Z80 CPU + interactive console (in progress). A
    new default-off config key cpm_emu_enabled (wired into the telnet, web, and
    GUI config UIs) gates a K CP/M System main-menu item. Selecting it boots a
    real Z80 CPU (the BSD-licensed iz80 crate)
    in a 64 KB machine driven by our own CP/M 2.2 BDOS, and drops into a Rust
    CCP-lite A> prompt. The full console BDOS group (character/string output,
    console input with echo, direct console I/O, read-console-buffer, console
    status, version) is wired to the telnet/SSH session, so interactive Z80
    programs can read and write the console; built-in HELLO and ECHO demos
    exercise it. Works correctly on PETSCII (C64) terminals as well as ANSI/ASCII.
    On launch it creates the drive folders CPM/A..CPM/H under transfer_dir.
    An interactive program can be aborted with a double-ESC, and a runaway is
    bounded by an instruction budget. Completely separate from the Gateway Shell,
    which emulates no CPU; the item is hidden and the key rejected while the
    toggle is off.

    • Filesystem, part 1 — FCB + drives + sequential file I/O. The emulator
      now has a directory-backed CP/M filesystem: each drive A:–H: is a folder in
      the CPM/ container, and the BDOS file calls for opening, creating,
      closing, and sequentially reading/writing files (via the DMA buffer and a
      parsed 36-byte FCB) are implemented and jailed under transfer_dir. Drive
      select / current-disk / set-DMA are wired, 8.3 filenames are enforced
      (case-insensitive; host files that aren't valid 8.3 are invisible to CP/M),
      and the A> prompt gained A:..H: drive-change commands with a
      drive-aware prompt.
    • Filesystem, part 2 — directory search + erase. The BDOS
      search-first/search-next calls enumerate a drive's files (with ?
      wildcards, synthesizing CP/M directory entries per 16 KB extent) and the
      delete call removes matching files. The CCP-lite gained the authentic
      built-ins DIR (list the current drive) and ERA name (erase, with a
      *.* confirmation), so files uploaded into a CPM/ drive can be listed
      and removed interactively.
    • Filesystem, part 3 — random-record I/O, file size, rename. The BDOS
      random read/write calls seek to a record number (with the sequential
      position kept in sync), compute-file-size reports a file's length in
      records, and rename moves a file to a new 8.3 name (no clobber). This
      completes the CP/M 2.2 file BDOS surface, so real utilities like PIP
      and STAT become runnable once .COM loading lands.
    • Run a real .COM from a drive. A command at the A> prompt that
      isn't a built-in is now resolved as <verb>.COM on the drive (honoring a
      B: drive prefix), loaded into the TPA, and executed — so actual CP/M
      software (PIP, STAT, ASM, …) uploaded into a CPM/ drive runs over
      telnet/SSH. Page zero is set up exactly as the CCP does before launch: the
      command tail is placed at 0x0080 and the first two arguments are parsed
      into the default FCBs at 0x005C / 0x006C. The program image is loaded
      jailed under transfer_dir (canonical-prefix + symlink checks) and bounded
      by the per-file size cap.
    • Full CP/M resident command set. The A> prompt now implements all
      six authentic CP/M 2.2 resident commands — DIR, ERA, REN, TYPE,
      SAVE, USER (plus the d: drive change) — so no upload is needed for
      everyday file work. REN renames (no clobber); TYPE streams a text file
      and stops at the ^Z end-of-file marker (binary files refused); USER
      selects an area (only area 0 exists — one flat area per drive). To make
      SAVE authentic, the emulated machine now stays resident across commands:
      the transient program area survives a warm boot back to A> (as on real
      CP/M), so SAVE n file dumps the image a previous program (e.g. DDT)
      left in memory. Low-memory vectors are reinstalled on each program load so
      a program that trashes page zero can't corrupt the next one.
    • Virtual-modem UART port selection. A new config key cpm_emu_uart
      (wired into the telnet, web, and GUI config UIs, each showing a description
      beside every choice) selects which machine/port address the emulated CP/M's
      modem answers at — off (default), the RC2014/RomWBW Z80 SIO/2 channels
      (rc2014_1arc2014_2b, 0x80–0x87), or the Altair 88‑2SIO / 88‑SIO
      (altair_2sio1/altair_2sio2/altair_sio). Addresses and status-bit
      conventions are taken from the RomWBW SIO driver and David Hansel's Altair
      simulator. With a profile selected, CpmMachine's port I/O answers at those
      addresses with a valid idle UART (transmit ready, nothing received) so
      comms software can probe and initialise the port.
    • Virtual modem — outbound dialling. The CP/M modem now speaks Hayes
      AT and can place calls: ATD A / ATD B dial the gateway's own serial
      Port A / Port B (via the existing peer-dial plumbing, like one machine
      calling another), and ATDT host:port opens a TCP connection. On answer it
      reports CONNECT and becomes a transparent data pipe; +++ returns to
      command mode and ATH hangs up. A new aux profile choice puts the modem
      on the CP/M BDOS AUX: device (functions 3/4) — the hardware-independent
      path for SC126/RomWBW software (a Z180 ASCI port profile can't work: the
      Z80 core doesn't implement the Z180 IN0/OUT0 instructions the ASCI
      uses). The modem is a self-contained async layer bridged to the guest's
      synchronous UART/AUX byte rings at the CPU batch seam.
  • EGT80 — "Ethernet Gateway Terminal", a CP/M terminal of our own. Every
    period CP/M terminal is built for one machine's serial port — commonly a
    separate binary per port, or a build for one specific card, and some generic
    builds carry no serial driver at all — and pairing one with the wrong port
    produces silence rather than an error. EGT80.COM (new EGT80/ directory,
    Z80 assembly, CP/M 2.2 and CP/M 3) asks instead: a menu picks the port at run
    time, from five families — Z80 SIO/2, 6850 ACIA, RomWBW HBIOS (RST 8),
    Z180 ASCI and the CP/M AUX: device — with a free-form address entry for
    boards at unusual ports. It carries a console layer chosen once at startup
    (BIOS vectors on 2.2, BDOS 6 on 3), menus and help written for someone who
    does not yet know which port their machine uses, terminal mode with an
    escape-key menu, an ANSI/ASCII inbound filter (pass escape sequences through,
    or strip them and the high bit for a printing terminal), settings saved into
    its own .COM
    (a 128-byte-aligned patch area rewritten with a
    random-record write, signature- and range-checked at startup and falling back
    to defaults if damaged), and XMODEM transfer both ways — 128-byte blocks,
    CRC-16 with the checksum fallback an older peer may insist on, one buffer
    serving as both protocol block and CP/M record, abortable with a keypress, and
    refusing to leave a partial file behind when a download fails.

    Shipped ready to use: the .COM is compiled into the gateway binary and
    placed on CP/M drive A: when the emulator first creates its drive folders, so
    it is simply there. That copy is never overwritten afterwards, because the
    settings live inside it; deleting it restores the shipped copy. Release
    archives also carry the loose EGT80.COM for sending to real hardware.

    The port screen names the port in force and offers D — the default port
    (the one the gateway also defaults to, so the pair works together again in one
    keystroke), and the menu says (changed — press V to keep it for next time)
    while a setting is live but not yet written to the file. ^C leaves the
    wrong-port notice — which swallows everything else you type, so a half-typed
    line can no longer run menu commands by accident — aborts a transfer in
    progress, and cancels at the filename prompt.

    Built by running the real period assembler (SLR Z80ASM) under zxcc, so
    SLR80 compatibility is structural rather than hoped for, with M80+L80 and ZMAC
    as portability gates. Wrong choices are diagnosed rather than left to hang: a
    port that never accepts a byte is named and explained, HBIOS is refused on a
    machine with no RST 8 vector, and the Z180 ASCI family is refused on a
    processor that is not a Z180 (MLT tells them apart). Two paths cannot be
    exercised by any test here and are documented as reasoned-not-run: the Z180
    ASCI driver (our Z80 core has no IN0/OUT0) and the CP/M 3 console path.

    • Virtual modem — RomWBW HBIOS access (hbios_1 / hbios_2). Some CP/M
      comms software doesn't drive a UART at all: software built for RomWBW asks
      the firmware to move the byte, issuing an RST 8 with a function number in
      B and a serial unit in C. On a port profile such a program hangs before
      printing anything — its first call goes nowhere — which no port address can
      fix; RomWBW-targeted builds of period comms software are exactly this
      case. Two new cpm_emu_uart choices answer that
      API for one serial unit (the virtual modem), so those builds now run: AT
      OK, ATDT host:portCONNECT, with data flowing both ways. The RST 8
      vector is installed only for these profiles, so every other setting keeps
      the untouched page zero of a plain CP/M 2.2 machine; a call for a unit other
      than the selected one is refused, so a mismatch fails the same recognisable
      way a wrong port address does. Implemented: the serial (character device)
      group — in, out, input/output status, initialise, query, describe — plus the
      version and serial-unit-count calls. Refused with an error result: bank
      switching / memory management, disk, RTC, video, sound, DSKY (RomWBW
      hardware services with no counterpart here) — an honest failure at...
Read more

ethernetgateway v0.7.0

Choose a tag to compare

@github-actions github-actions released this 18 Jul 02:14

Added

  • Gateway Shell — a CP/M-inspired file manager over telnet/SSH. A new
    S Gateway Shell item on the File Transfer menu opens an A> command prompt
    that presents the transfer directory as drive A: (flavor A: pure Rust, no
    Z80/.COM emulation). Resident commands DIR/LS, TYPE, DUMP, ERA
    (DEL/RM), REN, COPY (PIP/CP), MOVE (MV), MKDIR (MD),
    RMDIR (RD), CD, PWD, STAT, HELP (?), and EXIT cover full file
    management, including cross-directory copy/move via a /-separated path
    syntax the base CP/M command set can't express, and */? wildcards for
    DIR/ERA/COPY. TYPE/DUMP/DIR paginate with a --More-- prompt, and
    TYPE refuses binary files. Every operand is jailed to the transfer directory
    (validated + canonicalized; ../absolute/symlink escapes are refused); copy/
    move honor the 8 MB transfer cap and the TYPE/DUMP viewers cap reads at
    1 MiB. Works identically over telnet and SSH. Documented in user manual §8.10.
    (Flavor B — a real Z80 CP/M 2.2 emulator — remains deferred.)
  • Third-party license notices and a license-policy gate. THIRD-PARTY-NOTICES.md
    (generated by cargo-about from
    about.toml + about.hbs) reproduces every dependency's copyright notice and
    license text. A new CI licenses job runs
    cargo-deny against the
    GPL-3-compatible allowlist in deny.toml and gates the build (no
    continue-on-error), so a GPL-3-incompatible or unknown-licensed dependency
    can't land silently. README documents the workflow.

Changed

  • Added a full "as is" / no-warranty disclaimer at the top and bottom of
    both README.md and the user manual (usermanual.html), including a note
    that portions of the project were developed with the assistance of AI tools.
  • README.md condensed to a quick-start + feature overview (~1815 → ~190
    lines). The reference material that duplicated the user manual (the annotated
    egateway.conf dump, AT-command / S-register tables, telnet RFC compliance
    matrix, release-verification and systemd walkthroughs, per-distro build
    matrix) now lives only in the manual, with links; the repo-unique hardware
    quick-start, security posture, and license are kept, and the screenshot is
    surfaced near the top.
  • User manual: documented kercpm3's Password: prompt on remote cd.
    CP/M Kermit clients prompt for the optional Kermit CWD password (Protocol
    Manual §6.7 second field) before sending the G C packet; the gateway's
    Kermit server is unauthenticated by design and ignores that field, so the
    directory change always succeeds — press Enter. Also noted the
    recognised-but-unsupported USER command in the Gateway Shell (§8.10).

Fixed

  • Gateway Shell: DIR SUB now lists the subdirectory's contents (like
    DIR SUB/) instead of just showing the <DIR> entry — a wildcard-free
    operand that names an existing directory is listed, matching the DOS/Unix
    expectation. DIR name, DIR *.pat, and DIR file are unchanged.
  • Gateway Shell: name resolution is now case-insensitive. DIR shows names
    uppercased, so a directory stored on disk as z80asm displayed as Z80ASM
    and CD Z80ASM then failed "No such directory." (only the lowercase form
    worked) — and PETSCII terminals swap case on the wire, compounding it.
    CD/TYPE/DUMP/ERA/STAT/REN/COPY/MOVE source operands now match
    an existing name case-insensitively (exact case wins, else the first
    case-insensitive hit) and resolve to the real on-disk name; newly created
    names keep the case as typed. Still fully jailed to the transfer directory.
  • Gateway Shell: the A> prompt after HELP now appears on its own line. The
    help pager's "Press any key" was dismissed with the cursor mid-line, so the
    returning prompt was glued to it (Press any key.A>); the pager now advances
    to a fresh line before returning (harmless for the menu callers, which
    redraw).
  • PETSCII: declining color no longer drops a Commodore terminal to ASCII.
    Color was tracked implicitly by the terminal type, so answering "N" to the
    color prompt forced TerminalType::Ascii — which also discarded PETSCII's
    40-column, case-swapped, ANSI-stripped layout, leaving a C64 caller in an
    80-column ASCII view. Color is now a separate color_enabled flag: declining
    color keeps the detected terminal type (PETSCII stays PETSCII) and simply
    emits plain text. Also makes the SSH/telnet gateway's PETSCII handling correct
    for a no-color C64. Side effect for ANSI terminals: declining color now
    means "no color in the gateway's own menus" rather than "downgrade to ASCII,"
    so remote-host ANSI passed through the SSH/telnet gateway is no longer stripped
    and the onward terminal is advertised as xterm instead of dumb — the
    correct behavior for a terminal that answered the ANSI-color prompt.
  • Serial: a serial-manager thread can no longer panic on a dropped runtime
    across a config restart (round-7 review).
    The detached serial threads
    block_on the tokio runtime, but a SIGHUP restart dropped the runtime
    without joining them; a thread stuck in the synchronous connect_timeout
    (an in-flight ATDT/peer dial to an unresponsive host, up to 60 s) would then
    panic on its next block_on. The dial now connects asynchronously, raced
    against the shutdown/restart flag (aborting within ~100 ms instead of being
    blind for the whole carrier wait), and main bounded-joins the serial
    threads before dropping the runtime. Self-healing before; airtight now.
  • Web browser: a hostile page can no longer soft-DoS the render thread with
    form-label lookups (round-6 F1).
    Each form field with an id but no
    placeholder/aria-label/title triggered a full recursive walk of the form
    subtree looking for <label for="id"> — O(fields × subtree), so a page of
    tens of thousands of bare <input id=…> (under the 1 MB body cap) cost
    quadratic CPU on a shared spawn_blocking thread with no render time budget.
    Labels are now collected in a single O(subtree) pass into an id→label map;
    per-field lookup is O(1).
  • Weather: a malformed forecast timestamp no longer panics the fetch
    (round-6).
    The MET.no forecast parse sliced the first 10 bytes of the
    time field guarded by byte length only; a timestamp with a multibyte char
    in the first 10 bytes would panic on a mid-character boundary. It now uses a
    char-boundary-safe accessor and skips a malformed entry. (Was contained to a
    single weather fetch by spawn_blocking, never a process crash.)
  • Serial: dialing a console-mode port relayed to the master no longer wedges
    the caller's serial thread (round-5 review).
    On a slave gateway with
    allow_peer_dial on, ATD <ConsolePort>@<local-ip> reached a local console
    bridge that nothing services (the console port runs the master-registration
    loop, not the local bridge), so the caller's thread blocked forever on the
    bridge oneshot — unrecoverable short of a full restart. connect_local_peer
    now fails that dial fast with NO CARRIER (mirroring the Serial Gateway
    picker's exclusion), and the console-bridge request is additionally raced
    against a shutdown/restart poll so no path can pin the thread.
  • Relay: IPv6 onward-dial targets are handled correctly (F1). The onward-
    dial path split/rebuilt host:port with a bare rsplit_once(':'), leaving
    brackets on an IPv6 host so connect failed. A shared split_dial_host_port
    now parses [2001:db8::1]:6400 into a bare literal, the slave brackets IPv6
    on the wire, and both halves agree (unbracketed IPv6 is rejected as
    ambiguous). IPv4/hostname dialing is unchanged.
  • Telnet: a session-slot / broadcast-writer leak on a panicking session is
    now prevented (F3).
    Slot release and writer de-registration ran only after
    session.run() returned, so a future reachable panic would leak a
    max_sessions slot and grow the broadcast list unbounded. A RAII backstop
    (SessionSlotGuard) now reclaims both on unwind; the normal path defuses it
    after the graceful async cleanup. (No panic is reachable today — this is
    defensive hardening consistent with the SSH/relay Drop guards.)
  • Kermit receive: windowed receiver now ACKs buffered out-of-order packets
    (selective repeat, spec §5.5) (K1).
    Previously it buffered a correctly-
    received future packet but only NAKed the missing expected_seq — once per
    future — never ACKing the good packet; the windowed sender counted each
    duplicate NAK as a retry and re-sent packets it didn't need to. The receiver
    now ACKs each future by its own seq and NAKs the gap once, matching the spec
    and removing the redundant retransmissions (and the retry-budget pressure a
    reordering link could put on a large window). Live C-Kermit 10.0 sliding-
    window interop unchanged.
  • Punter receive: a premature-retransmit duplicate no longer corrupts the
    file (P1/P2).
    On a slow/jittery link a data block whose first byte was
    delayed past the byte-wait could trigger an early S/B resend; the delayed
    block and a re-sent copy would both arrive, and the receiver — which ignored
    the block index for sequencing — appended the interior block twice,
    returning a silently one-block-too-long file. The receiver now dedups on the
    checksum-protected NUMPOS block index (dropping a block whose index did not
    advance), bounded so a peer stuck re-sending one block still gives up. Verified
    against the live CCGMS reference (both directions) — the dedup never triggers
    on conforming traffic.
  • Punter: a mid-IAC-sequence timeout in a handshake window is now recoverable
    (P3).
    accept_code treats the tnio IAC-timeout (N4) as a soft "no code
    this round" re-probe — matching read_block — instead of aborting the whole
    transfer; other read errors still abor...
Read more

ethernetgateway v0.6.4

Choose a tag to compare

@github-actions github-actions released this 14 Jul 18:56

Added

  • Serial ports gain a third mode: Kermit Server. Alongside Modem
    (AT Command) Mode
    and Telnet-Serial Mode, each serial port (A/B) can
    now run as an always-on Kermit server: as soon as the port is enabled it
    listens for Kermit packets directly on the wire — no AT commands, no
    dialing, no menu. It is the same server ATDT KERMIT reaches from the
    modem emulator, but always on and with no AT layer; received files land
    in transfer_dir, and it re-arms after every FINISH/BYE so the wire stays
    a live server. The port reopens automatically if the device disappears
    (matching modem mode). Selectable from the GUI Mode dropdown, the web
    config's per-port "More…" popup, and the telnet per-port T toggle
    (which now cycles Modem → Console → Kermit). Persists as
    serial_a_mode / serial_b_mode = kermit. Auth and the telnet menu are
    bypassed by design — enable only on trusted serial lines (same posture as
    allow_atdt_kermit).
  • YMODEM receives multi-file batches (sb file1 file2 …). The receiver
    previously ran the end-of-batch handshake right after the first file's EOT,
    so a batch sender lost every file after the first (and could hang waiting for
    the receiver). It now reads the next block 0 at each EOT — a named block 0
    starts the next file, the null block 0 ends the batch — and returns every
    file. The first file keeps the user-entered name; files 2..N use the sender's
    (sanitized) block-0 name, saved atomically like ZMODEM/Kermit batches. A
    corrupt inter-file block 0 is NAK-retried (bounded), the batch is capped at
    1000 files, and a non-UTF-8 file name is received under a generated name
    rather than truncating the batch.
  • Weather works worldwide, not just US zip codes. The Weather menu now
    accepts any city name or postal code (London, SW1A 1AA, Zürich,
    62051), percent-encoding the query so spaces and non-ASCII are safe. A
    City, Country or City, Region qualifier disambiguates common names
    (London, GB vs London, Ontario; Paris, France vs Paris, Texas), and
    the matched country is shown. A new weather_units setting — auto
    (default: Fahrenheit/mph for the US, Celsius/km/h elsewhere), us, or
    metric — controls display units; press U on the weather screen to cycle
    them in place (no re-fetch). Wired into the telnet Other-Settings menu, the
    web config page, and the GUI. The config key weather_zip is renamed to
    weather_location; an existing weather_zip value migrates automatically
    on first load, and any saved location persists across sessions as before.
  • Configurable desktop GUI display scale (gui_zoom). The console window
    now honors a gui_zoom setting: auto (default) follows the monitor's own
    scale factor as before, while a number (e.g. 1.0, 1.25, 0.8) pins the
    window's pixels-per-point absolutely so a display that reports an inflated
    DPI no longer renders the GUI oversized. Selectable as "Display scale" from
    the GUI's Server → More panel and the web config's Server → More page
    (Auto / 75% / 100% / 125% / 150% / 200%), and clamped to 0.5–3.0.
  • Show the file being downloaded on the SELECT PROTOCOL screen. The
    download protocol picker now displays the file name (truncated to the
    terminal width) and byte size above the protocol list, so the user can
    confirm the right file before choosing a protocol.
  • Make directories from the telnet file-transfer menu. A new M option
    creates a subdirectory inside the current transfer working directory (the
    name is validated like a filename — a single component, no .. or /), then
    asks whether to make it the working directory.
  • Weather falls back to MET Norway when Open-Meteo is unreachable. If the
    Open-Meteo forecast host can't be reached, the Weather menu now automatically
    retries the forecast against MET Norway (api.met.no Locationforecast 2.0 —
    free, no API key, independent infrastructure), reusing the coordinates
    already geocoded via Open-Meteo (worldwide coverage, so the fallback works for
    any location). MET's data is kept in metric and converted to your chosen units
    at display time, and its symbol codes mapped to descriptions; you only see an
    error if both providers fail.
  • Wait for the receiver before starting a Kermit download
    (kermit_wait_for_receiver, default on).
    A Kermit transfer is
    receiver-driven at the start — the receiving side sends a NAK to solicit the
    sender's Send-Init (Frank da Cruz, Kermit Protocol Manual §4). The gateway
    now holds its Send-Init until that poke arrives and then sends exactly one, on
    both interactive downloads and Kermit server GET responses. A client that
    never pokes (e.g. C-Kermit) falls through a short bounded wait and gets an
    unprompted Send-Init as before. Wired into the telnet Kermit-settings menu
    (G), the web config page, and the GUI.
  • Verbose Kermit receive-path logging. With verbose = true, the Kermit
    upload/receive path now emits periodic per-packet progress plus a
    per-file summary (bytes, packets, block-check type), matching the diagnostic
    style of the XMODEM/YMODEM/ZMODEM paths. Off by default.

Changed

  • Warning popups are now dark red (GUI and web). Security/confirmation
    warnings previously looked identical to ordinary popups, so it wasn't obvious
    the modal was blocking the next click and had to be acknowledged. The GUI's
    four warning popups (ATDT-KERMIT, Kermit-server, disable-IP-safety,
    master-needs-SSH) now use a dark-red panel + red border. On the web, the
    native confirm()/alert() warnings (disable web server, change web port,
    master-needs-SSH) are replaced with matching dark-red modal dialogs whose
    overlay blocks the form until the operator chooses Continue/Cancel. The web
    also gains the enable-guard warnings it was missing versus the GUI —
    Disable IP Safety, Kermit Server, and Allow ATDT KERMIT now raise
    the same red confirmation before they take effect.
  • ZMODEM batch receive is capped at 1000 files (MAX_BATCH_FILES, matching
    YMODEM and Kermit), so a peer that streams endless files can't grow the
    in-memory batch without bound; the receiver cancels and errors past the cap.
  • Config UI: tidier frames via "More" popups. The web config page now keeps
    the Master/Slave relay settings under the Server frame's More… popup
    (they were a separate card), matching the GUI and returning the page to six
    frames. The AI Chat, Browser, and Weather frame (both web and GUI) is now
    three rows — API key and homepage on the frame, with a More… button that
    opens the weather location and units.
  • Weather fetch fails fast with a clearer message. The Open-Meteo request
    now uses a 5 s connect timeout (was a single 15 s global) and retries once on
    a transient transport failure, so an unreachable/blocked forecast host no
    longer hangs the Weather menu for 15 s. Errors are distinguished:
    "Not found - try 'City, Country'." (no geocoder match) vs "Weather service
    unreachable. Try again later." (network/host down) vs "Weather service
    returned bad data." (parse).

Fixed

  • Serial Kermit Server Mode transfers at full speed. Kermit Server Mode was
    far slower than the same server launched from the File Transfer menu. The
    bridge that pumps bytes between the wire and the Kermit server
    (run_console_bridge) drained its outbound queue only between wire reads,
    and Kermit is stop-and-wait — so while the gateway composed each reply the
    wire sat idle and the bridge blocked out the full wire-read timeout before
    writing that reply, adding a fixed delay to every gateway-originated packet
    (each ACK when receiving, each DATA when sending — hence the slowdown in both
    directions). The menu/ATDT KERMIT server never had this because its pump
    produces and flushes the reply in the same iteration that consumed the
    request. Server Mode now uses that same inline pump (run_kermit_bridge_inline,
    modeled on the modem online-mode path) instead of the decoupled two-thread
    bridge, so a reply leaves the wire the moment it's produced. (The interactive
    Serial Gateway console still uses run_console_bridge, whose backpressure
    design is intentional there.)
  • Kermit server uploads no longer drop a file on a name collision. When an
    uploaded file's name already exists in transfer_dir, every Kermit-server
    receive path (the telnet-menu server, the standalone TCP listener, and the
    new serial Kermit Server Mode / ATDT KERMIT) now renames the incoming file
    DOS/CP-M-Kermit style instead of skipping it — the base name is numbered
    within 8 characters the way CP/M Kermit clients (e.g. kercpm3) do on a
    download collision: abcdefgh.txtabcdefg0.txtabcdefg9.txt
    abcdef10.txt, and a shorter name such as hi.txthi0.txt. The original
    file is never overwritten, and a verified resume still replaces its own
    partial in place. (The pre-existing telnet-menu and TCP-listener paths
    previously skipped such a collision with an "already exists" note.)
  • ATDT KERMIT uploads now actually save to disk. The serial Kermit-server
    dial path passed a no-op file-commit hook to the server, but the Kermit
    receiver only buffers uploaded files in memory and relies on that hook to
    persist them — so a client send/put over ATDT KERMIT completed on the
    wire but left nothing in transfer_dir (downloads/get, which read from
    disk, were unaffected). Both the always-on serial Kermit Server Mode (new,
    above) and the ATDT KERMIT dial now commit each received file with the same
    filename / subdir path-safety validation as the telnet and TCP-listener
    Kermit server paths.
  • Kermit server GET is now case-insensitive. A client requesting a file in
    a different case than it is stored on disk — CP/M clients such as kercpm3
    uppercase filenames — no longer fails "File not found" and bu...
Read more

ethernet-gateway v0.6.3

Choose a tag to compare

@github-actions github-actions released this 03 Jul 17:37

Added

  • The desktop GUI remembers its window position and size. The
    configuration window now reopens where you last left it — its outer position
    and inner size are saved (debounced) to gui_window_geometry in
    egateway.conf and restored on the next launch. It is auto-managed: there is
    no config-UI field for it, and an empty value means "use the default size and
    let the window manager place it." Works on X11/Windows/macOS; Wayland
    compositors don't expose a window's position, so it isn't remembered there.
  • Peer-dial: call another serial port directly. With the new
    allow_peer_dial opt-in (default off; wired into telnet Configuration > M >
    P
    , web, and GUI), a modem-mode port can dial another port by address —
    ATD <Port>@<IP> (e.g. ATD B@192.168.1.50) — or select that port in the
    Serial Gateway menu, and bridge straight through to the device on it (the
    gateway equivalent of calling a friend's modem). A modem-mode target
    rings and answers per its own AT rules (S0 auto-answer / manual ATA); a
    console-mode target connects directly. The connection is a transparent
    byte pipe, so a file-transfer protocol runs end to end between the two
    devices. Result codes follow ATX (CONNECT/BUSY/NO ANSWER/NO CARRIER).
    Works on the same gateway and, over the master/slave relay, from a slave
    device to a port on its master
    (ATD <Port>@<master-ip>): the slave relays
    the call and the master resolves the address to one of its own ports and
    rings/connects it (gated by the master's master_accept_relays +
    allow_peer_dial). Cross-gateway is symmetric: the master routes a peer
    address to any port a slave has registered — a slave's console port
    and its modem port (a slave modem port announces itself to the master and,
    when dialed, rings the attached device) — so <Port>@<slave-ip> reaches a
    slave's port from the master or, via the master as a crossbar, from another
    slave (device ↔ slave-A ↔ master ↔ slave-B ↔ device). Addressing is by IP, so
    gateways need distinct addresses (normal for separate machines). See README
    "Peer-Dial" and user manual §9.2.3.
  • Live relay status in the telnet Master/Slave screen. A master now lists
    the remote console ports slaves have registered (so you can see connected
    slaves at a glance); a slave shows each console port's link state to its
    master (down/connecting/registered/bridging) — relay connectivity is
    now visible without reading the logs.
  • Relay channel handshake / protocol version. The master now writes a small
    hello (EGR magic + a protocol-version byte) as the first bytes on every
    accepted master/slave relay or console-registration channel; the slave
    validates it before using the channel. A master/slave version skew now fails
    cleanly with an "upgrade the older gateway" message instead of desyncing, and
    a slave pointed at a master that is declining relays (standalone,
    master_accept_relays=false, or at capacity) now detects the refusal — the
    absence of the hello — and backs off with a clear message, instead of
    mistaking the refused-but-open channel for a live registration and idling.
  • Optional hardware carrier (DCD) signalling. New per-port opt-in
    serial_a_drive_carrier / serial_b_drive_carrier (default false; also a
    checkbox in the GUI/web config and the C key in the telnet per-port modem
    menu). When enabled, the modem emulator drives DTR as a carrier proxy
    (a PC/USB-serial adapter is a DTE and can't drive a DCD output, so you cross
    DTR→DCD in a null-modem cable, as tcpser does), following AT&C: &C0 forces
    it always asserted while the port is open, &C1 (default) asserts on
    CONNECT and drops on NO CARRIER / ATH / hangup / relay-link-loss (so a
    slave-attached machine sees loss-of-carrier in hardware too). When off, the
    gateway makes zero modem-control-line calls
    , so ports without DCD wiring are
    byte-for-byte unaffected. Modem mode only.
  • Master/Slave serial extender (optional). A gateway set to
    gateway_role = slave extends its serial ports to a master gateway over
    the master's existing SSH port; the serial device reaches the master's menu,
    file transfer, and dial-out as if attached to the master, and files always
    land on the master
    . Default gateway_role = standalone leaves the feature
    entirely inert. Modem-mode ports relay on connect (the slave resolves its
    local dial map; the master dials onward — "resolve local, dial central");
    console-mode ports register with the master and appear in the master's Serial
    Gateway picker (local ports + registered remote ports). New config keys
    (telnet/web/GUI): gateway_role, master_accept_relays, slave_master_host,
    slave_master_port, slave_master_username, slave_master_password,
    relay_transport (only ssh implemented). The slave authenticates with the
    master's unified username/password and pins the master's SSH host key (TOFU,
    in gateway_hosts); relay connections are gated by master_accept_relays and
    count against the session cap. The slave's main menu shows a SLAVE-mode notice
    with the master address, and reconnects automatically if the link drops.
  • Serial sessions can now receive administrative broadcasts. A process-global
    broadcast channel (serial::broadcast_to_serial) fans a message out to every
    open serial port, delivered at the command prompt only — an in-call
    (online) serial session, which may be carrying a binary file transfer, drains
    its queued messages when it next returns to command mode (+++, hangup, or
    call end) so a notice can never corrupt a transfer. This is the serial-side
    counterpart to the telnet/SSH/relay broadcast_to_sessions list, completing
    broadcast coverage across all connection types. The shutdown "Goodbye" keeps
    its own reliable shutdown-flag write (which fires even mid-online) and is not
    routed through this channel. Modem mode only. (Extension point: no production
    broadcast is routed to it yet — the first admin-notice feature plugs in here.)

Fixed

  • Serial AT&C now updates the hardware carrier (DCD/DTR) line immediately.
    With serial_X_drive_carrier enabled, changing AT&C at the command prompt
    used to take effect only at the next connect/hangup; it now re-applies the
    DCD line right away — &C0 asserts DTR (carrier forced on regardless of call
    state) and &C1 restores follow-the-carrier — matching the documented
    contract and the existing ATZ/AT&F behavior. Found during on-hardware DCD
    validation (DTR→DCD crossover).
  • GUI console started as a boot service now waits for the window manager.
    When launched as a boot-time systemd service, the console window could come
    up undecorated (no title bar / minimize / close) or with its title bar tucked
    under the desktop panel, because it opened as soon as the X server accepted a
    connection — before the window manager had taken over decoration and
    placement. The display-wait now also waits (bounded, X11-only) for an EWMH
    window manager (_NET_SUPPORTING_WM_CHECK on the root window) before opening
    the window. Degrades safely: no xprop, a bare X server, or a non-EWMH WM
    falls through after a short cap and opens anyway, and the server is never
    delayed (only the window waits). Non-X11 targets (Windows, macOS, headless,
    pure-Wayland) are unaffected — the wait returns immediately without DISPLAY.
  • Serial Gateway menu shows peer-dial addresses without spaces around @.
    Remote (slave) port entries are now displayed as <Port>@<ip> — exactly the
    string you type to dial them (ATDT <Port>@<ip>). The previous spaced form
    (<Port> @ <ip>) invited mistyped dial strings with embedded spaces. The
    remote-bridge screen title and the master's registered-ports status list were
    unspaced to match.
  • Master/Slave configuration now guides the operator by role. Across the
    telnet menu, web, and desktop GUI, fields that don't apply to the selected
    role are greyed out / disabled: accept relays is editable only for a
    Master (and now defaults on when you switch to Master, since a master
    with it off can't accept slaves), while the master host / port / user / pass
    are editable only for a Slave. Switching to Master while the SSH server is
    off now surfaces a warning (a popup in web/GUI, a dedicated screen in telnet)
    explaining that slaves connect over SSH — it points you at the setting but
    never toggles SSH for you.
  • Peer-dial now reminds you about local echo. A peer-dial connection is a
    transparent link with no host echoing keystrokes back, so the Serial Gateway
    picker shows a "enable local echo to see typing" tip, and the README /
    user-manual peer-dial sections explain that each terminal needs local echo
    (half-duplex) — and that ATE does not affect the online data path.
  • Shutdown "Goodbye" now reaches every session, not just when telnet is
    enabled.
    The shutdown broadcast used to live inside the telnet accept loop,
    so an SSH-only deployment (telnet_enabled = false) tore SSH and relay
    sessions down with no notice. It is now a transport-neutral broadcast invoked
    centrally at shutdown, so telnet, SSH, and master/slave relay sessions all
    receive it for any combination of enabled servers (serial ports already emit
    their own notice). The mechanism is reusable for future all-session messages.
  • File transfers over telnet no longer apply NVT CR-NUL stuffing, which
    corrupted binary transfers through telnet↔serial bridges (e.g. tcpser) and
    telnet-aware WiFi modems that don't symmetrically un-stuff. The shared
    transfer I/O layer (tnio, used by XMODEM/YMODEM/ZMODEM/Kermit/Punter) now
    escapes only IAC (0xFFIAC IAC) and passes every other byte —
    including CR (0x0D) — through literally, matching RFC 856 binary-transmission
    semantics that 8-bit file transfer req...
Read more

ethernet-gateway v0.6.2

Choose a tag to compare

@github-actions github-actions released this 19 Jun 23:41

Added

  • Session cap and idle timeout are now editable from the telnet Server
    Configuration menu
    (the C and D keys), matching the desktop GUI and the
    web configuration page that already exposed max_sessions /
    idle_timeout_secs — completing three-UI parity for both settings. Idle
    timeout accepts 0 to disable the idle disconnect. The screen's detected-IP
    hint list is now capped so the new row keeps the PETSCII menu within its
    22-row budget even on a multi-homed host (it previously overflowed at three or
    more private addresses).

Security

  • Fixed an SSRF-guard bypass for IPv6-literal URLs in the text-mode web browser. guard_public_url classified IP literals with IpAddr::parse, but url::Url::host_str() returns IPv6 literals bracketed (e.g. [::1]), which fails that parse and fell through to the resolver path — allowing http://[::1]/, http://[::ffff:127.0.0.1]/, and the like to reach loopback / link-local / internal IPv6 services (initial request and every redirect hop). The guard now strips the brackets before classifying, blocking the entire internal IPv6 space. Regression test added. IPv4 literals and DNS names were already handled correctly.
  • SSH: an unauthenticated connection no longer consumes a session slot. new_client incremented the session counter for every inbound TCP connection, before authentication, so a peer that opened many transport handshakes and stalled could exhaust max_sessions and lock out real users. The slot is now claimed only on a successful login (atomic fetch_add + rollback, mirroring the telnet accept loop) and released only if it was claimed — and the cap is now exactly max_sessions (was off-by-one, max_sessions + 1).
  • Web config: POST /save now enforces a same-origin check (CSRF defense-in-depth). A request whose Origin/Referer doesn't match our Host is rejected with 403, blocking a malicious page from riding the operator's cached Basic-auth credentials to rewrite config (including disabling auth). Requests with neither header (non-browser clients such as curl, which can't be a CSRF vector) are still allowed; Basic auth continues to gate them. Lenient-on-absent by design for the trusted-LAN threat model.
  • Kermit server: defense-in-depth subdir re-validation on save. Both the in-session receiver and the standalone (auth-bypassing) Kermit listener now re-check rx.subdir with is_safe_relative_subdir before joining it to the transfer dir. No live traversal existed (subdir is only set after that same check inside the Kermit module), but re-validating at the save site closes the door on any future producer-side bypass — the same belt-and-suspenders rationale as the existing filename re-check.

Fixed

  • Serial console bridge: a stalled telnet peer can no longer wedge server shutdown / port restart. The dedicated serial-reader thread used an unbounded blocking_send onto a bounded channel; when a bridged peer stopped reading and the channel filled, the thread parked past its shutdown/restart checks. It now polls with try_send + a short sleep, bailing on shutdown/restart or when the async pump drops its receiver.
  • Serial modem online mode (TCP): a remote host that stops reading no longer blocks shutdown. online_mode_tcp set only a read timeout, so a full remote receive window parked write_all indefinitely with the loop's shutdown/restart checks unreachable. A 5 s write timeout is now set (matching the duplex path); an expiry drops carrier (NO CARRIER).
  • XMODEM/YMODEM: YMODEM block 0 is now always validated as CRC-16. If block 0 took enough retries to cross the negotiation's CRC→checksum fallback point, the block-0 body (and then the data phase) could be misread as a 1-byte checksum, NAK-looping a CRC-only YMODEM sender to exhaustion. The block-0 read and the post-block-0 data phase are now pinned to CRC-16.
  • Logging survives a poisoned lock. logger now recovers a poisoned mutex (into_inner) instead of silently dropping the line — matching config.rs / gui.rs, and most valuable exactly when a thread has just panicked.
  • Kermit streaming: a sequence-aliased NAK now aborts cleanly instead of silently corrupting the file. In streaming mode the whole file sits in the sender's outstanding-packet set with wrapping (mod-64) sequence numbers, so a file larger than ~64 chunks aliases each seq across many packets. On a genuine mid-stream NAK/loss the sender matched the NAKed seq to the first (oldest) outstanding packet sharing it and retransmitted that stale packet; the receiver appends D-packets by sequence with no position field, so it landed the wrong data at the wrong offset. This was benign on lossless TCP/SSH (streaming's intended transport, where NAKs don't occur) and only reachable on an unreliable link such as a serial bridge. An unresolvable NAK now aborts with an actionable error ("disable kermit_streaming for this peer"); the timeout-driven retransmit path skips aliased seqs for the same reason. The reliable-transport happy path is unchanged.
  • ZMODEM: ZFERR (0x0C) is now handled instead of ignored. A sender's file read/write-error frame aborts the receive cleanly with an informative error rather than falling through to the ignore arm and waiting out a frame timeout. Every Forsberg 1988 frame is now handled.
  • Text-mode web browser: fixed a remote-triggerable panic on Back. Returning to a previous page whose re-fetched content is shorter than the saved scroll position could index past the page and panic the session task. The scroll position is now clamped on restore and again defensively at render time.

Documentation

  • Documented ZMODEM ZCOMMAND (frame 0x12) as the one optional spec frame deliberately not implemented — it is recognized but always refused (non-zero ZCOMPL), since arbitrary /bin/sh -c execution on a shared, long-lived host is an unacceptable default; use SSH for shell access. Noted in the user manual and the ZMODEM web reference.
  • Documented previously-undocumented config keys: web_enabled, web_port, gateway_debug, and ssh_gateway_auth in the README config reference, and punter_max_bad_rounds / punter_hangup_on_failure in the user manual. Added the now-handled ZFERR frame to the ZMODEM web reference, and corrected the SSH reference's auth_password lifecycle description to match the new claim-slot-on-successful-login behavior.
  • README config-reference completeness pass: the "All options" egateway.conf sample now lists disable_ip_safety and the per-port serial_a_petscii_translate / serial_b_petscii_translate keys (all three are written by the config saver), the telnet Server-Configuration menu walkthrough documents the new session-cap / idle-timeout keys, and the Other Settings list now includes the gateway debug-trace toggle.

ethernet-gateway v0.6.1

Choose a tag to compare

@github-actions github-actions released this 06 Jun 21:31

Added

  • Raspberry Pi 4+ (aarch64 Linux) build — releases now ship an
    Ethernet_Gateway-aarch64.AppImage alongside the existing
    x86_64 Linux / Windows / macOS artifacts, built on a native arm64
    runner. Two ARM-only desktop-GUI fixes make it run on the Pi's
    VideoCore/V3D GPU: the wgpu device now requests exactly the limits
    the adapter advertises (so startup no longer aborts with
    "Limit 'max_color_attachments' value 8 is better than allowed 4" or
    the equivalent for other limits), and the GUI prefers the OpenGL ES
    backend instead of the Pi's incomplete Vulkan driver (which panicked
    with "Requested feature is not available on this device").
    WGPU_BACKEND still overrides. Other platforms are unaffected.
  • Punter (C1) file-transfer protocol — the protocol CCGMS /
    Novaterm / StrikeTerm speak natively on Commodore BBSes, added
    alongside XMODEM/YMODEM/ZMODEM/Kermit. Single-file C1 with the full
    two-phase (file-type then data) handshake, both block checksums
    (16-bit additive + cyclic), the "size of next block" framing, and
    the three-S/B end-off real C1 endpoints expect. Selectable in the
    telnet upload/download protocol pickers; the outbound PRG/SEQ file
    type is auto-detected from the filename. New punter_* tunables
    (block size, timeouts, retries) are editable from the telnet File
    Transfer settings menu, the web configuration page, and the desktop
    GUI, and persist to egateway.conf. The send/receive entry points
    take an open stream so a future Multi-Punter (MPP) batch wrapper can
    layer on without touching the wire code.
  • Serial modem AT+PETSCII=n command — toggles PETSCII⇄ASCII
    translation on direct-TCP dials (AT+PETSCII=1 on, AT+PETSCII=0
    off) so a Commodore 64/PET dialing ATDT host:port sees readable
    text instead of raw ASCII. Set-only, in the ITU-T V.250 +
    extension namespace (&P is the pulse-dial make/break ratio on real
    Hayes modems, so it is intentionally left alone). AT+PETSCII=1
    persists the setting immediately; AT&V reports it as +PETSCII:n.
  • PETSCII translation is now editable from every configuration
    surface
    — the per-port modem screen in the telnet/serial-console
    menu, the web configuration page, and the desktop GUI — in addition
    to the AT command. It is a per-serial-port setting saved to
    egateway.conf.
  • Serial: inbound PETSCII punctuation normalizer, and the C64 PETSCII
    DEL key (0x14, INST/DEL) is accepted as a command-line backspace
    when PETSCII translation is active. +++ escape sequences are
    traced when the gateway debug trace is on.
  • Persisted gateway_debug byte-trace flag, toggleable from the
    GUI/web General frame and the telnet Other Settings / Serial
    Configuration menus. Read fresh per gateway session (no restart
    needed); EGATEWAY_GATEWAY_DEBUG still forces it on. The trace now
    timestamps each input byte.
  • Web protocol reference pages served by the configuration web
    server — per-protocol references (XMODEM, YMODEM, ZMODEM, Kermit, the
    Hayes AT command set, and telnet), each documenting that protocol's
    retry/recovery behavior, plus character-set and ANSI escape-sequence
    references, reachable from a new References nav entry.
  • Kermit resume and locking-shift settings are now editable from
    the telnet Kermit settings menu, the web configuration page, and the
    desktop GUI (previously egateway.conf-only).
  • punter_hangup_on_failure — optional drop-carrier-on-give-up for
    Punter, editable from the telnet / web / GUI Punter settings. Because
    C1 has no in-band abort, a give-up otherwise leaves the C64 hung;
    enabling this drops carrier so it sees loss-of-carrier instead.

Fixed

  • AI chat: a follow-up question that merely starts with a menu command
    letter (e.g. "Quantum…") is no longer swallowed by the answer-screen
    navigation. A lone command letter still navigates; any longer line
    is sent to the model.
  • Transfer retry/recovery brought to strict spec. XMODEM/YMODEM now
    NAK on a data-phase inter-block timeout (re-prompting the sender) and
    cancel with CAN×3 on a non-duplicate block-sequence error instead of
    NAK-looping; ZMODEM routes every data-phase error through one bounded
    counter that re-sends ZRPOS and resets on progress (no infinite ZRPOS
    loop on a permanently-corrupt stream); Kermit emits an Error packet
    when it gives up so the peer is told rather than left waiting.
  • Punter no longer strands a peer on a failed transfer. A cancel /
    restart from the C64 side is tolerated (longer pre-transfer input
    drain), and corrupt-block recovery is bounded by its own larger round
    cap rather than quitting early and leaving the peer hung.

Changed

  • Removed the duplicate Port A/B status banner from the main
    configuration menu — per-port mode is already shown under Serial
    Configuration.
  • Punter bad-block cap decoupledpunter_max_bad_rounds (default
    30) bounds consecutive corrupt-block resend rounds separately from
    punter_max_retries, since a real C64 peer never caps resends and a
    low shared cap made the gateway give up first and strand it.

Security

  • Updated russh 0.60.2 → 0.60.3 to clear two high-severity
    (CVSS 7.5) allocation-DoS advisories in the SSH stack:
    RUSTSEC-2026-0154 (russh unbounded 32-bit allocation) and
    RUSTSEC-2026-0153 (russh-cryptovec unchecked CryptoVec
    allocation/growth). A malicious SSH client could otherwise drive
    unbounded memory allocation on the SSH listener.

ethernet-gateway v0.6.0

Choose a tag to compare

@github-actions github-actions released this 24 May 09:56

Added

Configuration web server

  • Optional HTTP listener that renders the same settings page the
    desktop GUI does, in a browser. Off by default; toggle in the GUI
    Server frame (new "Web Server" row between Telnet and Kermit) or
    the telnet Configuration > Server Configuration menu's
    W / B keys. Port defaults to 8080.
  • Hand-rolled HTTP/1.1 on tokio (no new dependencies) implementing
    GET / (settings page), GET /logo.png (the same logo the GUI
    uses), GET /logs (2-second polled log tail), GET /serial-ports
    (live device enumeration for the dropdown refresh), and
    POST /save (config persist + optional restart).
  • Per-frame Save buttons matching the GUI's three behaviors:
    Server's Save and Restart (full server restart cycles through
    main.rs exactly the way the GUI does), Serial's Save (just
    reloads serial managers via serial::restart_all_serial), and the
    plain Save on every other frame (persist only). Unknown action
    values fall back to plain Save so a hand-crafted POST with a typo
    can't accidentally restart the server.
  • POST → 303 See Other → GET pattern: the save handler redirects
    to /?notice=Configuration%20saved. so a browser reload after
    submit doesn't resubmit the form. Client-side
    history.replaceState strips the ?notice= query right after
    render so the banner appears once per save instead of persisting
    across refreshes.
  • Serial-port dropdown + refresh button populated server-side
    from serialport::available_ports() (the same source the GUI
    ComboBox uses); a small ↻ button next to each port re-scans via
    GET /serial-ports and rewrites both selects' options in-place
    without a full page reload. Operator's selection is preserved
    across refreshes, and a saved port that isn't currently detected
    stays visible with a (saved) suffix.
  • CSS Grid Server-frame layout so the two Port: colons in each
    column line up across rows; per-port inputs sized to 6 chars (any
    valid TCP port fits) so the More button fits on row 1 alongside
    Telnet + Web Server.
  • JS modal popups for the More views, plus inline confirmation
    dialogs that warn before disabling the web server or changing the
    web port — both actions break the operator's current connection.
  • Connection-breaking notice included in the post-save banner
    when the operator's just-confirmed change will sever the browser
    session (e.g. "Web server port changed to 9090. Reconnect at the
    new port.").

Web auth and lockout

  • HTTP Basic Auth gated on the same security_enabled flag that
    guards telnet. Uses the project's existing length-leak-resistant
    constant_time_eq from telnet.rs.
  • Shared brute-force lockout map with telnet and SSH. Three
    failures across any of the three protocols trip a 5-minute IP ban
    (the same LockoutMap the telnet listener uses); failed web
    attempts respond with 429 Too Many Requests + Retry-After: 300
    once the threshold is crossed. The 429 fires before the auth
    check on every subsequent request, so a banned IP can't keep us
    busy parsing malformed POSTs either.
  • Same IP-safety allowlist as telnet: when login is not required
    and disable_ip_safety is off, only private / loopback /
    link-local source IPs are accepted (and *.*.*.1 gateway
    addresses are rejected).

Web defense-in-depth

  • 30-second read timeout on read_request to stop slow-loris clients
    from parking a tokio task indefinitely.
  • MAX_INFLIGHT = 16 concurrent connections with a Drop-guarded
    slot release; excess connections get a 503 Service Unavailable +
    Retry-After: 5 rather than being parked behind the read timeout.
  • 16 KB cap on request headers, 64 KB cap on POST body — bounded so
    a hostile peer can't drive the per-connection buffer to OOM.
  • UTF-8 round-trip safe: url_decode accumulates percent-decoded
    bytes into a Vec<u8> then runs from_utf8_lossy, so values like
    weather_zip = 日本語 survive the form → config-file → form
    cycle without corruption.

Changed

Unified telnet / SSH / web credentials

  • One username / password pair now covers the telnet menu, the
    SSH server, and the web configuration UI. The old per-protocol
    ssh_username / ssh_password config keys are gone. Defaults
    unchanged at admin / changeme.
  • One-time migration: if the operator's egateway.conf still has
    non-default ssh_username / ssh_password values and the
    unified username / password are still at the factory defaults,
    the legacy SSH values are adopted into the unified pair on load
    (with a Note: migrating legacy ssh_username=… log line). Once
    the next save runs, the legacy keys disappear from the written
    file. If both pairs were already customized, the unified pair
    wins (the legacy SSH values are silently dropped).
  • GUI Security frame collapses from two rows (separate Telnet /
    SSH credential rows) to one Login User / Pass row + a spacer
    that keeps the frame the same height as the adjacent Server frame.
  • Telnet Security menu drops the S (Set SSH username) /
    W (Set SSH password) items; the remaining U / P items now
    read Set username / Set password (no more "telnet"
    qualifier). Status shows a single Username: / Password:
    pair instead of two.
  • Help screens under Configuration > Security and
    Configuration > Server Configuration updated: the security
    help notes "One username/password covers telnet, SSH, and the
    web UI" and the server help describes the new W (Toggle Web) /
    B (Set Web port) keys.

GUI Server frame

  • Fixed-width listener column slots so the two Port: colons line
    up between rows — the same colon-alignment the web frame gets
    from CSS Grid. The earlier hand-tuned add_space(16.0) left the
    colons at different X positions because "Telnet" / "SSH" and
    "Web Server" / "Kermit Server" have different intrinsic widths.
  • More button moved up to row 1 (with Telnet + Web Server),
    mirroring the web layout.

GUI Serial Ports frame (web-side parity adjustments)

  • Web Serial frame's header now carries both ports' Enabled
    checkboxes alongside per-port titles ("Serial Port A" / "Serial
    Port B"), matching the GUI's layout exactly. Per-port rows are
    now Port X: [select ▼] [↻] Baud: [...] [More...] with the More
    button kept on the same line via a no-wrap row class.

Logger

  • Added a parallel non-draining snapshot(max) API alongside the
    existing drain(). The GUI keeps using drain() for its
    per-frame console accumulator; the web /logs endpoint polls
    snapshot() so the two views don't compete for log lines.

ethernet-gateway v0.5.5

Choose a tag to compare

@github-actions github-actions released this 10 May 22:49

Added

Dual serial-port support

  • Two physically independent serial portsPort A and Port B
    each with its own enabled flag, mode (modem emulator or telnet-serial
    console), device path, baud, AT/S-register state, and stored
    phone-number slots. The two ports run in separate manager threads,
    persist AT&W state separately, and host independent console-bridge
    slots, so the operator can run a Hayes modem on one wire and a
    telnet-serial bridge on the other (or any other mix) without
    cross-talk.
  • A/B picker submenus — the Configuration > M entry is now
    Serial Configuration and opens a picker listing both ports' status;
    selecting a port drops into that port's settings. The main-menu
    Serial Gateway (G) likewise opens an A/B picker before bridging,
    showing both ports' status (ineligible ports are dimmed) so the user
    can see why a port isn't available.
  • Per-port mode toggle moved from the Configuration menu to the
    per-port settings menu (T item). Hidden from sessions that arrived
    over a serial port itself, since flipping that port to console mode
    would tear down the caller's own connection before they could
    acknowledge.
  • GUI Serial Port frame redesigned: header row carries both ports'
    Enabled checkboxes and a shared Save button; one row per port
    beneath with a device-path dropdown, baud field, and per-port
    More… button into an advanced popup (mode, framing, flow, full
    Hayes AT state). Both popups are independent so settings can be
    compared side-by-side.

Changed

  • Config schema split into per-port keys: every former serial_*
    key is now serial_a_* or serial_b_*. Legacy single-port configs
    auto-migrate into Port A on first read; the next save rewrites the
    file in dual-port form. Existing single-port deployments upgrade
    transparently with Port B disabled by default.
  • Serial Gateway main-menu visibility — now requires at least one
    port to be in console mode (so the menu can't dead-end at an empty
    picker).
  • Dialup mapping stays a single shared dialup.conf consulted by
    both ports' modems — phone-number lookups are global, not per-port.
  • Documentation refreshed end-to-end (README.md,
    usermanual.html, index.html) for the dual-port architecture,
    including config-key tables, GUI screenshots/descriptions, and the
    Console Mode walkthrough.
  • ATI0 / ATI3 identification strings now advertise the modem as
    Hayes-compatible, matching the behavior callers (BBS dialers, vintage
    terminal software) expect from a Hayes ID query.

Fixed

  • PETSCII width compliance in the new pickers and per-port menu
    titles: replaced em-dashes with ASCII hyphens and switched the
    picker layout to two lines per port (role label + device/baud) so
    worst-case lines fit the 40-col PETSCII budget.
  • Stale help text in console_show_help that told users to
    "Press T at the Configuration menu" — T moved into the per-port
    settings menu.

Security

  • AI-chat output sanitization — replies from the Groq API are now
    normalized (\r\n/\r\n) and passed through a
    sanitize_for_terminal filter before display, stripping ANSI escape
    sequences, control bytes, lone CRs, and telnet IAC so a prompt-injected
    reply can't smuggle terminal-control payloads through the chat surface.
  • Auth-lockout map boundedrecord_auth_failure now sweeps entries
    past the lockout window on every call, so a long-running public-facing
    instance can no longer accumulate one entry per distinct attacker IP
    indefinitely.

ethernet-gateway v0.5.4

Choose a tag to compare

@github-actions github-actions released this 07 May 07:13

Added

Serial Console Mode

  • Telnet-serial bridge as a second role for the serial port,
    alongside the existing Hayes AT modem emulator. Selectable via the
    new serial_mode config key (modem / console). The existing
    G Serial Gateway main-menu item now bridges the telnet/SSH session
    straight to the wire so an operator can drive a microcontroller,
    RS-232 device, or other serial console remotely.
  • Hot mode switch — flipping serial_mode (from the GUI dropdown,
    the new T Toggle Modem/Console mode entry on the Configuration
    menu, or egateway.conf directly) reconfigures the running serial
    thread within one manager-poll interval. No restart required.

Changed

  • Configuration menu reorganized to surface the new mode toggle and
    to relabel M Modem EmulatorM Serial Console based on
    current serial_mode. The new menu walkthrough is documented in
    user-manual §5.6.
  • Documentation pass: §3.2 of the user manual gained 22 previously
    undocumented config keys (the full kermit_* family,
    ssh_gateway_auth, disable_ip_safety, allow_atdt_kermit,
    kermit_server_enabled / _port); index.html grew a Kermit
    subsection in the file-transfer config tables and added cross-links
    to kermit.html from each protocol-prompt step; the chapter-8 intro
    now correctly describes five protocols (the old "three related
    protocols" framing predated the ZMODEM and Kermit chapters).

Fixed

Console bridge hardening

  • run_console_bridge could wedge indefinitely when the telnet
    peer's TCP write buffer was full: the spawned async task's
    duplex_write.write_all().await would park with no wake-up source,
    stranding the manager thread until process restart. Bounded with a
    200 ms timeout then abort().
  • Orphaned bridge requests on serial-mode flip: a request that
    arrived in the slot just before SERIAL_RESTART fired could be
    silently abandoned because console_manager_tick returned without
    polling the slot, leaving the requester's rx.await blocked forever.
    Slot is now drained with Err("Serial mode changed") on every exit
    path.
  • TOCTOU between request-eligibility check and slot insert:
    request_console_bridge now re-checks
    check_console_bridge_eligible under the slot lock so an operator
    flipping serial_mode (or disabling serial, or clearing the port
    path) and calling restart_serial() in the narrow window between
    the fast-path check and the slot insert can no longer leave a
    request stuck until shutdown.
  • Unbounded session_to_port channel replaced with a bounded
    tokio::sync::mpsc::channel(64); a flow-controlled wire (CTS-low,
    slow peer) plus a fast typist or paste can no longer balloon
    in-memory queue depth. The async-side .send().await now
    backpressures duplex_read, which backpressures the telnet peer.
  • Slot-cleanup duplication removed from the Err(_) arm of
    rx.await; let ConsoleSlotGuard's drop own slot teardown.

Serial mode switch responsiveness

  • Modem online loops (online_mode_tcp, online_mode_duplex) now
    honor SERIAL_RESTART on every iteration; previously a mode flip
    could lag by one block-read interval before the loop noticed.

Menu UX & doc-vs-code drift

  • G Serial Gateway and T Toggle Modem/Console mode are
    now hidden from sessions that arrived over the serial port itself.
    The handler-side rejections remain as defense in depth (a serial-side
    caller can still type the letter blind), but the menu no longer
    advertises items that always error.
  • Manual cross-references to "chapter 9.10" corrected to "9.13"
    (Console Mode lives at 9.13; 9.10 is Chained Command Lines).
  • AT&K1 redescribed as Auto-detect (stored, no wire effect)
    instead of "Reserved"; the parser at src/serial.rs:1140 accepts
    &K1 and emits FlowSet(1). Missing &K1 row added to Appendix
    B.4.
  • AT&F entry now notes that it drops the active connection,
    matching the AtResult::Reset return.
  • Bare kermit alias for ATDT KERMIT documented alongside the
    existing kermit-server / kermit server aliases.