summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/proto.h6
-rw-r--r--mbbsd/Makefile11
-rw-r--r--mbbsd/mbbsd.c324
-rw-r--r--mbbsd/telnet.c327
4 files changed, 344 insertions, 324 deletions
diff --git a/include/proto.h b/include/proto.h
index f62c1e7e..9a956b96 100644
--- a/include/proto.h
+++ b/include/proto.h
@@ -378,7 +378,6 @@ void talk_request(int sig);
int reply_connection_request(const userinfo_t *uip);
int establish_talk_connection(const userinfo_t *uip);
void my_talk(userinfo_t * uin, int fri_stat, char defact);
-ssize_t tty_read(unsigned char *buf, size_t max);
int query_file_money(const fileheader_t *pfh);
/* menu */
@@ -401,8 +400,11 @@ void m_sob_brd(char *bname,char *fromdir);
/* old more */
int more(char *fpath, int promptend);
-/* piaip's new pager */
+/* piaip's new pager, pmore.c */
int pmore(char *fpath, int promptend);
+/* piaip's new telnet, telnet.c */
+void telnet_init(void);
+ssize_t tty_read(unsigned char *buf, size_t max);
/* name */
typedef int (*gnc_comp_func)(int, const char*, int);
diff --git a/mbbsd/Makefile b/mbbsd/Makefile
index 84468ffc..0998c962 100644
--- a/mbbsd/Makefile
+++ b/mbbsd/Makefile
@@ -11,12 +11,13 @@ LDFLAGS+= -L/usr/local/lib/mysql -lmysqlclient
PROG= mbbsd
OBJS= admin.o announce.o args.o assess.o bbs.o board.o cache.o cal.o card.o\
- chat.o chc.o chc_tab.o chicken.o convert.o crypt.o dark.o edit.o fav.o friend.o gamble.o\
- gomo.o guess.o indict.o io.o kaede.o lovepaper.o mail.o mbbsd.o menu.o\
- more.o name.o osdep.o othello.o read.o record.o register.o\
+ chat.o chicken.o convert.o crypt.o edit.o fav.o friend.o gamble.o\
+ guess.o indict.o io.o kaede.o lovepaper.o mail.o mbbsd.o menu.o\
+ more.o name.o osdep.o read.o record.o register.o\
screen.o stuff.o talk.o term.o topsong.o user.o brc.o vice.o vote.o\
- xyz.o voteboard.o syspost.o var.o passwd.o calendar.o go.o file.o \
- pmore.o chess.o reversi.o
+ xyz.o voteboard.o syspost.o var.o passwd.o calendar.o file.o \
+ pmore.o telnet.o\
+ chc.o chc_tab.o chess.o go.o gomo.o dark.o reversi.o othello.o
.if defined(DIET)
OBJS+= random.o time.o alloc.o
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
*/
diff --git a/mbbsd/telnet.c b/mbbsd/telnet.c
new file mode 100644
index 00000000..aa985471
--- /dev/null
+++ b/mbbsd/telnet.c
@@ -0,0 +1,327 @@
+/*
+ * piaip's simplified implementation of TELNET protocol
+ */
+#ifdef DEBUG
+#define TELOPTS
+#define TELCMDS
+#endif
+
+#include "bbs.h"
+
+#ifdef DETECT_CLIENT
+void UpdateClientCode(unsigned char c); // see mbbsd.c
+#endif
+
+unsigned int telnet_handler(unsigned char c) ;
+void telnet_init(void);
+ssize_t tty_read(unsigned char *buf, size_t max);
+
+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)
+ 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 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:
+#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<iac_buflen;i++)
+ UpdateClientCode(iac_buf[i]);
+ UpdateClientCode(IAC);
+ UpdateClientCode(SE);
+ }
+#endif
+ break;
+ }
+ return 1;
+ }
+ return 1; /* never reached */
+}
+
+// vim: sw=4