# 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` | `` | No-op definitions | | `stubs/go32.h` | `` | No-op definitions | | `stubs/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 go32.h stub for DJGPP sys/ farptr.h stub for DJGPP ```