/*---------------------------------------------------------------------------*/ static void newdata(void) { u16_t len; u8_t c; char *dataptr; len = uip_datalen(); dataptr = (char *)uip_appdata; while(len > 0 && s.bufptr < sizeof(s.buf)) { c = *dataptr; ++dataptr; --len; switch(s.state) { case STATE_IAC: if(c == TELNET_IAC) { get_char(c); s.state = STATE_NORMAL; } else { switch(c) { case TELNET_WILL: s.state = STATE_WILL; break; case TELNET_WONT: s.state = STATE_WONT; break; case TELNET_DO: s.state = STATE_DO; break; case TELNET_DONT: s.state = STATE_DONT; break; default: s.state = STATE_NORMAL; break; } } break; case STATE_WILL: /* Reply with a DONT */ sendopt(TELNET_DONT, c); s.state = STATE_NORMAL; break; case STATE_WONT: /* Reply with a DONT */ sendopt(TELNET_DONT, c); s.state = STATE_NORMAL; break; case STATE_DO: /* Reply with a WONT */ sendopt(TELNET_WONT, c); s.state = STATE_NORMAL; break; case STATE_DONT: /* Reply with a WONT */ sendopt(TELNET_WONT, c); s.state = STATE_NORMAL; break; case STATE_NORMAL: if(c == TELNET_IAC) { s.state = STATE_IAC; } else { get_char(c); } break; } } }
/*---------------------------------------------------------------------------*/ static void newdata(void) { u16_t len; u8_t c; uint8_t *ptr; len = uip_datalen(); PRINTF("newdata len %d '%.*s'\n", len, len, (char *)uip_appdata); ptr = uip_appdata; while(len > 0 && s.bufptr < sizeof(s.buf)) { c = *ptr; PRINTF("newdata char '%c' %d %d state %d\n", c, c, len, s.state); ++ptr; --len; switch(s.state) { case STATE_IAC: if(c == TELNET_IAC) { get_char(c); s.state = STATE_NORMAL; } else { switch(c) { case TELNET_WILL: s.state = STATE_WILL; break; case TELNET_WONT: s.state = STATE_WONT; break; case TELNET_DO: s.state = STATE_DO; break; case TELNET_DONT: s.state = STATE_DONT; break; default: s.state = STATE_NORMAL; break; } } break; case STATE_WILL: /* Reply with a DONT */ sendopt(TELNET_DONT, c); s.state = STATE_NORMAL; break; case STATE_WONT: /* Reply with a DONT */ sendopt(TELNET_DONT, c); s.state = STATE_NORMAL; break; case STATE_DO: /* Reply with a WONT */ sendopt(TELNET_WONT, c); s.state = STATE_NORMAL; break; case STATE_DONT: /* Reply with a WONT */ sendopt(TELNET_WONT, c); s.state = STATE_NORMAL; break; case STATE_NORMAL: if(c == TELNET_IAC) { s.state = STATE_IAC; } else { get_char(c); } break; } } }
/* * read a maximum of size chars from remote host * using the telnet protocol. return chars read. */ int tcp_read __P3 (int,fd, char *,buffer, int,maxsize) { char state = CONN_LIST(fd).state; int i; static byte subopt[MAX_SUBOPT]; static int subchars; byte *p, *s, *linestart; while ((i = read(fd, buffer, maxsize)) < 0 && errno == EINTR) ; if (i == 0) { CONN_LIST(fd).state = NORMAL; tcp_close(NULL); return 0; } if (i < 0) { errmsg("read from socket"); return 0; } /* * scan through the buffer, * interpret telnet protocol escapes and MUME MPI messages */ for (s = p = linestart = (byte *)buffer; i; s++, i--) { switch (state) { case NORMAL: case ALTNORMAL: case GOT_R: case GOT_N: /* * Some servers like to send NULs and other funny chars. * Clean up as much as possible. */ switch (*s) { case IAC: state = GOTIAC; break; case '\r': /* start counting \r, unless just got \n */ if (state == NORMAL || state == ALTNORMAL) { /* * ALTNORMAL is safe here: \r cannot be in MPI header, * and any previous MPI header has already been rejected */ state = GOT_R; } else if (state == GOT_N) /* after \n\r, we forbid MPI messages */ state = ALTNORMAL; break; case '\n': state = GOT_N; *p++ = *s; linestart = p; break; case '\0': /* skip NULs */ break; default: /* first, flush any missing \r */ if (state == GOT_R) *p++ = '\r'; *p++ = *s; /* check for MUME MPI messages: */ if (p - linestart == MPILEN && !memcmp(linestart, MPI, MPILEN)) { if (!(CONN_LIST(fd).flags & IDEDITOR)) { PRINTF("#warning: MPI message received without #request editor!\n"); } else if (state == ALTNORMAL) { /* no MPI messages after \n\r */ PRINTF("#warning: MPI attack?\n"); } else { subchars = process_message((char*)s+1, i-1); /* no +MPILEN here, as it was already processed. */ s += subchars; i-= subchars; p = linestart; } } if (state != ALTNORMAL) state = NORMAL; break; } break; case GOTIAC: switch (*s) { case WILL: state = GOTWILL; break; case WONT: state = GOTWONT; break; case DO: state = GOTDO; break; case DONT: state = GOTDONT; break; case SB: /* BUG (multiple connections): */ state = GOTSB; /* there is only one subopt buffer */ subchars = 0; break; case IAC: *p++ = IAC; state = NORMAL; break; case GA: /* I should handle GA as end-of-prompt marker one day */ /* one day has come ;) - Max */ prompt_set_iac((char*)p); state = NORMAL; break; default: /* ignore the rest of the telnet commands */ #ifdef TELOPTS tty_printf("[skipped IAC <%d>]\n", *s); #endif state = NORMAL; break; } break; case GOTWILL: #ifdef TELOPTS tty_printf("[got WILL %s]\n", TELOPTSTR(*s)); #endif switch(*s) { case TELOPT_ECHO: /* host echoes, turn off echo here * but only for main connection, since we do not want * subsidiary connection password entries to block anything * in the main connection */ if (fd == tcp_main_fd) linemode |= LM_NOECHO; sendopt(DO, *s); break; case TELOPT_SGA: /* this can't hurt */ linemode |= LM_CHAR; tty_special_keys(); sendopt(DO, *s); break; default: /* don't accept other options */ sendopt(DONT, *s); break; } state = NORMAL; break; case GOTWONT: #ifdef TELOPTS tty_printf("[got WONT %s]\n", TELOPTSTR(*s)); #endif if (*s == TELOPT_ECHO) { /* host no longer echoes, we do it instead */ linemode &= ~LM_NOECHO; } /* accept any WONT */ sendopt(DONT, *s); state = NORMAL; break; case GOTDO: #ifdef TELOPTS tty_printf("[got DO %s]\n", TELOPTSTR(*s)); #endif switch(*s) { case TELOPT_SGA: linemode |= LM_CHAR; tty_special_keys(); /* FALLTHROUGH */ case TELOPT_TTYPE: sendopt(WILL, *s); break; case TELOPT_NAWS: sendopt(WILL, *s); tcp_write_tty_size(); break; default: /* accept nothing else */ sendopt(WONT, *s); break; } state = NORMAL; break; case GOTDONT: #ifdef TELOPTS tty_printf("[got DONT %s]\n", TELOPTSTR(*s)); #endif if (*s == TELOPT_SGA) { linemode &= ~LM_CHAR; tty_special_keys(); } sendopt(WONT, *s); state = NORMAL; break; case GOTSB: if (*s == IAC) { state = GOTSBIAC; } else { if (subchars < MAX_SUBOPT) subopt[subchars++] = *s; } break; case GOTSBIAC: if (*s == IAC) { if (subchars < MAX_SUBOPT) subopt[subchars++] = IAC; state = GOTSB; } else if (*s == SE) { subopt[subchars] = '\0'; dosubopt(subopt); state = NORMAL; } else { /* problem! I haven't the foggiest idea of what to do here. * I'll just ignore it and hope it goes away. */ PRINTF("#telnet: got IAC <%d> instead of IAC SE!\n", (int)*s); state = NORMAL; } break; } } CONN_LIST(fd).state = state; if (!(CONN_LIST(tcp_fd).flags & SPAWN)) { log_write(buffer, (char *)p - buffer, 0); } return (char *)p - buffer; }