#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../pathnames.h" static int kissfd, nrsfd; static int logging = FALSE; static int debugging = FALSE; static int flowcontrol = FALSE; static char *kissdev, *nrsdev; #define NUL 000 #define STX 002 #define ETX 003 #define DLE 020 #define NRS_WAIT 0 #define NRS_DATA 1 #define NRS_ESCAPE 2 #define NRS_CKSUM 3 static int nrs_state = NRS_WAIT; static unsigned char nrs_cksum = 0; static unsigned char nrs_rxbuffer[512]; static int nrs_rxcount = 0; #define FEND 0300 #define FESC 0333 #define FESCEND 0334 #define FESCESC 0335 #define KISS_WAIT 0 #define KISS_CTRL 1 #define KISS_DATA 2 #define KISS_ESCAPE 3 static int kiss_state = KISS_WAIT; static unsigned char kiss_rxbuffer[512]; static int kiss_rxcount = 0; static void terminate(int sig) { if (logging) { syslog(LOG_INFO, "terminating on SIGTERM\n"); closelog(); } tty_unlock(kissdev); tty_unlock(nrsdev); exit(0); } static void key_rts(int fd) { int status; if (!flowcontrol) return; /* Wait for CTS to be low */ while (1) { /* Get CTS status */ if (ioctl(fd, TIOCMGET, &status) < 0) { syslog(LOG_INFO|LOG_ERR, "TIOCMGET failed: flowcontrol disabled (%m)\n"); flowcontrol = 0; return; } if (status & TIOCM_CTS) { if (debugging) { fprintf(stderr,"CTS high: waiting\n"); } ioctl(fd, TIOCMIWAIT, &status); } else { break; } } if (debugging) { fprintf(stderr,"CTS low: keying RTS\n"); } status |= TIOCM_RTS | TIOCM_DTR; if (ioctl(fd, TIOCMSET, &status) < 0) { syslog(LOG_INFO|LOG_ERR, "TIOCMGET failed: flowcontrol disabled (%m)\n"); flowcontrol = 0; } } static void unkey_rts(int fd) { int status; if (!flowcontrol) return; if (debugging) { fprintf(stderr,"Transmission finished: unkeying RTS\n"); } ioctl(fd, TIOCMGET, &status); status &= ~TIOCM_RTS; status |= TIOCM_DTR; if (ioctl(fd, TIOCMSET, &status) < 0) { syslog(LOG_INFO|LOG_ERR, "TIOCMGET failed: flowcontrol disabled (%m)\n"); flowcontrol = 0; } } static void nrs_esc(unsigned char *s, int len) { static unsigned char buffer[512]; unsigned char *ptr = buffer; unsigned char csum = 0; unsigned char c; *ptr++ = STX; while (len-- > 0) { switch (c = *s++) { case STX: case ETX: case DLE: *ptr++ = DLE; /* Fall through */ default: *ptr++ = c; break; } csum += c; } *ptr++ = ETX; *ptr++ = csum; *ptr++ = NUL; *ptr++ = NUL; key_rts(nrsfd); write(nrsfd, buffer, ptr - buffer); unkey_rts(nrsfd); } static void kiss_esc(unsigned char *s, int len) { static unsigned char buffer[512]; unsigned char *ptr = buffer; unsigned char c; *ptr++ = FEND; *ptr++ = 0x00; /* KISS DATA */ while (len-- > 0) { switch (c = *s++) { case FESC: *ptr++ = FESC; *ptr++ = FESCESC; break; case FEND: *ptr++ = FESC; *ptr++ = FESCEND; break; default: *ptr++ = c; break; } } *ptr++ = FEND; write(kissfd, buffer, ptr - buffer); } static void nrs_unesc(unsigned char *buffer, int len) { int i; for (i = 0; i < len; i++) { switch (nrs_state) { case NRS_WAIT: if (buffer[i] == STX) { nrs_state = NRS_DATA; nrs_rxcount = 0; nrs_cksum = 0; } break; case NRS_DATA: switch (buffer[i]) { case STX: /* !! */ nrs_rxcount = 0; nrs_cksum = 0; break; case DLE: nrs_state = NRS_ESCAPE; break; case ETX: nrs_state = NRS_CKSUM; break; default: if (nrs_rxcount < 512) { nrs_cksum += buffer[i]; nrs_rxbuffer[nrs_rxcount++] = buffer[i]; } break; } break; case NRS_ESCAPE: nrs_state = NRS_DATA; if (nrs_rxcount < 512) { nrs_cksum += buffer[i]; nrs_rxbuffer[nrs_rxcount++] = buffer[i]; } break; case NRS_CKSUM: if (buffer[i] == nrs_cksum) kiss_esc(nrs_rxbuffer, nrs_rxcount); nrs_state = NRS_WAIT; nrs_cksum = 0; nrs_rxcount = 0; break; } } } static void kiss_unesc(unsigned char *buffer, int len) { int i; for (i = 0; i < len; i++) { switch (kiss_state) { case KISS_WAIT: if (buffer[i] == FEND) { kiss_state = KISS_CTRL; kiss_rxcount = 0; } break; case KISS_CTRL: if ((buffer[i] & 0x0F) == 0x00) { kiss_state = KISS_DATA; kiss_rxcount = 0; } else { kiss_state = KISS_WAIT; kiss_rxcount = 0; } break; case KISS_DATA: switch (buffer[i]) { case FEND: if (kiss_rxcount > 2) nrs_esc(kiss_rxbuffer, kiss_rxcount); kiss_state = KISS_WAIT; kiss_rxcount = 0; break; case FESC: kiss_state = KISS_ESCAPE; break; default: if (kiss_rxcount < 512) kiss_rxbuffer[kiss_rxcount++] = buffer[i]; break; } break; case KISS_ESCAPE: kiss_state = KISS_DATA; switch (buffer[i]) { case FESCESC: if (kiss_rxcount < 512) kiss_rxbuffer[kiss_rxcount++] = FESC; break; case FESCEND: if (kiss_rxcount < 512) kiss_rxbuffer[kiss_rxcount++] = FEND; break; } break; } } } int main(int argc, char *argv[]) { static char buffer[512]; unsigned int speed = 0; fd_set read_fd; int c, n; while ((c = getopt(argc, argv, "dfls:v")) != -1) { switch (c) { case 'd': debugging = TRUE; break; case 'f': flowcontrol = TRUE; break; case 'l': logging = TRUE; break; case 's': if ((speed = atoi(optarg)) <= 0) { fprintf(stderr, "nrsdrv: invalid speed %s\n", optarg); return 1; } break; case 'v': printf("kissattach: %s\n", VERSION); return 0; case ':': case '?': fprintf(stderr, "usage: nrsdrv [-f] [-l] [-s speed] [-v] kisstty nrstty\n"); return 1; } } if (debugging) { fprintf(stderr,"Flow control %s\n", flowcontrol ? "enabled" : "disabled"); } if ((argc - optind) != 2) { fprintf(stderr, "usage: nrsdrv [-f] [-l] [-s speed] [-v] kisstty nrstty\n"); return 1; } kissdev = argv[optind + 0]; nrsdev = argv[optind + 1]; if (tty_is_locked(kissdev)) { fprintf(stderr, "nrsdrv: device %s already in use\n", argv[optind]); return 1; } if (tty_is_locked(nrsdev)) { fprintf(stderr, "nrsdrv: device %s already in use\n", argv[optind + 1]); return 1; } if ((kissfd = open(kissdev, O_RDWR)) == -1) { perror("nrsdrv: open kiss device"); return 1; } if ((nrsfd = open(nrsdev, O_RDWR)) == -1) { perror("nrsdrv: open nrs device"); return 1; } tty_lock(kissdev); tty_lock(nrsdev); if (!tty_raw(kissfd, FALSE)) { tty_unlock(kissdev); tty_unlock(nrsdev); return 1; } if (!tty_raw(nrsfd, FALSE)) { tty_unlock(kissdev); tty_unlock(nrsdev); return 1; } if (speed != 0 && !tty_speed(kissfd, speed)) { tty_unlock(kissdev); tty_unlock(nrsdev); return 1; } if (speed != 0 && !tty_speed(nrsfd, speed)) { tty_unlock(kissdev); tty_unlock(nrsdev); return 1; } if (logging) { openlog("nrsdrv", LOG_PID, LOG_DAEMON); syslog(LOG_INFO, "KISS device %s connected to NRS device %s\n", argv[optind + 0], argv[optind + 1]); } signal(SIGHUP, SIG_IGN); signal(SIGTERM, terminate); /* * Become a daemon if we can. */ if (!daemon_start(FALSE)) { fprintf(stderr, "nrsdrv: cannot become a daemon\n"); tty_unlock(kissdev); tty_unlock(nrsdev); return 1; } if (flowcontrol) { unkey_rts(nrsfd); } c = ((kissfd > nrsfd) ? kissfd : nrsfd) + 1; for (;;) { FD_ZERO(&read_fd); FD_SET(kissfd, &read_fd); FD_SET(nrsfd, &read_fd); n = select(c, &read_fd, NULL, NULL, NULL); if (FD_ISSET(kissfd, &read_fd)) { if ((n = read(kissfd, buffer, 512)) <= 0) { if (logging) { syslog(LOG_INFO, "terminating on KISS device closure\n"); closelog(); } break; } kiss_unesc(buffer, n); } if (FD_ISSET(nrsfd, &read_fd)) { if ((n = read(nrsfd, buffer, 512)) <= 0) { if (logging) { syslog(LOG_INFO, "terminating on NRS device closure\n"); closelog(); } break; } nrs_unesc(buffer, n); } } tty_unlock(kissdev); tty_unlock(nrsdev); return 0; }