/* * TNET A server program for MINIX which implements the TCP/IP * suite of networking protocols. It is based on the * TCP/IP code written by Phil Karn et al, as found in * his NET package for Packet Radio communications. * * This module handles telnet option processing. * * Author: Michael Temari, 01/13/93 * */ #include #include #include #include #include #include #include "telnetd.h" #include "telnet.h" #include #include #define IN_DATA 0 #define IN_CR 1 #define IN_IAC 2 #define IN_IAC2 3 #define IN_SB 4 _PROTOTYPE(static void dowill, (int c)); _PROTOTYPE(static void dowont, (int c)); _PROTOTYPE(static void dodo, (int c)); _PROTOTYPE(static void dodont, (int c)); _PROTOTYPE(static void respond, (int ack, int option)); _PROTOTYPE(static void respond_really, (int ack, int option)); #define LASTTELOPT TELOPT_SGA static int r_winch = 0; static int TelROpts[LASTTELOPT+1]; static int TelLOpts[LASTTELOPT+1]; static int telfdout; void tel_init() { int i; for(i = 0; i <= LASTTELOPT; i++) { TelROpts[i] = 0; TelLOpts[i] = 0; } } void telopt(fdout, what, option) int fdout; int what; int option; { char buf[3]; int len; buf[0] = IAC; buf[1] = what; buf[2] = option; len = 0; switch(what) { case DO: if(option <= LASTTELOPT) { TelROpts[option] = 1; len = 3; } else if(option == TELOPT_WINCH && !r_winch) { r_winch = 1; len = 3; } break; case DONT: if(option <= LASTTELOPT) { TelROpts[option] = 1; len = 3; } break; case WILL: if(option <= LASTTELOPT) { TelLOpts[option] = 1; len = 3; } break; case WONT: if(option <= LASTTELOPT) { TelLOpts[option] = 1; len = 3; } break; } if(len > 0) (void) write(fdout, buf, len); } int set_winsize(int fd, unsigned int cols, unsigned int rows) { struct winsize w; memset(&w, 0, sizeof(w)); w.ws_col = cols; w.ws_row = rows; ioctl(fd, TIOCSWINSZ, (char *) &w); } int tel_in(fdout, telout, buffer, len) int fdout; int telout; char *buffer; int len; { static int InState = IN_DATA; static int ThisOpt = 0; char *p; char *p2; int size; int c; telfdout = telout; p = p2 = buffer; size = 0; while(len > 0) { c = (unsigned char)*p++; len--; switch(InState) { case IN_CR: InState = IN_DATA; if(c == 0 || c == '\n') break; /* fall through */ case IN_DATA: if(c == IAC) { InState = IN_IAC; break; } *p2++ = c; size++; if(c == '\r') InState = IN_CR; break; case IN_IAC: switch(c) { case IAC: *p2++ = c; size++; InState = IN_DATA; break; case WILL: case WONT: case DO: case DONT: InState = IN_IAC2; ThisOpt = c; break; case SB: InState = IN_SB; break; case EOR: case SE: case NOP: case BREAK: case IP: case AO: case AYT: case EC: case EL: case GA: break; default: break; } break; case IN_IAC2: if(size > 0) { write(fdout, buffer, size); p2 = buffer; size = 0; } InState = IN_DATA; switch(ThisOpt) { case WILL: dowill(c); break; case WONT: dowont(c); break; case DO: dodo(c); break; case DONT: dodont(c); break; } break; case IN_SB: { static int winchpos = -1; /* Subnegotiation. */ if(winchpos >= 0) { static unsigned int winchbuf[5], iacs = 0; winchbuf[winchpos] = c; /* IAC is escaped - unescape it. */ if(c == IAC) iacs++; else { iacs = 0; winchpos++; } if(iacs == 2) { winchpos++; iacs = 0; } if(winchpos >= 4) { /* End of WINCH data. */ set_winsize(fdout, (winchbuf[0] << 8) | winchbuf[1], (winchbuf[2] << 8) | winchbuf[3]); winchpos = -1; } } else { static int lastiac = 0; switch(c) { case TELOPT_WINCH: /* Start listening. */ winchpos = 0; break; case SE: if(lastiac) InState = IN_DATA; break; default: break; } if(c == IAC) lastiac = 1; else lastiac = 0; } break; } } } if(size > 0) write(fdout, buffer, size); } int tel_out(fdout, buf, size) int fdout; char *buf; int size; { char *p; int got_iac, len; p = buf; while(size > 0) { buf = p; got_iac = 0; if((p = (char *)memchr(buf, IAC, size)) != (char *)NULL) { got_iac = 1; p++; } else p = buf + size; len = p - buf; if(len > 0) (void) write(fdout, buf, len); if(got_iac) (void) write(fdout, p - 1, 1); size = size - len; } } static void dowill(c) int c; { int ack; switch(c) { case TELOPT_BINARY: case TELOPT_ECHO: case TELOPT_SGA: if(TelROpts[c] == 1) return; TelROpts[c] = 1; ack = DO; break; case TELOPT_WINCH: if(r_winch) return; r_winch = 1; ack = DO; respond_really(ack, c); return; default: ack = DONT; } respond(ack, c); } static void dowont(c) int c; { if(c <= LASTTELOPT) { if(TelROpts[c] == 0) return; TelROpts[c] = 0; } respond(DONT, c); } static void dodo(c) int c; { int ack; switch(c) { default: ack = WONT; } respond(ack, c); } static void dodont(c) int c; { if(c <= LASTTELOPT) { if(TelLOpts[c] == 0) return; TelLOpts[c] = 0; } respond(WONT, c); } static void respond(ack, option) int ack, option; { unsigned char c[3]; c[0] = IAC; c[1] = ack; c[2] = option; /* write(telfdout, c, 3); */ } static void respond_really(ack, option) int ack, option; { unsigned char c[3]; c[0] = IAC; c[1] = ack; c[2] = option; write(telfdout, c, 3); }