DVX_GUI/proxy/README.md
2026-03-20 20:00:05 -05:00

183 lines
6.2 KiB
Markdown

# 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>
```