DVX_GUI/proxy/proxy.c

296 lines
7.8 KiB
C

// SecLink proxy — bridges an 86Box serial connection to a telnet BBS
//
// 86Box (DOS terminal) ←→ TCP ←→ proxy ←→ TCP ←→ BBS
// secLink protocol plain telnet
//
// Usage: proxy [listen_port] [bbs_host] [bbs_port]
// Defaults: 2323 bbs.duensing.digital 23
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <errno.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <poll.h>
#include <fcntl.h>
#include "sockShim.h"
#include "../seclink/secLink.h"
#include "../security/security.h"
// ========================================================================
// Defines
// ========================================================================
#define DEFAULT_LISTEN_PORT 2323
#define DEFAULT_BBS_HOST "bbs.duensing.digital"
#define DEFAULT_BBS_PORT 23
#define CHANNEL_TERMINAL 0
#define POLL_TIMEOUT_MS 10
// ========================================================================
// Static globals
// ========================================================================
static volatile bool sRunning = true;
// ========================================================================
// Static prototypes (alphabetical)
// ========================================================================
static int connectToBbs(const char *host, int port);
static int createListenSocket(int port);
static void onRecvFromDos(void *ctx, const uint8_t *data, int len, uint8_t channel);
static void seedRng(void);
static void sigHandler(int sig);
// ========================================================================
// Static functions (alphabetical)
// ========================================================================
static int connectToBbs(const char *host, int port) {
struct addrinfo hints;
struct addrinfo *res;
char portStr[16];
int fd;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
snprintf(portStr, sizeof(portStr), "%d", port);
if (getaddrinfo(host, portStr, &hints, &res) != 0) {
return -1;
}
fd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
if (fd < 0) {
freeaddrinfo(res);
return -1;
}
if (connect(fd, res->ai_addr, res->ai_addrlen) < 0) {
close(fd);
freeaddrinfo(res);
return -1;
}
freeaddrinfo(res);
return fd;
}
static int createListenSocket(int port) {
struct sockaddr_in addr;
int fd;
int opt = 1;
fd = socket(AF_INET, SOCK_STREAM, 0);
if (fd < 0) {
return -1;
}
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = INADDR_ANY;
addr.sin_port = htons(port);
if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
close(fd);
return -1;
}
if (listen(fd, 1) < 0) {
close(fd);
return -1;
}
return fd;
}
static void onRecvFromDos(void *ctx, const uint8_t *data, int len, uint8_t channel) {
int bbsFd = *(int *)ctx;
(void)channel;
int sent = 0;
while (sent < len) {
ssize_t n = write(bbsFd, data + sent, len - sent);
if (n <= 0) {
break;
}
sent += (int)n;
}
}
static void seedRng(void) {
uint8_t entropy[32];
FILE *f;
f = fopen("/dev/urandom", "rb");
if (f) {
if (fread(entropy, 1, sizeof(entropy), f) < sizeof(entropy)) {
fprintf(stderr, "Warning: short read from /dev/urandom\n");
}
fclose(f);
}
secRngSeed(entropy, sizeof(entropy));
}
static void sigHandler(int sig) {
(void)sig;
sRunning = false;
}
// ========================================================================
// Main
// ========================================================================
int main(int argc, char *argv[]) {
int listenPort = DEFAULT_LISTEN_PORT;
const char *bbsHost = DEFAULT_BBS_HOST;
int bbsPort = DEFAULT_BBS_PORT;
int listenFd;
int clientFd;
int bbsFd;
SecLinkT *link;
struct sockaddr_in clientAddr;
socklen_t clientLen;
struct pollfd fds[2];
int rc;
if (argc > 1 && (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "--help") == 0)) {
printf("Usage: %s [listen_port] [bbs_host] [bbs_port]\n", argv[0]);
printf("Defaults: %d %s %d\n", DEFAULT_LISTEN_PORT, DEFAULT_BBS_HOST, DEFAULT_BBS_PORT);
return 0;
}
if (argc > 1) {
listenPort = atoi(argv[1]);
}
if (argc > 2) {
bbsHost = argv[2];
}
if (argc > 3) {
bbsPort = atoi(argv[3]);
}
signal(SIGPIPE, SIG_IGN);
signal(SIGINT, sigHandler);
signal(SIGTERM, sigHandler);
// Listen for 86Box connection
listenFd = createListenSocket(listenPort);
if (listenFd < 0) {
fprintf(stderr, "Failed to listen on port %d: %s\n", listenPort, strerror(errno));
return 1;
}
printf("Listening on port %d...\n", listenPort);
// Accept connection from 86Box
clientLen = sizeof(clientAddr);
clientFd = accept(listenFd, (struct sockaddr *)&clientAddr, &clientLen);
close(listenFd);
if (clientFd < 0) {
fprintf(stderr, "Accept failed: %s\n", strerror(errno));
return 1;
}
printf("86Box connected.\n");
// Associate socket with COM0 for the secLink stack
sockShimSetFd(0, clientFd);
// Connect to BBS
printf("Connecting to %s:%d...\n", bbsHost, bbsPort);
bbsFd = connectToBbs(bbsHost, bbsPort);
if (bbsFd < 0) {
fprintf(stderr, "Failed to connect to BBS: %s\n", strerror(errno));
close(clientFd);
return 1;
}
printf("BBS connected.\n");
// Seed RNG from /dev/urandom and open secLink
seedRng();
link = secLinkOpen(0, 115200, 8, 'N', 1, 0, onRecvFromDos, &bbsFd);
if (!link) {
fprintf(stderr, "Failed to open secLink.\n");
close(bbsFd);
close(clientFd);
return 1;
}
// DH key exchange (blocks until both sides complete)
printf("Waiting for secLink handshake...\n");
rc = secLinkHandshake(link);
if (rc != SECLINK_SUCCESS) {
fprintf(stderr, "Handshake failed: %d\n", rc);
secLinkClose(link);
close(bbsFd);
return 1;
}
printf("Handshake complete. Proxying traffic.\n");
// Set BBS socket non-blocking for the main loop
int flags = fcntl(bbsFd, F_GETFL, 0);
fcntl(bbsFd, F_SETFL, flags | O_NONBLOCK);
// Main proxy loop
fds[0].fd = clientFd;
fds[0].events = POLLIN;
fds[1].fd = bbsFd;
fds[1].events = POLLIN;
while (sRunning) {
poll(fds, 2, POLL_TIMEOUT_MS);
// Process incoming secLink packets from 86Box
// (callback forwards decrypted data to BBS)
secLinkPoll(link);
// Read from BBS and send encrypted to 86Box
if (fds[1].revents & POLLIN) {
uint8_t buf[SECLINK_MAX_PAYLOAD];
ssize_t n = read(bbsFd, buf, sizeof(buf));
if (n <= 0) {
printf("BBS disconnected.\n");
break;
}
secLinkSend(link, buf, (int)n, CHANNEL_TERMINAL, true, true);
}
// Check for disconnects
if (fds[0].revents & (POLLERR | POLLHUP)) {
printf("86Box disconnected.\n");
break;
}
if (fds[1].revents & (POLLERR | POLLHUP)) {
printf("BBS disconnected.\n");
break;
}
}
printf("Shutting down.\n");
secLinkClose(link);
close(bbsFd);
close(clientFd);
return 0;
}