Skip to content

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 requires. CR-NUL stuffing (RFC 854 §2.2)
    is a text-mode rule and was inserting/deleting 0x00 bytes around 0x0D,
    which manifested as endless mid-transfer checksum failures and a hung peer
    (a Commodore Punter sender, whose S/B wait loops are unbounded, would
    strand). Validated against the genuine CCGMS Punter reference
    (ccgmsterm/test/punter.c) in both directions, including through a
    telnet-bridge emulation. IAC escaping (the I toggle) is unchanged.
  • GUI: external changes to the Kermit idle-timeout are no longer reverted on
    save.
    kermit_idle_timeout was rendered and saved in the desktop config
    editor but missing from its refresh-from-global and dirty-detection paths, so
    a value changed via the web/telnet UI while the GUI was open could be silently
    overwritten by the GUI's stale field on the next Save.
  • Serial modem mode now auto-reconnects when the device behind the port
    disappears
    (e.g. a socat/USB-serial bridge that exits when its attached
    terminal closes). Command-mode previously hit a hard I/O error and re-looped,
    spamming the error ~twice/second forever with no recovery; it now logs the
    outage once, backs off 1 s, and reopens the port automatically when the device
    returns — matching console mode.
  • ATDT to a hostname now tries every resolved address. Dialing resolved
    via to_socket_addrs() but only attempted the first address, so a host whose
    DNS returns an unreachable IPv6 record first could fail with a silent
    NO CARRIER even when a working IPv4 address followed. It now attempts each
    resolved address until one connects, and logs the failure reason instead of
    failing silently.
  • Config save failures are now surfaced. write_config_file/save_config
    return a Result; the explicit-save paths (desktop GUI Save buttons, telnet
    reset-to-defaults) report a failure instead of always logging success.
  • Hand-edited serial_*_parity / serial_*_flowcontrol values are honored.
    Both are now normalized (trim + lowercase) on read and apply, consistent with
    mode, so e.g. serial_a_parity = Even no longer silently reverts.
  • Config values round-trip without whitespace drift. sanitize_value now
    trims surrounding whitespace (the reader already trimmed), and the dialup
    number/host are sanitized on save so an embedded newline can't corrupt
    dialup.conf framing.
  • GUI waits for the X display before opening the console window, fixing the
    headless drop when the gateway is started as a boot-time service before the
    desktop session's X auth cookie is ready. The wait is adaptive (no delay on a
    normal manual launch) and degrades safely when there is no display.
  • Kermit's async server/receive paths no longer stall a runtime worker
    blocking std::fs calls moved to tokio::fs and the directory listing
    offloaded via spawn_blocking.

Security

  • SSH: warn when a pre-existing host/client private key is group- or
    world-readable.
    New keys are written 0600; a key restored from a backup or
    created by an older build could be more permissive. The gateway now logs a
    chmod 600 recommendation on load (warn-only — it does not refuse the key,
    matching the trusted-LAN threat model).
  • ZMODEM: bound consecutive empty data subpackets (MAX_EMPTY_SUBPACKETS)
    so a peer can't tar-pit the receive loop with CRC-valid zero-progress
    subpackets.
  • Telnet: bound in-subnegotiation reads (SB_DRAIN_TIMEOUT) so a peer that
    opens an IAC SB and then stalls can't pin the reader (slowloris); the outer
    idle wait is unchanged.