Added terminal app to test communications chain and ANSI widget.
This commit is contained in:
parent
27b5ad5a16
commit
79b2825a98
3 changed files with 464 additions and 0 deletions
43
termdemo/Makefile
Normal file
43
termdemo/Makefile
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
# SecLink Terminal Demo Makefile for DJGPP cross-compilation
|
||||
|
||||
DJGPP_PREFIX = $(HOME)/djgpp/djgpp
|
||||
CC = $(DJGPP_PREFIX)/bin/i586-pc-msdosdjgpp-gcc
|
||||
CFLAGS = -O2 -Wall -Wextra -march=i486 -mtune=i586 -I../dvx -I../seclink -I../security
|
||||
LDFLAGS = -L../lib -ldvx -lseclink -lpacket -lsecurity -lrs232 -lm
|
||||
|
||||
OBJDIR = ../obj/termdemo
|
||||
BINDIR = ../bin
|
||||
LIBDIR = ../lib
|
||||
|
||||
SRCS = termdemo.c
|
||||
OBJS = $(patsubst %.c,$(OBJDIR)/%.o,$(SRCS))
|
||||
TARGET = $(BINDIR)/termdemo.exe
|
||||
|
||||
.PHONY: all clean libs
|
||||
|
||||
all: libs $(TARGET)
|
||||
|
||||
libs:
|
||||
$(MAKE) -C ../dvx
|
||||
$(MAKE) -C ../rs232
|
||||
$(MAKE) -C ../packet
|
||||
$(MAKE) -C ../security
|
||||
$(MAKE) -C ../seclink
|
||||
|
||||
$(TARGET): $(OBJS) $(LIBDIR)/libdvx.a $(LIBDIR)/libseclink.a $(LIBDIR)/libpacket.a $(LIBDIR)/libsecurity.a $(LIBDIR)/librs232.a | $(BINDIR)
|
||||
$(CC) $(CFLAGS) -o $@ $(OBJS) $(LDFLAGS)
|
||||
|
||||
$(OBJDIR)/%.o: %.c | $(OBJDIR)
|
||||
$(CC) $(CFLAGS) -c -o $@ $<
|
||||
|
||||
$(OBJDIR):
|
||||
mkdir -p $(OBJDIR)
|
||||
|
||||
$(BINDIR):
|
||||
mkdir -p $(BINDIR)
|
||||
|
||||
# Dependencies
|
||||
$(OBJDIR)/termdemo.o: termdemo.c ../dvx/dvxApp.h ../dvx/dvxWidget.h ../seclink/secLink.h ../security/security.h
|
||||
|
||||
clean:
|
||||
rm -rf $(OBJDIR) $(TARGET)
|
||||
150
termdemo/README.md
Normal file
150
termdemo/README.md
Normal file
|
|
@ -0,0 +1,150 @@
|
|||
# SecLink Terminal Demo
|
||||
|
||||
DOS terminal emulator combining the DV/X windowed GUI with SecLink
|
||||
encrypted serial communication. Connects to a remote BBS through the
|
||||
SecLink proxy, providing a full ANSI terminal in a DESQview/X-style
|
||||
window with encrypted transport.
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
termdemo (DOS, 86Box)
|
||||
|
|
||||
+--- DV/X GUI windowed desktop, ANSI terminal widget
|
||||
|
|
||||
+--- SecLink encrypted serial link
|
||||
| |
|
||||
| +--- packet HDLC framing, CRC, retransmit
|
||||
| +--- security DH key exchange, XTEA-CTR cipher
|
||||
| +--- rs232 ISR-driven UART I/O
|
||||
|
|
||||
COM port (86Box emulated modem)
|
||||
|
|
||||
TCP:2323
|
||||
|
|
||||
secproxy (Linux)
|
||||
|
|
||||
TCP:23
|
||||
|
|
||||
Remote BBS
|
||||
```
|
||||
|
||||
All traffic between the terminal and the proxy is encrypted via
|
||||
XTEA-CTR on SecLink channel 0. The proxy decrypts and forwards
|
||||
plaintext to the BBS over telnet.
|
||||
|
||||
## Usage
|
||||
|
||||
```
|
||||
termdemo [com_port] [baud_rate]
|
||||
```
|
||||
|
||||
| Argument | Default | Description |
|
||||
|-------------|---------|------------------------------|
|
||||
| `com_port` | 1 | COM port number (1-4) |
|
||||
| `baud_rate` | 115200 | Serial baud rate |
|
||||
|
||||
```
|
||||
termdemo # COM1 at 115200
|
||||
termdemo 2 # COM2 at 115200
|
||||
termdemo 1 57600 # COM1 at 57600
|
||||
termdemo -h # show usage
|
||||
```
|
||||
|
||||
## Startup Sequence
|
||||
|
||||
1. Seed the RNG from hardware entropy
|
||||
2. Open SecLink on the specified COM port (8N1, no handshake)
|
||||
3. Perform DH key exchange (blocks until the proxy completes its side)
|
||||
4. Initialize the DV/X GUI (1024x768, 16bpp VESA)
|
||||
5. Create a resizable terminal window with menu bar and status bar
|
||||
6. Enter the main loop
|
||||
|
||||
The handshake completes in text mode before the GUI starts, so the
|
||||
DOS console shows progress messages during connection setup.
|
||||
|
||||
## Main Loop
|
||||
|
||||
Each iteration:
|
||||
|
||||
1. `dvxUpdate()` — process mouse, keyboard, paint, and window events
|
||||
2. `secLinkPoll()` — read serial data, decrypt, deliver to ring buffer
|
||||
3. `wgtAnsiTermPoll()` — drain ring buffer into the ANSI parser
|
||||
|
||||
## Data Flow
|
||||
|
||||
```
|
||||
BBS → proxy → serial → secLinkPoll() → onRecv() → ring buffer
|
||||
→ commRead() → wgtAnsiTermWrite() → ANSI parser → screen
|
||||
|
||||
Keyboard → widgetAnsiTermOnKey() → commWrite()
|
||||
→ secLinkSend() → serial → proxy → BBS
|
||||
```
|
||||
|
||||
A 4KB ring buffer bridges the SecLink receive callback (which fires
|
||||
during `secLinkPoll()`) and the terminal widget's comm read interface
|
||||
(which is polled by `wgtAnsiTermPoll()`).
|
||||
|
||||
## GUI
|
||||
|
||||
- **Window**: resizable, titled "SecLink Terminal"
|
||||
- **Menu bar**: File → Quit
|
||||
- **Terminal**: 80x25 ANSI terminal widget with 1000-line scrollback
|
||||
- **Status bar**: shows COM port, baud rate, and encryption status
|
||||
|
||||
The ANSI terminal widget supports standard escape sequences including
|
||||
cursor control, SGR colors (16-color CGA palette), erase, scroll,
|
||||
insert/delete lines, and DEC private modes (cursor visibility, line
|
||||
wrap).
|
||||
|
||||
## Test Setup
|
||||
|
||||
1. Start the SecLink proxy on the Linux host:
|
||||
|
||||
```
|
||||
secproxy 2323 bbs.example.com 23
|
||||
```
|
||||
|
||||
2. Configure 86Box with a COM port pointing at the proxy's listen port
|
||||
(TCP client mode, port 2323, no telnet negotiation)
|
||||
|
||||
3. Run the terminal inside 86Box:
|
||||
|
||||
```
|
||||
termdemo
|
||||
```
|
||||
|
||||
4. The handshake completes, the GUI appears, and BBS output is
|
||||
displayed in the terminal window
|
||||
|
||||
## Building
|
||||
|
||||
```
|
||||
make # builds ../bin/termdemo.exe
|
||||
make clean # removes objects and binary
|
||||
```
|
||||
|
||||
The Makefile builds all dependency libraries automatically. Objects
|
||||
are placed in `../obj/termdemo/`, the binary in `../bin/`.
|
||||
|
||||
## Dependencies
|
||||
|
||||
All libraries are in `../lib/`:
|
||||
|
||||
| Library | Purpose |
|
||||
|------------------|--------------------------------------|
|
||||
| `libdvx.a` | DV/X windowed GUI and widget system |
|
||||
| `libseclink.a` | Secure serial link wrapper |
|
||||
| `libpacket.a` | HDLC framing and reliability |
|
||||
| `libsecurity.a` | DH key exchange and XTEA cipher |
|
||||
| `librs232.a` | ISR-driven UART serial driver |
|
||||
|
||||
Target: DJGPP cross-compiler, 486+ CPU, VESA VBE 2.0+ video.
|
||||
|
||||
## Files
|
||||
|
||||
```
|
||||
termdemo/
|
||||
termdemo.c terminal emulator program
|
||||
Makefile DJGPP cross-compilation build
|
||||
```
|
||||
271
termdemo/termdemo.c
Normal file
271
termdemo/termdemo.c
Normal file
|
|
@ -0,0 +1,271 @@
|
|||
// termdemo.c — SecLink terminal emulator demo
|
||||
//
|
||||
// Uses DV/X GUI ANSI terminal widget with SecLink encrypted serial link
|
||||
// to provide a BBS terminal over a secured serial connection.
|
||||
//
|
||||
// Usage: termdemo [com_port] [baud_rate]
|
||||
// com_port — 1-4 (default 1)
|
||||
// baud_rate — baud rate (default 115200)
|
||||
|
||||
#include "dvxApp.h"
|
||||
#include "dvxWidget.h"
|
||||
#include "secLink.h"
|
||||
#include "security.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
// ============================================================
|
||||
// Constants
|
||||
// ============================================================
|
||||
|
||||
#define CMD_FILE_QUIT 100
|
||||
|
||||
#define RECV_BUF_SIZE 4096
|
||||
|
||||
#define CHANNEL_DATA 0
|
||||
|
||||
// ============================================================
|
||||
// Types
|
||||
// ============================================================
|
||||
|
||||
// Bridges secLink receive callback and terminal widget comm interface
|
||||
typedef struct {
|
||||
SecLinkT *link;
|
||||
AppContextT *app;
|
||||
WidgetT *term;
|
||||
uint8_t recvBuf[RECV_BUF_SIZE];
|
||||
int32_t recvHead;
|
||||
int32_t recvTail;
|
||||
} TermContextT;
|
||||
|
||||
// ============================================================
|
||||
// Prototypes
|
||||
// ============================================================
|
||||
|
||||
static int32_t commRead(void *ctx, uint8_t *buf, int32_t maxLen);
|
||||
static int32_t commWrite(void *ctx, const uint8_t *data, int32_t len);
|
||||
static void onCloseCb(WindowT *win);
|
||||
static void onMenuCb(WindowT *win, int32_t menuId);
|
||||
static void onRecv(void *ctx, const uint8_t *data, int len, uint8_t channel);
|
||||
|
||||
|
||||
// ============================================================
|
||||
// commRead — drain receive ring buffer for terminal widget
|
||||
// ============================================================
|
||||
|
||||
static int32_t commRead(void *ctx, uint8_t *buf, int32_t maxLen) {
|
||||
TermContextT *tc = (TermContextT *)ctx;
|
||||
int32_t count = 0;
|
||||
|
||||
while (count < maxLen && tc->recvTail != tc->recvHead) {
|
||||
buf[count++] = tc->recvBuf[tc->recvTail];
|
||||
tc->recvTail = (tc->recvTail + 1) % RECV_BUF_SIZE;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// commWrite — send keystrokes via secLink (encrypted, channel 0)
|
||||
// ============================================================
|
||||
|
||||
static int32_t commWrite(void *ctx, const uint8_t *data, int32_t len) {
|
||||
TermContextT *tc = (TermContextT *)ctx;
|
||||
int rc = secLinkSend(tc->link, data, len, CHANNEL_DATA, true, false);
|
||||
|
||||
return (rc == SECLINK_SUCCESS) ? len : 0;
|
||||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// onCloseCb — quit when the terminal window is closed
|
||||
// ============================================================
|
||||
|
||||
static void onCloseCb(WindowT *win) {
|
||||
AppContextT *ctx = (AppContextT *)win->userData;
|
||||
|
||||
if (ctx) {
|
||||
dvxQuit(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// onMenuCb — handle menu commands
|
||||
// ============================================================
|
||||
|
||||
static void onMenuCb(WindowT *win, int32_t menuId) {
|
||||
AppContextT *ctx = (AppContextT *)win->userData;
|
||||
|
||||
switch (menuId) {
|
||||
case CMD_FILE_QUIT:
|
||||
if (ctx) {
|
||||
dvxQuit(ctx);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// onRecv — secLink receive callback, fills ring buffer
|
||||
// ============================================================
|
||||
|
||||
static void onRecv(void *ctx, const uint8_t *data, int len, uint8_t channel) {
|
||||
(void)channel;
|
||||
TermContextT *tc = (TermContextT *)ctx;
|
||||
|
||||
for (int i = 0; i < len; i++) {
|
||||
int32_t next = (tc->recvHead + 1) % RECV_BUF_SIZE;
|
||||
|
||||
if (next == tc->recvTail) {
|
||||
break;
|
||||
}
|
||||
|
||||
tc->recvBuf[tc->recvHead] = data[i];
|
||||
tc->recvHead = next;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// main
|
||||
// ============================================================
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
int comPort = 0;
|
||||
int32_t baudRate = 115200;
|
||||
|
||||
// Parse command line
|
||||
if (argc >= 2) {
|
||||
if (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "--help") == 0) {
|
||||
printf("Usage: termdemo [com_port] [baud_rate]\n");
|
||||
printf(" com_port -- 1-4 (default 1)\n");
|
||||
printf(" baud_rate -- baud rate (default 115200)\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
comPort = atoi(argv[1]) - 1;
|
||||
|
||||
if (comPort < 0 || comPort > 3) {
|
||||
fprintf(stderr, "Invalid COM port (must be 1-4)\n");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (argc >= 3) {
|
||||
baudRate = atol(argv[2]);
|
||||
|
||||
if (baudRate <= 0) {
|
||||
fprintf(stderr, "Invalid baud rate\n");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
printf("SecLink Terminal Demo\n");
|
||||
printf("COM%d at %ld 8N1\n\n", comPort + 1, (long)baudRate);
|
||||
|
||||
// Seed the RNG with hardware entropy
|
||||
printf("Gathering entropy...\n");
|
||||
uint8_t entropy[16];
|
||||
int got = secRngGatherEntropy(entropy, sizeof(entropy));
|
||||
secRngSeed(entropy, got);
|
||||
|
||||
// Initialize terminal context
|
||||
TermContextT tc;
|
||||
memset(&tc, 0, sizeof(tc));
|
||||
|
||||
// Open secLink on the specified COM port
|
||||
printf("Opening SecLink on COM%d...\n", comPort + 1);
|
||||
tc.link = secLinkOpen(comPort, baudRate, 8, 'N', 1, 0, onRecv, &tc);
|
||||
|
||||
if (!tc.link) {
|
||||
fprintf(stderr, "Failed to open SecLink\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Perform DH key exchange (blocks until both sides complete)
|
||||
printf("Performing key exchange (waiting for remote side)...\n");
|
||||
int rc = secLinkHandshake(tc.link);
|
||||
|
||||
if (rc != SECLINK_SUCCESS) {
|
||||
fprintf(stderr, "Handshake failed (%d)\n", rc);
|
||||
secLinkClose(tc.link);
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("Secure link established.\n\n");
|
||||
|
||||
// Initialize DV/X GUI
|
||||
AppContextT ctx;
|
||||
|
||||
printf("Initializing video...\n");
|
||||
|
||||
if (dvxInit(&ctx, 1024, 768, 16) != 0) {
|
||||
fprintf(stderr, "Failed to initialize DV/X GUI\n");
|
||||
secLinkClose(tc.link);
|
||||
return 1;
|
||||
}
|
||||
|
||||
tc.app = &ctx;
|
||||
|
||||
// Create the terminal window
|
||||
WindowT *win = dvxCreateWindow(&ctx, "SecLink Terminal", 40, 30, 680, 460, true);
|
||||
|
||||
if (!win) {
|
||||
dvxShutdown(&ctx);
|
||||
secLinkClose(tc.link);
|
||||
return 1;
|
||||
}
|
||||
|
||||
win->userData = &ctx;
|
||||
win->onClose = onCloseCb;
|
||||
win->onMenu = onMenuCb;
|
||||
|
||||
// File menu
|
||||
MenuBarT *bar = wmAddMenuBar(win);
|
||||
|
||||
if (bar) {
|
||||
MenuT *fileMenu = wmAddMenu(bar, "File");
|
||||
|
||||
if (fileMenu) {
|
||||
wmAddMenuItem(fileMenu, "Quit", CMD_FILE_QUIT);
|
||||
}
|
||||
}
|
||||
|
||||
// Widget tree: terminal + status bar
|
||||
WidgetT *root = wgtInitWindow(&ctx, win);
|
||||
WidgetT *term = wgtAnsiTerm(root, 80, 25);
|
||||
|
||||
term->weight = 100;
|
||||
wgtAnsiTermSetScrollback(term, 1000);
|
||||
tc.term = term;
|
||||
|
||||
// Connect terminal widget to secLink via comm interface
|
||||
wgtAnsiTermSetComm(term, &tc, commRead, commWrite);
|
||||
|
||||
// Status bar showing connection info
|
||||
WidgetT *sb = wgtStatusBar(root);
|
||||
char statusText[64];
|
||||
snprintf(statusText, sizeof(statusText), "COM%d %ld 8N1 [Encrypted]", comPort + 1, (long)baudRate);
|
||||
wgtLabel(sb, statusText);
|
||||
|
||||
wgtInvalidate(root);
|
||||
|
||||
// Main loop — poll secLink and terminal each frame
|
||||
while (dvxUpdate(&ctx)) {
|
||||
secLinkPoll(tc.link);
|
||||
wgtAnsiTermPoll(term);
|
||||
}
|
||||
|
||||
// Cleanup
|
||||
dvxShutdown(&ctx);
|
||||
secLinkClose(tc.link);
|
||||
|
||||
printf("SecLink Terminal Demo ended.\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue