diff options
author | piaip <piaip@63ad8ddf-47c3-0310-b6dd-a9e9d9715204> | 2007-12-13 01:47:03 +0800 |
---|---|---|
committer | piaip <piaip@63ad8ddf-47c3-0310-b6dd-a9e9d9715204> | 2007-12-13 01:47:03 +0800 |
commit | a752a92e27ab75785c9c13fbf7e5fb47e0f06a02 (patch) | |
tree | 204beef0b9f3dd20ffd88135a3cf026f1aed5d5e /mbbsd/mbbsd.c | |
parent | d45cf2b2910628b10aa71f68a5eb0fc88722e517 (diff) | |
download | pttbbs-a752a92e27ab75785c9c13fbf7e5fb47e0f06a02.tar pttbbs-a752a92e27ab75785c9c13fbf7e5fb47e0f06a02.tar.gz pttbbs-a752a92e27ab75785c9c13fbf7e5fb47e0f06a02.tar.bz2 pttbbs-a752a92e27ab75785c9c13fbf7e5fb47e0f06a02.tar.lz pttbbs-a752a92e27ab75785c9c13fbf7e5fb47e0f06a02.tar.xz pttbbs-a752a92e27ab75785c9c13fbf7e5fb47e0f06a02.tar.zst pttbbs-a752a92e27ab75785c9c13fbf7e5fb47e0f06a02.zip |
- isolate telnet protocol. moved from mbbsd.c
git-svn-id: http://opensvn.csie.org/pttbbs/trunk/pttbbs@3679 63ad8ddf-47c3-0310-b6dd-a9e9d9715204
Diffstat (limited to 'mbbsd/mbbsd.c')
-rw-r--r-- | mbbsd/mbbsd.c | 324 |
1 files changed, 7 insertions, 317 deletions
diff --git a/mbbsd/mbbsd.c b/mbbsd/mbbsd.c index 41a8ab4a..499d0421 100644 --- a/mbbsd/mbbsd.c +++ b/mbbsd/mbbsd.c @@ -1,8 +1,4 @@ /* $Id$ */ -#ifdef DEBUG -#define TELOPTS -#define TELCMDS -#endif #include "bbs.h" #include "banip.h" @@ -36,8 +32,14 @@ static char remoteusername[40] = "?"; static unsigned char enter_uflag; static int use_shell_login_mode = 0; + #ifdef DETECT_CLIENT Fnv32_t client_code=FNV1_32_INIT; + +void UpdateClientCode(unsigned char c) +{ + FNV1A_CHAR(c, client_code); +} #endif #ifdef USE_RFORK @@ -278,7 +280,7 @@ abort_bbs_debug(int sig) /* log */ /* assume vsnprintf() in log_file() is signal-safe, is it? */ log_filef("log/crash.log", LOG_CREAT, - "%ld %d %d %.12s\n", time4(NULL), getpid(), sig, cuser.userid); + "%d %d %d %.12s\n", time4(NULL), getpid(), sig, cuser.userid); /* try logout... not a good idea, maybe crash again. now disabled */ /* @@ -1581,9 +1583,6 @@ shell_login(int argc, char *argv[], char *envp[]) return 1; } -void telnet_init(void); -unsigned int telnet_handler(unsigned char c) ; - static int daemon_login(int argc, char *argv[], char *envp[]) { @@ -1804,314 +1803,5 @@ static int check_banip(char *host) return uintbsearch(thisip, &banip[1], banip[0]) ? 1 : 0; } -/* ------- piaip's implementation of TELNET protocol ------- */ - -enum TELNET_IAC_STATES { - IAC_NONE, - IAC_COMMAND, - IAC_WAIT_OPT, - IAC_WAIT_SE, - IAC_PROCESS_OPT, - IAC_ERROR -}; - -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, - }; - - raw_connection = 1; - write(0, telnet_init_cmds, sizeof(telnet_init_cmds)); -} - -/* tty_read - * read from tty, process telnet commands if raw connection. - * return: >0 = length, <=0 means read more, abort/eof is automatically processed. - */ -ssize_t -tty_read(unsigned char *buf, size_t max) -{ - ssize_t l = read(0, buf, max); - - if(l == 0 || (l < 0 && !(errno == EINTR || errno == EAGAIN))) - abort_bbs(0); - - 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; - } - return l; -} - -/* input: raw character - * output: telnet command if c was handled, otherwise zero. - */ -unsigned int -telnet_handler(unsigned char c) -{ - 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) - FNV1A_CHAR(IAC, client_code); - FNV1A_CHAR(c, client_code); - } - } -#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: -#ifdef 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 process these. or maybe in future. */ - case BREAK: /* break */ - 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: -#ifdef 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; -#ifdef 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) { - FNV1A_CHAR(iac_buf[0], client_code); - if(w==80 && h==24) - FNV1A_CHAR(1, client_code); - else if(w==80) - FNV1A_CHAR(2, client_code); - else if(h==24) - FNV1A_CHAR(3, client_code); - else - FNV1A_CHAR(4, client_code); - FNV1A_CHAR(IAC, client_code); - FNV1A_CHAR(SE, client_code); - } -#endif - } - break; - - default: -#ifdef DETECT_CLIENT - if(cuser.userid[0]==0) { - int i; - for(i=0;i<iac_buflen;i++) - FNV1A_CHAR(iac_buf[i], client_code); - FNV1A_CHAR(IAC, client_code); - FNV1A_CHAR(SE, client_code); - } -#endif - break; - } - return 1; - } - return 1; /* never reached */ -} /* vim: sw=4 */ |