From a810729f9c6413f0b0fd61643f9ce5d3c2553d6d Mon Sep 17 00:00:00 2001 From: kcwu Date: Sat, 6 Jun 2009 17:55:53 +0000 Subject: - extract telnet.c to cmsys git-svn-id: http://opensvn.csie.org/pttbbs/trunk/pttbbs@4505 63ad8ddf-47c3-0310-b6dd-a9e9d9715204 --- mbbsd/mbbsd.c | 1 + mbbsd/telnet.c | 332 +++++---------------------------------------------------- mbbsd/var.c | 1 - 3 files changed, 30 insertions(+), 304 deletions(-) (limited to 'mbbsd') diff --git a/mbbsd/mbbsd.c b/mbbsd/mbbsd.c index fd2fd73f..f7fc9dd5 100644 --- a/mbbsd/mbbsd.c +++ b/mbbsd/mbbsd.c @@ -705,6 +705,7 @@ login_query(void) outs("½Ð­«·s¿é¤J¡C\n"); continue; } + telnet_turnoff_client_detect(); #ifdef CONVERT /* switch to gb mode if uid end with '.' */ diff --git a/mbbsd/telnet.c b/mbbsd/telnet.c index 332b3f3a..45f000a7 100644 --- a/mbbsd/telnet.c +++ b/mbbsd/telnet.c @@ -1,68 +1,37 @@ -/* - * piaip's simplified implementation of TELNET protocol - */ -#ifdef DEBUG -#define TELOPTS -#define TELCMDS -#endif - #include "bbs.h" +static TelnetCtx telnet_ctx; +static char raw_connection = 0; + #ifdef DETECT_CLIENT -void UpdateClientCode(unsigned char c); // see mbbsd.c -#endif +extern void UpdateClientCode(unsigned char c); -unsigned int telnet_handler(unsigned char c) ; -void telnet_init(void); -ssize_t tty_read(unsigned char *buf, size_t max); +void telnet_cb_update_client_code(void *ccctx, unsigned char c) +{ + UpdateClientCode(c); +} +#endif -enum TELNET_IAC_STATES { - IAC_NONE, - IAC_COMMAND, - IAC_WAIT_OPT, - IAC_WAIT_SE, - IAC_PROCESS_OPT, - IAC_ERROR +const static struct TelnetCallback telnet_callback = { + term_resize, +#ifdef DETECT_CLIENT + telnet_cb_update_client_code, +#else + NULL, +#endif }; -static unsigned char iac_state = 0; /* as byte to reduce memory */ - -#define TELNET_IAC_MAXLEN (16) -/* We don't reply to most commands, so this maxlen can be minimal. - * Warning: if you want to support ENV passing or other long commands, - * remember to increase this value. Howver, some poorly implemented - * terminals like xxMan may not follow the protocols and user will hang - * on those terminals when IACs were sent. - */ - void telnet_init(void) { - /* We are the boss. We don't respect to client. - * It's client's responsibility to follow us. - * Please write these codes in i-dont-care opt handlers. - */ - const char telnet_init_cmds[] = { - /* retrieve terminal type and throw away. - * why? because without this, clients enter line mode. - */ - IAC, DO, TELOPT_TTYPE, - IAC, SB, TELOPT_TTYPE, TELQUAL_SEND, IAC, SE, - - /* i'm a smart term with resize ability. */ - IAC, DO, TELOPT_NAWS, - - /* i will echo. */ - IAC, WILL, TELOPT_ECHO, - /* supress ga. */ - IAC, WILL, TELOPT_SGA, - /* 8 bit binary. */ - IAC, WILL, TELOPT_BINARY, - IAC, DO, TELOPT_BINARY, - }; - + int fd = 0; + TelnetCtx *ctx = &telnet_ctx; raw_connection = 1; - write(0, telnet_init_cmds, sizeof(telnet_init_cmds)); + telnet_ctx_init(ctx, &telnet_callback, fd); +#ifdef DETECT_CLIENT + telnet_ctx_set_ccctx(ctx, (void*)1); +#endif + telnet_send_init_cmds(fd); } /* tty_read @@ -73,6 +42,7 @@ ssize_t tty_read(unsigned char *buf, size_t max) { ssize_t l = read(0, buf, max); + TelnetCtx *ctx = &telnet_ctx; if(l == 0 || (l < 0 && !(errno == EINTR || errno == EAGAIN))) abort_bbs(0); @@ -80,259 +50,15 @@ tty_read(unsigned char *buf, size_t max) if(!raw_connection) return l; - /* process buffer */ - if (l > 0) { - unsigned char *buf2 = buf; - size_t i = 0, i2 = 0; - - /* prescan. because IAC is rare, - * this cost is worthy. */ - if (iac_state == IAC_NONE && memchr(buf, IAC, l) == NULL) - return l; - - /* we have to look into the buffer. */ - for (i = 0; i < l; i++, buf++) - if(telnet_handler(*buf) == 0) - *(buf2++) = *buf; - else - i2 ++; - l = (i2 == l) ? -1L : l - i2; - } + l = telnet_process(ctx, buf, l); return l; } -#ifdef DBG_OUTRPT -extern unsigned char fakeEscape; -#endif // DBG_OUTRPT - -/* input: raw character - * output: telnet command if c was handled, otherwise zero. - */ -unsigned int -telnet_handler(unsigned char c) +void +telnet_turnoff_client_detect(void) { - static unsigned char iac_quote = 0; /* as byte to reduce memory */ - static unsigned char iac_opt_req = 0; - - static unsigned char iac_buf[TELNET_IAC_MAXLEN]; - static unsigned int iac_buflen = 0; - - /* we have to quote all IACs. */ - if(c == IAC && !iac_quote) { - iac_quote = 1; - return NOP; - } - -#ifdef DETECT_CLIENT - /* hash client telnet sequences */ - if(cuser.userid[0]==0) { - if(iac_state == IAC_WAIT_SE) { - // skip suboption - } else { - if(iac_quote) - UpdateClientCode(IAC); - UpdateClientCode(c); - } - } -#endif - - /* a special case is the top level iac. otherwise, iac is just a quote. */ - if (iac_quote) { - if(iac_state == IAC_NONE) - iac_state = IAC_COMMAND; - if(iac_state == IAC_WAIT_SE && c == SE) - iac_state = IAC_PROCESS_OPT; - iac_quote = 0; - } - - /* now, let's process commands by state */ - switch(iac_state) { - - case IAC_NONE: - return 0; - - case IAC_COMMAND: -#if 0 // def DEBUG - { - int cx = c; /* to make compiler happy */ - write(0, "-", 1); - if(TELCMD_OK(cx)) - write(0, TELCMD(c), strlen(TELCMD(c))); - write(0, " ", 1); - } -#endif - iac_state = IAC_NONE; /* by default we restore state. */ - switch(c) { - case IAC: - // return 0; - // we don't want to allow IACs as input. - return 1; - - /* we don't want to process these. or maybe in future. */ - case BREAK: /* break */ -#ifdef DBG_OUTRPT - fakeEscape = !fakeEscape; - return NOP; -#endif - - case ABORT: /* Abort process */ - case SUSP: /* Suspend process */ - case AO: /* abort output--but let prog finish */ - case IP: /* interrupt process--permanently */ - case EOR: /* end of record (transparent mode) */ - case DM: /* data mark--for connect. cleaning */ - case xEOF: /* End of file: EOF is already used... */ - return NOP; - - case NOP: /* nop */ - return NOP; - - /* we should process these, but maybe in future. */ - case GA: /* you may reverse the line */ - case EL: /* erase the current line */ - case EC: /* erase the current character */ - return NOP; - - /* good */ - case AYT: /* are you there */ - { - const char *alive = "I'm still alive, loading: "; - char buf[STRLEN]; - - /* respond as fast as we can */ - write(0, alive, strlen(alive)); - cpuload(buf); - write(0, buf, strlen(buf)); - write(0, "\r\n", 2); - } - return NOP; - - case DONT: /* you are not to use option */ - case DO: /* please, you use option */ - case WONT: /* I won't use option */ - case WILL: /* I will use option */ - iac_opt_req = c; - iac_state = IAC_WAIT_OPT; - return NOP; - - case SB: /* interpret as subnegotiation */ - iac_state = IAC_WAIT_SE; - iac_buflen = 0; - return NOP; - - case SE: /* end sub negotiation */ - default: - return NOP; - } - return 1; - - case IAC_WAIT_OPT: -#if 0 // def DEBUG - write(0, "-", 1); - if(TELOPT_OK(c)) - write(0, TELOPT(c), strlen(TELOPT(c))); - write(0, " ", 1); -#endif - iac_state = IAC_NONE; - /* - * According to RFC, there're some tricky steps to prevent loop. - * However because we have a poor term which does not allow - * most abilities, let's be a strong boss here. - * - * Although my old imeplementation worked, it's even better to follow this: - * http://www.tcpipguide.com/free/t_TelnetOptionsandOptionNegotiation-3.htm - */ - switch(c) { - /* i-dont-care: i don't care about what client is. - * these should be clamed in init and - * client must follow me. */ - case TELOPT_TTYPE: /* termtype or line. */ - case TELOPT_NAWS: /* resize terminal */ - case TELOPT_SGA: /* supress GA */ - case TELOPT_ECHO: /* echo */ - case TELOPT_BINARY: /* we are CJK. */ - break; - - /* i-dont-agree: i don't understand/agree these. - * according to RFC, saying NO stopped further - * requests so there'll not be endless loop. */ - case TELOPT_RCP: /* prepare to reconnect */ - default: - if (iac_opt_req == WILL || iac_opt_req == DO) - { - /* unknown option, reply with won't */ - unsigned char cmd[3] = { IAC, DONT, 0 }; - if(iac_opt_req == DO) cmd[1] = WONT; - cmd[2] = c; - write(0, cmd, sizeof(cmd)); - } - break; - } - return 1; - - case IAC_WAIT_SE: - iac_buf[iac_buflen++] = c; - /* no need to convert state because previous quoting will do. */ - - if(iac_buflen == TELNET_IAC_MAXLEN) { - /* may be broken protocol? - * whether finished or not, break for safety - * or user may be frozen. - */ - iac_state = IAC_NONE; - return 0; - } - return 1; - - case IAC_PROCESS_OPT: - iac_state = IAC_NONE; -#if 0 // def DEBUG - write(0, "-", 1); - if(TELOPT_OK(iac_buf[0])) - write(0, TELOPT(iac_buf[0]), strlen(TELOPT(iac_buf[0]))); - write(0, " ", 1); -#endif - switch(iac_buf[0]) { - - /* resize terminal */ - case TELOPT_NAWS: - { - int w = (iac_buf[1] << 8) + (iac_buf[2]); - int h = (iac_buf[3] << 8) + (iac_buf[4]); - term_resize(w, h); -#ifdef DETECT_CLIENT - if(cuser.userid[0]==0) { - UpdateClientCode(iac_buf[0]); - if(w==80 && h==24) - UpdateClientCode(1); - else if(w==80) - UpdateClientCode(2); - else if(h==24) - UpdateClientCode(3); - else - UpdateClientCode(4); - UpdateClientCode(IAC); - UpdateClientCode(SE); - } -#endif - } - break; - - default: -#ifdef DETECT_CLIENT - if(cuser.userid[0]==0) { - int i; - for(i=0;i