# #include "util.h" #include "telnet.h" #include struct scb scb; int pty; FILE *ptyr, *ptyw; int nptyerr = 0; #define NPTY 4 char *ptydev[NPTY] = { "/dev/ptyA", "/dev/ptyB", "/dev/ptyC", "/dev/ptyD" }; tel_init() { register struct scb *pscb; pscb = &scb; pscb->s_state = DRCV|DSND; pscb->s_rstate = NORMAL; pscb->s_rspecial = NORMAL; pscb->s_sstate = NORMAL; pscb->s_sspecial = NORMAL; time(&pscb->s_rtime); pscb->s_tmngo = NORMAL; if (!pty_init()) { tel_close("No teletype lines available for login"); return(FALSE); } pscb->s_echom = WILL; pscb->s_echongo = WILL; tc_put(IAC); tc_put(WILL); tc_put(ECHO); pscb->s_sga = WILL; pscb->s_sgango = WILL; tc_put(IAC); tc_put(WILL); tc_put(SGA); pscb->s_usga = WILL; pscb->s_usgango = DO; tc_put(IAC); tc_put(DO); tc_put(SGA); return(TRUE); } tel_close(str) char *str; { logerr("Telnet closing %s\n", str); tc_puts("\r\n\r\n"); tc_puts(str); tc_puts("\r\n"); scb.s_state = UCLOSED; tcp_close(); } tel_abort(str) char *str; { logerr("Telnet aborting %s\n", str); scb.s_state = UCLOSED; tcp_abort(); } tel_timeout() { register struct scb *pscb; pscb = &scb; if ((curtime - pscb->s_rtime) < TELTIMEOUT) return(FALSE); if (!(pscb->s_state & DSND)) { tel_abort("timeout"); return(TRUE); } pscb->s_rtime = curtime; if (pscb->s_tmngo == NORMAL) { pscb->s_tmngo = DO; tc_put(IAC); tc_put(DO); tc_put(TM); return(FALSE); } tel_close("timeout"); return(TRUE); } pty_init() { register int i; for (i = 0; i < NPTY; i++) if ((pty = open(ptydev[i], 2)) != ERR) break; if (pty == ERR) { logerr("pty_init cannot open to a pty\n"); return(FALSE); } ptyr = fcons(pty, "r"); ptyw = fcons(pty, "w"); if ((ptyr == NULL) || (ptyw == NULL)) { logerr("pty_init ptyr or ptyw is NULL %o %o\n", ptyr, ptyw); return(FALSE); } return(TRUE); } /* tel_rcv get chars wh. have been received from net and put on rseqq, do telnet processing on them, write chars to pty and possibly send some telnet option responses to sendbuf via tc_put */ tel_rcv() { register struct scb *pscb; register int ch, nch, outok; short temp, nc; pscb = &scb; if (!(pscb->s_state & DRCV)) return; ioctl(pty, FIONWRITE, &temp); nch = temp; outok = TRUE; nc = 0; while ( (nch > 0) && (outok) && ((ch = tc_get()) != EOF) ) { nc++; switch(pscb->s_rspecial) { case NORMAL: switch(ch) { case IAC: pscb->s_rspecial = ch; break; case '\r': pscb->s_rspecial = ch; default: if (pscb->s_rstate == NORMAL) { putc(ch, ptyw); --nch; } break; } break; case '\r': pscb->s_rspecial = NORMAL; if ((ch != '\n') && (ch != NUL) && (pscb->s_rstate == NORMAL)) { putc(ch, ptyw); --nch; } break; case IAC: pscb->s_rspecial = NORMAL; switch(ch) { case IAC: if (pscb->s_rstate == NORMAL) { putc(ch, ptyw); --nch; } break; case AYT: tc_puts(myname); outok = tc_puts(" here\r\n"); break; case BRK: case IP: tt_putc(CINTR, pty, ptyw); --nch; break; case AO: tt_putc(CSILENT, pty, ptyw); --nch; tc_put(IAC); outok = tc_put(DM); tcp_urg(); break; case EC: if (pscb->s_rstate == NORMAL) { tt_putc(CERASE, pty, ptyw); --nch; } break; case WILL: case WONT: case DO: case DONT: pscb->s_rspecial = ch; break; default: break; /* ignore IAC x */ } break; /* respond to option negotiation if not already in that mode and option negotiation request not outstanding */ case WILL: pscb->s_rspecial = NORMAL; switch(ch) { case TM: /* Timing mark */ if (pscb->s_tmngo == NORMAL) { tc_put(IAC); tc_put(DONT); outok = tc_put(TM); } else pscb->s_tmngo = NORMAL; break; case SGA: /* Suppress go ahead */ if ((pscb->s_usga == WONT) && (pscb->s_usgango == NORMAL)) { tc_put(IAC); tc_put(DO); outok = tc_put(SGA); } pscb->s_usga = WILL; pscb->s_usgango = NORMAL; break; default: tc_put(IAC); tc_put(DONT); outok = tc_put(ch); break; } break; case WONT: pscb->s_rspecial = NORMAL; switch(ch) { case TM: pscb->s_tmngo = NORMAL; break; case SGA: if ((pscb->s_usga == WILL) && (pscb->s_usgango == NORMAL) ) { tc_put(IAC); tc_put(DONT); outok = tc_put(SGA); } pscb->s_usga = WONT; pscb->s_usgango = NORMAL; break; default: break; } break; case DO: pscb->s_rspecial = NORMAL; switch(ch) { case SGA: if ((pscb->s_sga == WONT) && (pscb->s_sgango == NORMAL) ) { tc_put(IAC); tc_put(WILL); outok = tc_put(SGA); } pscb->s_sga = WILL; pscb->s_sgango = NORMAL; break; case ECHO: if ((pscb->s_echom == WONT) && (pscb->s_echongo == NORMAL) ) { tc_put(IAC); tc_put(WILL); outok = tc_put(ECHO); } if (pscb->s_echom == WONT) { temp = FORCECHOM|FECHO; ioctl(pty, TIOCCLRF2B, &temp); pscb->s_echom = WILL; } pscb->s_echongo = NORMAL; break; default: tc_put(IAC); tc_put(WONT); outok = tc_put(ch); break; } break; case DONT: pscb->s_rspecial = NORMAL; switch(ch) { case SGA: if ((pscb->s_sga == WILL) && (pscb->s_sgango == NORMAL) ) { tc_put(IAC); tc_put(WONT); outok = tc_put(SGA); } pscb->s_sga = WONT; pscb->s_sgango = NORMAL; break; case ECHO: if ((pscb->s_echom == WILL) && (pscb->s_echongo == NORMAL) ) { tc_put(IAC); tc_put(WONT); outok = tc_put(ECHO); } if (pscb->s_echom == WILL) { temp = FECHO; ioctl(pty, TIOCCLRF2B, &temp); temp = FORCECHOM; ioctl(pty, TIOCSETF2B, &temp); pscb->s_echom = WONT; } pscb->s_echongo = NORMAL; break; default: break; } break; default: logerr("tel_rcv strange rspecial char %c %c\n", pscb->s_rspecial, ch); pscb->s_rspecial = NORMAL; break; } } if (nc) pscb->s_rtime = curtime; fflush(ptyw); } /* tt_putc do ttymod to get tty's current char for special action (e.g. erase, interrupt process, abort output). Then write the char to wdev. */ tt_putc(cloc, dev, wdev) char cloc; short dev, wdev; { struct hilo { char byte0; char byte1; }; int cmds[2]; cmds[0].byte1 = cloc; cmds[1] = -1; ttymod(dev, cmds); putc(cmds[0].byte0, wdev); } /* tel_snd read chars from pty, do telnet processing on them, invoke tc_put to put chars in sendbuf (the buffer for chars to to be sent out to net). Number of chars to read from pty is determined from the stdio maintained counter of no. chars in stdio buffer and by ioctl to see how many chars are waiting in the kernel buffer for the device (i.e. in t_outq) Before exit from routine chk whether a cr is due to be sent - if so send cr nul. */ tel_snd() { register struct scb *pscb; register int nch, ch, outok; short temp; pscb = &scb; if ((!(pscb->s_state & DSND)) || (tc_nput() < 2) ) return; ioctl(pty, FIONREAD, &temp); nch = temp + ptyr->_cnt; outok = TRUE; while ((nch-- > 0) && (outok) ) { ch = getc(ptyr); if ((ch == EOF) && (nptyerr++ > NPTYERR) ) { logerr("tel_snd shutdown due to pty errors\n"); tel_close("Local teletype problems"); return; } switch(pscb->s_sspecial) { case NORMAL: switch(ch) { case IAC: tc_put(IAC); outok = tc_put(IAC); break; case '\r': pscb->s_sspecial = ch; break; default: outok = tc_put(ch); break; } break; case '\r': pscb->s_sspecial = NORMAL; tc_put('\r'); if (ch != '\n') tc_put(NUL); outok = tc_put(ch); break; default: logerr("tel_snd strange sspecial char %c %c\n", pscb->s_sspecial, ch); outok = tc_put(ch); pscb->s_sspecial = NORMAL; break; } } if (pscb->s_sspecial == '\r') { pscb->s_sspecial = NORMAL; tc_put('\r'); tc_put(NUL); } }