summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/convert.h3
-rw-r--r--include/proto.h1
-rw-r--r--mbbsd/convert.c97
-rw-r--r--mbbsd/io.c138
-rw-r--r--mbbsd/mbbsd.c307
-rw-r--r--sample/pttbbs.conf5
6 files changed, 363 insertions, 188 deletions
diff --git a/include/convert.h b/include/convert.h
index 9ce0b75a..cafa08b7 100644
--- a/include/convert.h
+++ b/include/convert.h
@@ -6,6 +6,7 @@
#define CONV_GB 1
#define CONV_UTF8 2
-typedef ssize_t (* read_write_type)(int, void *, size_t);
+typedef ssize_t (*read_write_type)(int, void *, size_t);
+typedef ssize_t (*convert_type)(void *, ssize_t);
#endif
diff --git a/include/proto.h b/include/proto.h
index d0544240..7daa496d 100644
--- a/include/proto.h
+++ b/include/proto.h
@@ -359,6 +359,7 @@ 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);
/* menu */
void showtitle(const char *title, const char *mid);
diff --git a/mbbsd/convert.c b/mbbsd/convert.c
index ff9fef5c..a7e1ff31 100644
--- a/mbbsd/convert.c
+++ b/mbbsd/convert.c
@@ -1,11 +1,8 @@
-/* $Id: convert.c 1374 2003-11-27 14:11:40Z victor $ */
+/* $Id$ */
#include "bbs.h"
#ifdef CONVERT
-extern read_write_type write_type;
-extern read_write_type read_type;
-
unsigned char *gb2big(unsigned char *, int *, int);
unsigned char *big2gb(unsigned char *, int *, int);
unsigned char *utf8_uni(unsigned char *, int *, int);
@@ -13,56 +10,104 @@ unsigned char *uni_utf8(unsigned char *, int *, int);
unsigned char *uni2big(unsigned char *, int *, int);
unsigned char *big2uni(unsigned char *, int *, int);
-static ssize_t gb_read(int fd, void *buf, size_t count)
+static ssize_t
+gb_input(void *buf, ssize_t icount)
+{
+ gb2big((char *)buf, &icount, 0);
+ return icount;
+}
+
+static ssize_t
+gb_read(int fd, void *buf, size_t count)
{
- int icount = (int)read(fd, buf, count);
- if (count > 0)
- gb2big((char *)buf, &icount, 0);
- return (size_t)icount;
+ ssize_t icount = read(fd, buf, count);
+ if (icount > 0)
+ icount = gb_input(buf, icount);
+ return icount;
}
-static ssize_t gb_write(int fd, void *buf, size_t count)
+static ssize_t
+gb_write(int fd, void *buf, size_t count)
{
int icount = (int)count;
big2gb((char *)buf, &icount, 0);
- return write(fd, buf, (size_t)icount);
+ if(icount > 0)
+ return write(fd, buf, (size_t)icount);
+ else
+ return count; /* fake */
}
-static ssize_t utf8_read(int fd, void *buf, size_t count)
+static ssize_t
+utf8_input (void *buf, ssize_t icount)
{
- count = read(fd, buf, count);
- if (count > 0) {
- int icount = (int)count;
- utf8_uni(buf, &icount, 0);
- uni2big(buf, &icount, 0);
- ((char *)buf)[icount] = 0;
- return (size_t)icount;
- }
- return count;
+ utf8_uni(buf, &icount, 0);
+ uni2big(buf, &icount, 0);
+ return icount;
+}
+
+static ssize_t
+utf8_read(int fd, void *buf, size_t count)
+{
+ ssize_t icount = read(fd, buf, count);
+ if (icount > 0)
+ icount = utf8_input(buf, icount);
+ return icount;
}
-static ssize_t utf8_write(int fd, void *buf, size_t count)
+static ssize_t
+utf8_write(int fd, void *buf, size_t count)
{
int icount = (int)count;
- big2uni(buf, &icount, 0);
- uni_utf8(buf, &icount, 0);
- ((char *)buf)[icount] = 0;
- return write(fd, buf, (size_t)icount);
+ static char *mybuf = NULL;
+ int cmybuf = 0;
+
+ /* utf8 output is a special case because
+ * we need larger buffer which can be
+ * tripple or more in size.
+ * Current implementation uses 128 for each block.
+ */
+
+ if(cmybuf < count * 4) {
+ cmybuf = (count*4+0x80) & (~0x7f) ;
+ mybuf = (char*) realloc (mybuf, cmybuf);
+ }
+ memcpy(mybuf, buf, count);
+ big2uni(mybuf, &icount, 0);
+ uni_utf8(mybuf, &icount, 0);
+ if(icount > 0)
+ return write(fd, mybuf, (size_t)icount);
+ else
+ return count; /* fake */
}
+static ssize_t
+norm_input(void *buf, ssize_t icount)
+{
+ return icount;
+}
+
+/* global function pointers */
+read_write_type write_type = (read_write_type)write;
+read_write_type read_type = read;
+convert_type input_type = norm_input;
+
void set_converting_type(int which)
{
if (which == CONV_NORMAL) {
read_type = read;
write_type = (read_write_type)write;
+ /* for speed up, NULL is better.. */
+ input_type = NULL; /* norm_input; */
}
else if (which == CONV_GB) {
read_type = gb_read;
write_type = gb_write;
+ input_type = gb_input;
}
else if (which == CONV_UTF8) {
read_type = utf8_read;
write_type = utf8_write;
+ input_type = utf8_input;
}
}
diff --git a/mbbsd/io.c b/mbbsd/io.c
index acbbe6f8..b2ffd476 100644
--- a/mbbsd/io.c
+++ b/mbbsd/io.c
@@ -4,7 +4,10 @@
#define OBUFSIZE 2048
#define IBUFSIZE 128
-static unsigned char outbuf[OBUFSIZE], inbuf[IBUFSIZE];
+/* realXbuf is Xbuf+3 because hz convert library requires buf[-2]. */
+static unsigned char real_outbuf[OBUFSIZE+6] = " ", real_inbuf[IBUFSIZE+6] = " ";
+static unsigned char *outbuf = real_outbuf + 3, *inbuf = real_inbuf + 3;
+
static int obufsize = 0, ibufsize = 0;
static int icurrchar = 0;
@@ -13,8 +16,18 @@ static int icurrchar = 0;
/* ----------------------------------------------------- */
#ifdef CONVERT
-read_write_type write_type = (read_write_type)write;
-read_write_type read_type = read;
+extern read_write_type write_type;
+extern read_write_type read_type;
+extern convert_type input_type;
+
+inline static ssize_t input_wrapper(void *buf, ssize_t count) {
+ /* input_wrapper is a special case.
+ * because we may do nothing,
+ * a if-branch is better than a function-pointer call.
+ */
+ if(input_type) return (*input_type)(buf, count);
+ else return count;
+}
inline static int read_wrapper(int fd, void *buf, size_t count) {
return (*read_type)(fd, buf, count);
@@ -112,10 +125,10 @@ num_in_buf(void)
* be inconsistent. We try to not segfault here...
*/
-static int
+ static int
dogetch(void)
{
- int len;
+ ssize_t len;
static time4_t lastact;
if (ibufsize <= icurrchar) {
@@ -142,7 +155,7 @@ dogetch(void)
STATINC(STAT_SYSSELECT);
while ((len = select(i_newfd + 1, &readfds, NULL, NULL,
- i_top ? &timeout : NULL)) < 0) {
+ i_top ? &timeout : NULL)) < 0) {
if (errno != EINTR)
abort_bbs(0);
/* raise(SIGHUP); */
@@ -166,32 +179,32 @@ dogetch(void)
return I_OTHERDATA;
}
}
+
#ifdef NOKILLWATERBALL
- if( currutmp && currutmp->msgcount && !reentrant_write_request )
- write_request(1);
+ if( currutmp && currutmp->msgcount && !reentrant_write_request )
+ write_request(1);
#endif
-#ifdef SKIP_TELNET_CONTROL_SIGNAL
- do{
-#endif
+ STATINC(STAT_SYSREADSOCKET);
- STATINC(STAT_SYSREADSOCKET);
+ do {
+ len = tty_read(inbuf, IBUFSIZE);
+ /* len = 0: abort, < 1: read more */
#ifdef CONVERT
- while ((len = read_wrapper(0, inbuf, IBUFSIZE)) <= 0) {
-#else
- while ((len = read(0, inbuf, IBUFSIZE)) <= 0) {
-#endif
- if (len == 0 || errno != EINTR)
- abort_bbs(0);
- /* raise(SIGHUP); */
-
+ if(len > 0) {
+ len = input_wrapper(inbuf, len);
+ if(len == 0) len = -1;
}
-#ifdef SKIP_TELNET_CONTROL_SIGNAL
- } while( inbuf[0] == IAC );
#endif
+ } while (len < 0);
+
+ if (len == 0)
+ abort_bbs(0);
+
ibufsize = len;
icurrchar = 0;
}
+
if (currutmp) {
#ifdef OUTTA_TIMER
now = SHM->GV2.e.now;
@@ -207,90 +220,12 @@ dogetch(void)
return (unsigned char)inbuf[icurrchar++];
}
-enum IAC_STATE {
- IAC_NONE,
- IAC_WAIT1,
- IAC_WAIT_NAWS,
- IAC_WAIT_NAWS_IAC,
- IAC_WAIT_IAC_SE_1,
- IAC_WAIT_IAC_SE_2,
- IAC_ERROR
-};
-
static int water_which_flag = 0;
int
igetch(void)
{
register int ch, mode = 0, last = 0;
- static int iac_state = IAC_NONE;
- static unsigned char nawsbuf[4], inaws = 0;
while ((ch = dogetch()) >= 0) {
- if(raw_connection) /* only process IAC in raw connection mode */
- switch (iac_state) {
- case IAC_NONE:
- if(ch == IAC) {
- iac_state = IAC_WAIT1;
- continue;
- }
- break;
- case IAC_WAIT1:
- if(ch == SB)
- iac_state = IAC_WAIT_IAC_SE_1;
- else
- iac_state = IAC_NONE;
- if(ch == AYT) {
- redoscr();
- outmsg("我還活著喔");
- }
- continue;
- case IAC_WAIT_IAC_SE_1:
- if(ch == IAC)
- iac_state = IAC_WAIT_IAC_SE_2;
- if(ch == TELOPT_NAWS) {
- iac_state = IAC_WAIT_NAWS;
- inaws = 0;
- }
- continue;
- case IAC_WAIT_IAC_SE_2:
- if(ch == SE) {
- iac_state = IAC_NONE;
- continue;
- }
- else
- iac_state = IAC_WAIT_IAC_SE_1;
- continue;
- case IAC_WAIT_NAWS_IAC:
- // when we're waiting for NAWS and an IAC comes, orz
- iac_state = IAC_WAIT_NAWS;
- if (ch == IAC)
- nawsbuf[inaws-1] = IAC;
- // else? i don't know how to handle sich situation
- // there shall not be any other cases
- continue;
- case IAC_WAIT_NAWS:
- nawsbuf[inaws++] = (unsigned char)ch;
- if(ch == IAC) {
- iac_state = IAC_WAIT_NAWS_IAC;
- continue;
- }
- if(inaws == 4) {
- int w = (nawsbuf[0] << 8) + nawsbuf[1];
- int h = (nawsbuf[2] << 8) + nawsbuf[3];
- iac_state = IAC_WAIT_IAC_SE_1;
- term_resize(w, h); /* term_resize is safe */
- /* redoscr(); */ /* will not work as we want */
-#ifdef DEBUG
- {
- char buf[256];
- sprintf(buf, "[%dx%d]", w, h);
- outmsg(buf);
- }
-#endif
- iac_state = IAC_WAIT_IAC_SE_1;
- }
- continue;
- } else if (ch == 0) /* in non-raw connection mode, ignore zero. */
- return 0;
if (mode == 0 && ch == KEY_ESC) // here is state machine for 2 bytes key
mode = 1;
@@ -768,4 +703,5 @@ getdata(int line, int col, const char *prompt, char *buf, int len, int echo)
return oldgetdata(line, col, prompt, buf, len, echo);
}
-
+/* vim:sw=4
+ */
diff --git a/mbbsd/mbbsd.c b/mbbsd/mbbsd.c
index e54c0139..6fdba9f8 100644
--- a/mbbsd/mbbsd.c
+++ b/mbbsd/mbbsd.c
@@ -1122,61 +1122,6 @@ start_client(void)
domenu(MMENU, "主功\能表", (currutmp->mailalert ? 'M' : 'C'), cmdlist);
}
-/* FSA (finite state automata) for telnet protocol */
-static void
-telnet_init(void)
-{
- const static char svr[] = {
- IAC, DO, TELOPT_TTYPE,
- IAC, SB, TELOPT_TTYPE, TELQUAL_SEND, IAC, SE,
- IAC, DO, TELOPT_NAWS,
- IAC, WILL, TELOPT_ECHO,
- IAC, WILL, TELOPT_SGA,
- };
- const char *cmd;
- int n, len;
- ssize_t cread = 0;
- struct timeval to;
- unsigned char buf[64];
- fd_set ReadSet, r;
-
- raw_connection = 1;
-
- FD_ZERO(&ReadSet);
- FD_SET(0, &ReadSet);
- for (n = 0, cmd = svr; n < 5; n++) {
- len = (n == 1 ? 6 : 3);
- write(0, cmd, len);
- cmd += len;
- to.tv_sec = 3;
- to.tv_usec = 0;
- r = ReadSet;
- if (select(1, &r, NULL, NULL, &to) > 0)
- cread = recv(0, buf, sizeof(buf), 0);
-
-#define MIN_NAWS (8)
-#define GETB() (buf[bi++] == IAC ? buf[bi++] : buf[bi-1])
- if(cread > MIN_NAWS) { /* 8 = minimal NAWS size */
- int bi = 0;
- while(bi < cread-MIN_NAWS && !(
- buf[bi] == IAC &&
- buf[bi+1] == SB &&
- buf[bi+2] == TELOPT_NAWS &&
- buf[bi+3] == 0)) /* hack: safe term size. */
- bi++;
- if(bi < cread-MIN_NAWS) {
- /* NAWS handler ? */
- int w, h;
- bi += 3;
- w = GETB() << 8; w |= GETB();
- h = GETB() << 8; h |= GETB();
- if(buf[bi++] == IAC && buf[bi++] == SE)
- term_resize(w, h);
- }
- }
- }
-}
-
/* 取得 remote user name 以判定身份 */
/*
* rfc931() speaks a common subset of the RFC 931, AUTH, TAP, IDENT and RFC
@@ -1416,6 +1361,9 @@ 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[])
{
@@ -1601,3 +1549,252 @@ check_ban_and_load(int fd)
return 0;
}
+
+/* ------- piaip's implementation of TELNET protocol ------- */
+
+enum {
+ IAC_NONE,
+ IAC_COMMAND,
+ IAC_WAIT_OPT,
+ IAC_WAIT_SE,
+ IAC_PROCESS_OPT,
+ IAC_ERROR
+} TELNET_IAC_STATES;
+
+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.
+ */
+ 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, WILL, TELOPT_NAWS,
+ IAC, DO, TELOPT_NAWS,
+
+ /* i will echo. */
+ IAC, WILL, TELOPT_ECHO,
+ /* supress ga. */
+ IAC, WILL, TELOPT_SGA,
+ };
+
+ raw_connection = 1;
+ write(0, telnet_init_cmds, sizeof(telnet_init_cmds));
+}
+
+/* tty_read
+ * read from tty, process telnet commands if raw connection.
+ * return: >1 = length, -1 means read more, 0 = abort/EOF.
+ */
+ssize_t
+tty_read(unsigned char *buf, size_t max)
+{
+ ssize_t l = read(0, buf, max);
+
+ if(l < 0 && !(errno == EINTR || errno == EAGAIN))
+ return 0; /* 0 will abort BBS. */
+
+ 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;
+ }
+
+ /* 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:
+ 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.\r\n";
+ write(0, alive, strlen(alive));
+ }
+ 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:
+ 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.
+ *
+ * If client says 'DONT' or DO,
+ * ignore because we can't really do/don't that.
+ */
+ switch(c) {
+ case TELOPT_ECHO: /* echo */
+ case TELOPT_RCP: /* prepare to reconnect */
+ case TELOPT_SGA: /* suppress go ahead */
+ if(iac_opt_req == WONT) {
+ /* we need these options, whether you want or not */
+ unsigned char cmd[3] = { IAC, 0, 0 };
+ cmd[1] = WILL;
+ cmd[2] = c;
+ write(0, cmd, sizeof(cmd));
+ }
+ break;
+
+ case TELOPT_TTYPE:
+ case TELOPT_NAWS: /* resize terminal */
+ /* i don't care the client */
+ break;
+
+ default:
+ if (iac_opt_req == WILL)
+ {
+ /* unknown option, reply with won't */
+ unsigned char cmd[3] = { IAC, 0, 0 };
+ 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;
+ 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);
+ }
+ break;
+
+ default:
+ break;
+ }
+ return 1;
+ }
+ return 1; /* never reached */
+}
+/* vim: sw=4
+ */
diff --git a/sample/pttbbs.conf b/sample/pttbbs.conf
index 410c13d8..3410610c 100644
--- a/sample/pttbbs.conf
+++ b/sample/pttbbs.conf
@@ -114,11 +114,6 @@
/* 若定義, 則在文章列表的時候不同日期會標上不同顏色 */
//#define COLORDATE
-/* 若定義, 則會在 read socket的時候, 則會跳過讀入時第一個 byte 是 -1
- (即 telnet 的 control packet), 可避免循環錯誤.
- 但是就沒有辦法處理 term resize的情況. 不確定這個現在還會不會用到 */
-//#define SKIP_TELNET_CONTROL_SIGNAL
-
/* 若定義, 在使用者註冊之前, 會先顯示出該檔案, 經使用者確認後才能註冊 */
//#define HAVE_USERAGREEMENT "etc/UserAgreement"