/* * serproxy.c - Outpost telnet-to-TNC BBS proxy * * 19-Dec-2008: Dan Smith * * Compile with: * * gcc -o serproxy serproxy.c * * Usage: * * ./serproxy /dev/ttyS0 * * Configure Outpost to use no password, and logon to this proxy with * the username equal to the BBS you want to connect to. This proxy * will capture the username and perform a "C $USER" command to the TNC * to connect. Upon receiving a "B" command from Outpost, this proxy * will terminate the socket connection. * * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include #include #include #include #include #include #include #include #include #include #include #define MAX(a, b) (a > b ? a : b) #define ASCII_LF 0x0A struct spopts { char *ser_port; int soc_port; }; int remove_newlines(char *buf, int len) { int i, j; for (i = 0, j = 0; i < len; i++) { if (buf[i] == ASCII_LF) continue; buf[j++] = buf[i]; } return j; } int open_serial(char *path) { int serial; struct termios tio; serial = open(path, O_RDWR); if (serial < 0) return -1; if (tcgetattr(serial, &tio) != 0) goto err; cfmakeraw(&tio); tio.c_cflag |= CRTSCTS; tio.c_iflag |= BRKINT; tio.c_oflag &= ~(OCRNL); tio.c_oflag |= ONLCR; cfsetspeed(&tio, B9600); if (tcsetattr(serial, TCSANOW, &tio) != 0) goto err; return serial; err: close(serial); return -1; } int listen_on(int port) { int lsocket; struct sockaddr_in sin; int optval = 1; lsocket = socket(AF_INET, SOCK_STREAM, 0); if (lsocket < 0) return -1; sin.sin_family = AF_INET; sin.sin_port = htons(port); sin.sin_addr.s_addr = INADDR_ANY; setsockopt(lsocket, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)); if (bind(lsocket, (struct sockaddr *)&sin, sizeof(sin)) < 0) goto err; if (listen(lsocket, 1) < 0) goto err; return lsocket; err: close(lsocket); return -1; } int do_logon(int serial, int lsocket) { int ret; char buf[1024]; char str[1024]; struct sockaddr_in csin; unsigned int csin_len = sizeof(csin); int csocket = -1; csocket = accept(lsocket, (struct sockaddr *)&csin, &csin_len); if (csocket < 0) return -1; printf("Accepted socket client %s:%i\n", inet_ntop(AF_INET, &csin.sin_addr, str, sizeof(str)), ntohs(csin.sin_port)); ret = read(csocket, buf, sizeof(buf)); ret = remove_newlines(buf, ret); if (buf[0] > '!') { write(serial, "C ", 2); write(serial, buf, ret); write(serial, "\r", 1); printf("Connecting to BBS %s\n", buf); } else { printf("No logon; connecting to TNC\n"); } return csocket; } int proxy(int serial, int lsocket) { int csocket = -1; fd_set iset; char buf[4096]; int last_serial = 0; while (1) { int ret; int maxfd; FD_ZERO(&iset); FD_SET(serial, &iset); if (csocket >= 0) { FD_SET(csocket, &iset); maxfd = MAX(csocket, serial) + 1; } else { FD_SET(lsocket, &iset); maxfd = MAX(lsocket, serial) + 1; } ret = select(maxfd, &iset, NULL, NULL, NULL); if (ret == -1) { perror("select"); exit(1); } if (FD_ISSET(serial, &iset)) { if (!last_serial) { printf("\n\nSERIAL:\n"); last_serial = 1; } ret = read(serial, buf, sizeof(buf)); if (ret < 0) { fprintf(stderr, "Error reading from serial: %m\n"); } else { if (csocket >= 0) write(csocket, buf, ret); write(1, buf, ret); } } if (FD_ISSET(lsocket, &iset) && (csocket < 0)) { csocket = do_logon(serial, lsocket); } if ((csocket >= 0) && FD_ISSET(csocket, &iset)) { if (last_serial) { printf("\n\nSOCKET:\n"); last_serial = 0; } ret = read(csocket, buf, sizeof(buf)); if (ret <= 0) { printf("Socket closed\n"); close(csocket); csocket = -1; } else { ret = remove_newlines(buf, ret); write(serial, buf, ret); write(1, buf, ret); if (strstr(buf, "B\r") == buf) { printf("Disconnecting...\n"); close(csocket); csocket = -1; } } } } return 0; } void usage(const char *argv0) { printf("Usage:\n" " %s [-p PORT] SERIAL\n" "\n" "Where:\n" " PORT is a numeric port to listen on (default is 2000)\n" " SERIAL is a path to the serial device to proxy\n" "\n", argv0); } int parse_args(int argc, char **argv, struct spopts *options) { int c; options->soc_port = 2000; while (1) { int option_index = 0; int ret; static struct option lopts[] = { {"port", 1, 0, 'p'}, {NULL, 0, 0, 0} }; c = getopt_long(argc, argv, "p:", lopts, &option_index); if (c == -1) break; switch (c) { case 'p': ret = sscanf(optarg, "%i", &options->soc_port); if (ret != 1) { fprintf(stderr, "Invalid port `%s'\n", optarg); return -1; } break; case '?': break; } } if (optind == argc) { fprintf(stderr, "Missing serial port\n"); return -1; } options->ser_port = argv[optind]; return 0; } int main(int argc, char **argv) { int serial; int lsocket; struct spopts options; if (parse_args(argc, argv, &options)) { usage(argv[0]); exit(1); } serial = open_serial(options.ser_port); if (serial < 0) { perror("serial"); return 1; } lsocket = listen_on(options.soc_port); if (lsocket < 0) { perror("socket"); return 1; } proxy(serial, lsocket); return 0; }