From 9de6e7ba1083861d35e04f84e8051634958b3d04 Mon Sep 17 00:00:00 2001 From: kris Date: Mon, 23 Jul 2001 21:52:26 +0000 Subject: [PATCH] output_data(), output_datalen() and netflush() didn't actually guarantee to do what they are supposed to: under some circumstances output data would be truncated, or the buffer would not actually be flushed (possibly leading to overflows when the caller assumes the operation succeeded). Change the semantics so that these functions ensure they complete the operation before returning. Comment out diagnostic code enabled by '-D reports' which causes an infinite recursion and an eventual crash. Patch developed with assistance from ru and assar. git-svn-id: http://svn0.us-east.freebsd.org/base/head/contrib/telnet@80224 ccf9f872-aa2e-dd11-9fc8-001c23d0bc1f --- telnetd/ext.h | 2 +- telnetd/slc.c | 1 - telnetd/state.c | 58 +++++++++++++++++++++++++--------------------- telnetd/telnetd.c | 1 - telnetd/termstat.c | 2 -- telnetd/utility.c | 39 +++++++++++++++++-------------- 6 files changed, 54 insertions(+), 49 deletions(-) diff --git a/telnetd/ext.h b/telnetd/ext.h index 74dd985..0744db1 100644 --- a/telnetd/ext.h +++ b/telnetd/ext.h @@ -190,7 +190,7 @@ extern void wontoption P((int)); int output_data __P((const char *, ...)) __printflike(1, 2); -int output_datalen __P((const char *, size_t)); +void output_datalen __P((const char *, int)); #ifdef ENCRYPTION extern void (*encrypt_output) P((unsigned char *, int)); diff --git a/telnetd/slc.c b/telnetd/slc.c index 01c4258..780adf1 100644 --- a/telnetd/slc.c +++ b/telnetd/slc.c @@ -176,7 +176,6 @@ end_slc(bufp) register unsigned char **bufp; { register int len; - void netflush(); /* * If a change has occured, store the new terminal control diff --git a/telnetd/state.c b/telnetd/state.c index 1b9372c..f5b1299 100644 --- a/telnetd/state.c +++ b/telnetd/state.c @@ -1615,40 +1615,46 @@ send_status() /* * This function appends data to nfrontp and advances nfrontp. + * Returns the number of characters written altogether (the + * buffer may have been flushed in the process). */ int output_data(const char *format, ...) { va_list args; - size_t remaining, ret; + int len; + char *buf; va_start(args, format); - remaining = BUFSIZ - (nfrontp - netobuf); - /* try a netflush() if the room is too low */ - if (strlen(format) > remaining || BUFSIZ / 4 > remaining) { - netflush(); - remaining = BUFSIZ - (nfrontp - netobuf); - } - ret = vsnprintf(nfrontp, remaining, format, args); - nfrontp += (ret < remaining) ? ret : remaining; + if ((len = vasprintf(&buf, format, args)) == -1) + return -1; + output_datalen(buf, len); va_end(args); - return ret; -} - -int -output_datalen(const char *buf, size_t len) -{ - size_t remaining; - - remaining = BUFSIZ - (nfrontp - netobuf); - if (remaining < len) { - netflush(); - remaining = BUFSIZ - (nfrontp - netobuf); - if (remaining < len) - return -1; - } - memmove(nfrontp, buf, len); - nfrontp += len; + free(buf); return (len); } + +void +output_datalen(const char *buf, int len) +{ + int remaining, copied; + + remaining = BUFSIZ - (nfrontp - netobuf); + while (len > 0) { + /* Free up enough space if the room is too low*/ + if ((len > BUFSIZ ? BUFSIZ : len) > remaining) { + netflush(); + remaining = BUFSIZ - (nfrontp - netobuf); + } + + /* Copy out as much as will fit */ + copied = remaining > len ? len : remaining; + memmove(nfrontp, buf, copied); + nfrontp += copied; + len -= copied; + remaining -= copied; + buf += copied; + } + return; +} diff --git a/telnetd/telnetd.c b/telnetd/telnetd.c index 60353d6..7ae4568 100644 --- a/telnetd/telnetd.c +++ b/telnetd/telnetd.c @@ -952,7 +952,6 @@ telnet(f, p, host) char *HE; char *HN; char *IM; - void netflush(); int nfd; /* diff --git a/telnetd/termstat.c b/telnetd/termstat.c index 30230d4..a24135b 100644 --- a/telnetd/termstat.c +++ b/telnetd/termstat.c @@ -140,7 +140,6 @@ int newmap = 1; /* nonzero if \n maps to ^M^J */ void localstat() { - void netflush(); int need_will_echo = 0; #if defined(CRAY2) && defined(UNICOS5) @@ -404,7 +403,6 @@ flowstat() clientstat(code, parm1, parm2) register int code, parm1, parm2; { - void netflush(); /* * Get a copy of terminal characteristics. diff --git a/telnetd/utility.c b/telnetd/utility.c index d59657f..48eac65 100644 --- a/telnetd/utility.c +++ b/telnetd/utility.c @@ -69,10 +69,9 @@ static const char rcsid[] = void ttloop() { - void netflush(); DIAG(TD_REPORT, output_data("td: ttloop\r\n")); - if (nfrontp-nbackp) { + if (nfrontp - nbackp > 0) { netflush(); } ncc = read(net, netibuf, sizeof netibuf); @@ -257,10 +256,13 @@ netflush() int n; extern int not42; - if ((n = nfrontp - nbackp) > 0) { + while ((n = nfrontp - nbackp) > 0) { +#if 0 + /* XXX This causes output_data() to recurse and die */ DIAG(TD_REPORT, { n += output_data("td: netflush %d chars\r\n", n); }); +#endif #ifdef ENCRYPTION if (encrypt_output) { char *s = nclearto ? nclearto : nbackp; @@ -293,25 +295,26 @@ netflush() n = send(net, nbackp, n, MSG_OOB); /* URGENT data */ } } - } - if (n < 0) { - if (errno == EWOULDBLOCK || errno == EINTR) - return; - cleanup(0); - } - nbackp += n; + if (n == -1) { + if (errno == EWOULDBLOCK || errno == EINTR) + continue; + cleanup(0); + /* NOTREACHED */ + } + nbackp += n; #ifdef ENCRYPTION - if (nbackp > nclearto) - nclearto = 0; + if (nbackp > nclearto) + nclearto = 0; #endif /* ENCRYPTION */ - if (nbackp >= neturg) { - neturg = 0; - } - if (nbackp == nfrontp) { - nbackp = nfrontp = netobuf; + if (nbackp >= neturg) { + neturg = 0; + } + if (nbackp == nfrontp) { + nbackp = nfrontp = netobuf; #ifdef ENCRYPTION - nclearto = 0; + nclearto = 0; #endif /* ENCRYPTION */ + } } return; } /* end of netflush */