DVX_GUI/proxy
2026-03-20 20:00:05 -05:00
..
stubs Unicode removed from source. Lots of bugs fixed. Close to Alpha 2! 2026-03-20 19:10:53 -05:00
Makefile Unicode removed from source. Lots of bugs fixed. Close to Alpha 2! 2026-03-20 19:10:53 -05:00
proxy.c Unicode removed from source. Lots of bugs fixed. Close to Alpha 2! 2026-03-20 19:10:53 -05:00
README.md Updated docs. 2026-03-20 20:00:05 -05:00
sockShim.c Unicode removed from source. Lots of bugs fixed. Close to Alpha 2! 2026-03-20 19:10:53 -05:00
sockShim.h Unicode removed from source. Lots of bugs fixed. Close to Alpha 2! 2026-03-20 19:10:53 -05:00

SecLink Proxy -- Linux Serial Bridge

Linux-hosted proxy that bridges an 86Box emulated serial port to a remote telnet BBS. Part of the DVX GUI project.

The 86Box side communicates using the SecLink protocol (HDLC packet framing, CRC-16, DH key exchange, XTEA-CTR encryption). The BBS side is plain telnet over TCP. All crypto is transparent to the BBS -- it sees a normal telnet client.

Architecture

86Box (DOS terminal)                              Remote BBS
       |                                              |
  emulated modem                                  telnet
       |                                              |
    TCP:2323                                       TCP:23
       |                                              |
       +--- secproxy ----------------------------------+
            secLink <-> plaintext
            (encrypted, reliable)

The proxy accepts a single TCP connection from 86Box, performs the SecLink handshake (Diffie-Hellman key exchange), then connects to the BBS. All traffic between 86Box and the proxy is encrypted via XTEA-CTR on channel 0. Traffic between the proxy and the BBS is unencrypted telnet with IAC negotiation handling.

Usage

secproxy [listen_port] [bbs_host] [bbs_port]
Argument Default Description
listen_port 2323 TCP port for 86Box connection
bbs_host 10.1.0.244 BBS hostname or IP
bbs_port 2023 BBS TCP port

Examples:

secproxy                              # all defaults
secproxy 5000                         # listen on port 5000
secproxy 2323 bbs.example.com 23      # different BBS
secproxy --help                       # show usage

Startup Sequence

  1. Listen on the configured TCP port.
  2. Wait for 86Box to connect (polls with Ctrl+C support).
  3. Map the TCP socket to COM0 via the socket shim.
  4. Seed the RNG from /dev/urandom.
  5. Open SecLink and perform the DH handshake (blocks until the DOS side completes its handshake).
  6. Wait for the terminal to send ENTER (so the user can confirm the connection is working before BBS output starts).
  7. Connect to the remote BBS.
  8. Enter the proxy loop.

Proxy Loop

The main loop uses poll() with a 10ms timeout to multiplex between the two TCP connections:

  • 86Box -> BBS: secLinkPoll() reads from the 86Box socket via the socket shim, decrypts incoming packets, and the receive callback writes plaintext to the BBS socket.
  • BBS -> 86Box: read() from the BBS socket, then the telnet filter strips IAC sequences, then secLinkSend() encrypts and sends to 86Box via the socket shim. If the send window is full, the loop retries with ACK processing until the data goes through.
  • Maintenance: secLinkPoll() also handles packet-layer retransmit timers on every iteration.

The proxy exits cleanly on Ctrl+C (SIGINT), SIGTERM, or when either side disconnects.

Telnet Negotiation

The proxy handles RFC 854 telnet IAC sequences from the BBS so they do not corrupt the terminal display. A state machine parser strips IAC sequences from the data stream, persisting state across TCP segment boundaries.

Accepted options:

  • ECHO (option 1) -- server echoes characters
  • SGA (option 3) -- suppress go-ahead for character-at-a-time mode
  • TTYPE (option 24) -- terminal type negotiation
  • NAWS (option 31) -- window size negotiation

All other options are refused. Subnegotiations (SB...SE) are consumed silently.

Socket Shim

The proxy reuses the same packet, security, and secLink source code as the DOS build. A socket shim (sockShim.h / sockShim.c) provides rs232-compatible functions backed by TCP sockets instead of UART hardware:

rs232 function Socket shim behavior
rs232Open() Validates socket assigned; ignores serial params
rs232Close() Marks port closed (socket lifecycle is caller's)
rs232Read() Non-blocking recv() with MSG_DONTWAIT
rs232Write() Blocking send() loop with MSG_NOSIGNAL

The shim maps COM port indices (0-3) to socket file descriptors via sockShimSetFd(), which must be called before opening the SecLink layer. Up to 4 ports are supported.

The Makefile uses -include sockShim.h when compiling the packet and secLink layers, which defines RS232_H to prevent the real rs232.h from being included.

DJGPP Stubs

DOS-specific headers required by the security library are replaced by minimal stubs in stubs/:

Stub Replaces DJGPP header Contents
stubs/pc.h <pc.h> No-op definitions
stubs/go32.h <go32.h> No-op definitions
stubs/sys/farptr.h <sys/farptr.h> No-op definitions

The security library's hardware entropy function returns zeros on Linux, which is harmless since the proxy seeds the RNG from /dev/urandom before the handshake.

86Box Configuration

Configure the 86Box serial port to connect to the proxy:

  1. In 86Box settings, set a COM port to TCP client mode pointing at the proxy's listen port (default 2323).
  2. Enable "No telnet negotiation" to send raw bytes.
  3. The DOS terminal application running inside 86Box uses SecLink over this serial port.

Building

make            # builds ../bin/secproxy
make clean      # removes objects and binary

Objects are placed in ../obj/proxy/, the binary in ../bin/.

Requires only a standard Linux C toolchain (gcc, libc). No external dependencies beyond the project's own packet, security, and secLink source files, which are compiled directly from their source directories.

Files

proxy/
  proxy.c              main proxy program
  sockShim.h           rs232-compatible socket API (header)
  sockShim.c           socket shim implementation
  Makefile             Linux native build
  stubs/
    pc.h               stub for DJGPP <pc.h>
    go32.h             stub for DJGPP <go32.h>
    sys/
      farptr.h         stub for DJGPP <sys/farptr.h>