/* ttn.c */ #ifndef _POSIX_SOURCE #define _POSIX_SOURCE 1 #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ttn.h" #if __STDC__ #define PROTOTYPE(func,args) func args #else #define PROTOTYPE(func,args) func() #endif static int do_read(int fd, char *buf, unsigned len); static void screen(void); static void keyboard(void); static void send_brk(void); static int process_opt (char *bp, int count); static void do_option (int optsrt); static void dont_option (int optsrt); static void will_option (int optsrt); static void wont_option (int optsrt); static int writeall (int fd, char *buffer, int buf_size); static int sb_termtype (char *sb, int count); static void fatal(char *fmt, ...); static void usage(void); #if DEBUG #define where() (fprintf(stderr, "%s %d:", __FILE__, __LINE__)) #endif static char *prog_name; static tcp_fd; static char *term_env; static int esc_char= '~'; static enum { LS_NORM, LS_BOL, LS_ESC } line_state= LS_BOL; int main(int argc, char *argv[]) { struct hostent *hostent; struct servent *servent; ipaddr_t host; tcpport_t port; int pid, ppid; nwio_tcpconf_t tcpconf; int c, r; nwio_tcpcl_t tcpconnopt; struct termios termios; char *tcp_device, *remote_name, *port_name; char *e_arg; (prog_name=strrchr(argv[0],'/')) ? prog_name++ : (prog_name=argv[0]); e_arg= NULL; while (c= getopt(argc, argv, "?e:"), c != -1) { switch(c) { case '?': usage(); case 'e': e_arg= optarg; break; default: fatal("Optind failed: '%c'", c); } } if (optind >= argc) usage(); remote_name= argv[optind++]; if (optind < argc) port_name= argv[optind++]; else port_name= NULL; if (optind != argc) usage(); if (e_arg) { switch(strlen(e_arg)) { case 0: esc_char= -1; break; case 1: esc_char= e_arg[0]; break; default: fatal("Invalid escape character '%s'", e_arg); } } hostent= gethostbyname(remote_name); if (!hostent) fatal("Unknown host %s", remote_name); host= *(ipaddr_t *)(hostent->h_addr); if (!port_name) port= htons(TCPPORT_TELNET); else { servent= getservbyname (port_name, "tcp"); if (!servent) { port= htons(strtol(port_name, (char **)0, 0)); if (!port) fatal("Unknown port %s", port_name); } else port= (tcpport_t)(servent->s_port); } fprintf(stderr, "Connecting to %s:%u...\n", inet_ntoa(host), ntohs(port)); tcp_device= getenv("TCP_DEVICE"); if (tcp_device == NULL) tcp_device= TCP_DEVICE; tcp_fd= open (tcp_device, O_RDWR); if (tcp_fd == -1) fatal("Unable to open %s: %s", tcp_device, strerror(errno)); tcpconf.nwtc_flags= NWTC_LP_SEL | NWTC_SET_RA | NWTC_SET_RP; tcpconf.nwtc_remaddr= host; tcpconf.nwtc_remport= port; r= ioctl (tcp_fd, NWIOSTCPCONF, &tcpconf); if (r == -1) fatal("NWIOSTCPCONF failed: %s", strerror(errno)); tcpconnopt.nwtcl_flags= 0; do { r= ioctl (tcp_fd, NWIOTCPCONN, &tcpconnopt); if (r == -1 && errno == EAGAIN) { fprintf(stderr, "%s: Got EAGAIN, sleeping(1s)\n", prog_name); sleep(1); } } while (r == -1 && errno == EAGAIN); if (r == -1) fatal("Unable to connect: %s", strerror(errno)); printf("Connected\n"); ppid= getpid(); pid= fork(); switch(pid) { case 0: keyboard(); #if DEBUG fprintf(stderr, "killing %d with %d\r\n", ppid, SIGKILL); #endif kill(ppid, SIGKILL); break; case -1: fprintf(stderr, "%s: fork failed: %s\r\n", argv[0], strerror(errno)); exit(1); break; default: tcgetattr(0, &termios); screen(); #if DEBUG fprintf(stderr, "killing %d with %d\r\n", pid, SIGKILL); #endif kill(pid, SIGKILL); tcsetattr(0, TCSANOW, &termios); break; } exit(0); } static int do_read(fd, buf, len) int fd; char *buf; unsigned len; { nwio_tcpopt_t tcpopt; int count; for (;;) { count= read (fd, buf, len); if (count <0) { if (errno == EURG || errno == ENOURG) { /* Toggle urgent mode. */ tcpopt.nwto_flags= errno == EURG ? NWTO_RCV_URG : NWTO_RCV_NOTURG; if (ioctl(tcp_fd, NWIOSTCPOPT, &tcpopt) == -1) { return -1; } continue; } return -1; } return count; } } static void screen() { char buffer[1024], *bp, *iacptr; int count, optsize; for (;;) { count= do_read (tcp_fd, buffer, sizeof(buffer)); #if DEBUG && 0 { where(); fprintf(stderr, "read %d bytes\r\n", count); } #endif if (count <0) { perror ("read"); return; } if (!count) return; bp= buffer; do { iacptr= memchr (bp, IAC, count); if (!iacptr) { write(1, bp, count); count= 0; } if (iacptr && iacptr>bp) { #if DEBUG { where(); fprintf(stderr, "iacptr-bp= %d\r\n", iacptr-bp); } #endif write(1, bp, iacptr-bp); count -= (iacptr-bp); bp= iacptr; continue; } if (iacptr) { assert (iacptr == bp); optsize= process_opt(bp, count); #if DEBUG && 0 { where(); fprintf(stderr, "process_opt(...)= %d\r\n", optsize); } #endif if (optsize<0) return; assert (optsize); bp += optsize; count -= optsize; } } while (count); } } static void keyboard() { char c, buffer[1024]; int count; for (;;) { count= read (0, buffer, 1 /* sizeof(buffer) */); if (count == -1) fatal("Read: %s\r\n", strerror(errno)); if (!count) return; if (line_state != LS_NORM) { c= buffer[0]; if (line_state == LS_BOL) { if (c == esc_char) { line_state= LS_ESC; continue; } line_state= LS_NORM; } else if (line_state == LS_ESC) { line_state= LS_NORM; if (c == '.') return; if (c == '#') { send_brk(); continue; } /* Not a valid command or a repeat of the * escape char */ if (c != esc_char) { c= esc_char; write(tcp_fd, &c, 1); } } } if (buffer[0] == '\n') write(tcp_fd, "\r", 1); count= write(tcp_fd, buffer, count); if (buffer[0] == '\r') { line_state= LS_BOL; write(tcp_fd, "\0", 1); } if (count<0) { perror("write"); fprintf(stderr, "errno= %d\r\n", errno); return; } if (!count) return; } } static void send_brk(void) { int r; unsigned char buffer[2]; buffer[0]= IAC; buffer[1]= IAC_BRK; r= writeall(tcp_fd, (char *)buffer, 2); if (r == -1) fatal("Error writing to TCP connection: %s", strerror(errno)); } #define next_char(var) \ if (offset