// Socket shim — rs232-compatible API over TCP sockets // // Maps up to 4 "COM ports" to TCP socket file descriptors. Reads use // MSG_DONTWAIT (non-blocking) to match rs232Read's non-blocking semantics. // Writes are blocking (loop until all bytes sent) to match rs232Write's // guarantee of complete delivery. // // The shim does NOT close sockets in rs232Close — socket lifecycle is // managed by the proxy's main() function. This avoids double-close bugs // when secLinkClose calls rs232Close but the proxy still needs the fd. #include "sockShim.h" #include #include #include #include // ======================================================================== // Internal state // ======================================================================== #define MAX_PORTS 4 static int sFds[MAX_PORTS] = { -1, -1, -1, -1 }; static bool sOpen[MAX_PORTS] = { false, false, false, false }; // ======================================================================== // Public functions (alphabetical) // ======================================================================== int rs232Close(int com) { if (com < 0 || com >= MAX_PORTS) { return RS232_ERR_INVALID_PORT; } sOpen[com] = false; // Socket lifecycle is managed by the caller, not the shim return RS232_SUCCESS; } // Serial parameters are ignored — TCP handles framing, flow control, and // error correction. We just validate that a socket has been assigned. int rs232Open(int com, int32_t bps, int dataBits, char parity, int stopBits, int handshake) { (void)bps; (void)dataBits; (void)parity; (void)stopBits; (void)handshake; if (com < 0 || com >= MAX_PORTS) { return RS232_ERR_INVALID_PORT; } if (sFds[com] < 0) { return RS232_ERR_NOT_OPEN; } sOpen[com] = true; return RS232_SUCCESS; } // Non-blocking read matching rs232Read semantics: // > 0: bytes read // 0: no data available (EAGAIN) // -1: error or connection closed (maps to PKT_ERR_DISCONNECTED in pktPoll) int rs232Read(int com, char *data, int len) { if (com < 0 || com >= MAX_PORTS || sFds[com] < 0) { return -1; } ssize_t n = recv(sFds[com], data, len, MSG_DONTWAIT); if (n < 0) { if (errno == EAGAIN || errno == EWOULDBLOCK) { return 0; } return -1; } if (n == 0) { // TCP FIN — peer closed connection return -1; } return (int)n; } // Blocking write: loops until all bytes are sent. MSG_NOSIGNAL prevents // SIGPIPE on a closed connection (returns EPIPE instead). This matches // rs232Write's blocking-until-complete behavior. int rs232Write(int com, const char *data, int len) { if (com < 0 || com >= MAX_PORTS || sFds[com] < 0) { return RS232_ERR_NOT_OPEN; } int sent = 0; while (sent < len) { ssize_t n = send(sFds[com], data + sent, len - sent, MSG_NOSIGNAL); if (n < 0) { if (errno == EINTR) { continue; } return RS232_ERR_NOT_OPEN; } sent += (int)n; } return RS232_SUCCESS; } void sockShimSetFd(int com, int fd) { if (com < 0 || com >= MAX_PORTS) { return; } sFds[com] = fd; // Set non-blocking so rs232Read returns immediately when empty int flags = fcntl(fd, F_GETFL, 0); fcntl(fd, F_SETFL, flags | O_NONBLOCK); }