1
0
mirror of https://git.tartarus.org/simon/putty.git synced 2025-05-10 06:02:10 -05:00

Yet more trunk merges. Jacob's SSH signals stuff, some docs changes,

and stability stuff in ssh.c.

[originally from svn r4674]
This commit is contained in:
Simon Tatham 2004-10-24 13:31:55 +00:00
parent 5db33359a2
commit f12be80774
8 changed files with 442 additions and 122 deletions

View File

@ -1,4 +1,4 @@
\versionid $Id: config.but,v 1.92.2.2 2004/10/24 13:29:02 simon Exp $ \versionid $Id: config.but,v 1.92.2.3 2004/10/24 13:31:55 simon Exp $
\C{config} Configuring PuTTY \C{config} Configuring PuTTY
@ -1199,11 +1199,22 @@ native keyboard layout is not US or UK.
\cfg{winhelp-topic}{translation.linedraw} \cfg{winhelp-topic}{translation.linedraw}
VT100-series terminals allow the server to send control sequences VT100-series terminals allow the server to send control sequences that
that shift temporarily into a separate character set for drawing shift temporarily into a separate character set for drawing simple
lines and boxes. PuTTY has a variety of ways to support this lines and boxes. However, there are a variety of ways in which PuTTY
capability. In general you should probably try lots of options until can attempt to find appropriate characters, and the right one to use
you find one that your particular font supports. depends on the locally configured font. In general you should probably
try lots of options until you find one that your particular font
supports.
\b \q{Use Unicode line drawing code points} tries to use the box
characters that are present in Unicode. For good Unicode-supporting
fonts this is probably the most reliable and functional option.
\b \q{Poor man's line drawing} assumes that the font \e{cannot}
generate the line and box characters at all, so it will use the
\c{+}, \c{-} and \c{|} characters to draw approximations to boxes.
You should use this option if none of the other options works.
\b \q{Font has XWindows encoding} is for use with fonts that have a \b \q{Font has XWindows encoding} is for use with fonts that have a
special encoding, where the lowest 32 character positions (below the special encoding, where the lowest 32 character positions (below the
@ -1220,15 +1231,6 @@ different size depending on which character set you try to use.
\b \q{Use font in OEM mode only} is more reliable than that, but can \b \q{Use font in OEM mode only} is more reliable than that, but can
miss out other characters from the main character set. miss out other characters from the main character set.
\b \q{Poor man's line drawing} assumes that the font \e{cannot}
generate the line and box characters at all, so it will use the
\c{+}, \c{-} and \c{|} characters to draw approximations to boxes.
You should use this option if none of the other options works.
\b \q{Unicode mode} tries to use the box characters that are present
in Unicode. For good Unicode-supporting fonts this is probably the
most reliable and functional option.
\S{config-linedrawpaste} Controlling copy and paste of line drawing \S{config-linedrawpaste} Controlling copy and paste of line drawing
characters characters
@ -1248,7 +1250,8 @@ layout in another program, for example.
Note that this option only applies to line-drawing characters which Note that this option only applies to line-drawing characters which
\e{were} printed by using the VT100 mechanism. Line-drawing \e{were} printed by using the VT100 mechanism. Line-drawing
characters displayed using Unicode will paste as Unicode always. characters that were received as Unicode code points will paste as
Unicode always.
\H{config-selection} The Selection panel \H{config-selection} The Selection panel

View File

@ -1,4 +1,4 @@
\versionid $Id: using.but,v 1.33.2.1 2004/10/24 13:21:11 simon Exp $ \versionid $Id: using.but,v 1.33.2.2 2004/10/24 13:31:55 simon Exp $
\C{using} Using PuTTY \C{using} Using PuTTY
@ -175,19 +175,27 @@ PuTTY can also be configured to send this when Ctrl-Z is typed; see
In an SSH connection, the following special commands are available: In an SSH connection, the following special commands are available:
\b \I{Break, SSH special command}Break
\lcont{
Optional extension; may not be supported by server. PuTTY requests the
server's default break length.
}
\b \I{IGNORE message, SSH special command}\I{No-op, in SSH}IGNORE message \b \I{IGNORE message, SSH special command}\I{No-op, in SSH}IGNORE message
\lcont{ \lcont{
Should have no effect. Should have no effect.
} }
\b \I{Break, SSH special command}Break
\lcont{
Only available in SSH-2, and only during a session. Optional
extension; may not be supported by server. PuTTY requests the server's
default break length.
}
\b \I{Signal, SSH special command}Signals (SIGINT, SIGTERM etc)
\lcont{
Only available in SSH-2, and only during a session. Sends various
POSIX signals. Not honoured by all servers.
}
\S2{using-newsession} Starting new sessions \S2{using-newsession} Starting new sessions
PuTTY's system menu provides some shortcut ways to start new PuTTY's system menu provides some shortcut ways to start new
@ -278,15 +286,17 @@ See \k{config-logging} for more details and options.
\H{using-translation} Altering your \i{character set} configuration \H{using-translation} Altering your \i{character set} configuration
If you find that special characters (\i{accented characters}, for If you find that special characters (\i{accented characters}, for
example) are not being displayed correctly in your PuTTY session, it example, or \i{line-drawing characters}) are not being displayed
may be that PuTTY is interpreting the characters sent by the server correctly in your PuTTY session, it may be that PuTTY is interpreting
according to the wrong \e{character set}. There are a lot of the characters sent by the server according to the wrong \e{character
different character sets available, so it's entirely possible for set}. There are a lot of different character sets available, so it's
this to happen. entirely possible for this to happen.
If you click \q{Change Settings} and look at the \i{\q{Translation} If you click \q{Change Settings} and look at the \i{\q{Translation}
panel}, you should see a large number of character sets which you panel}, you should see a large number of character sets which you can
can select. Now all you need is to find out which of them you want! select, and other related options. Now all you need is to find out
which of them you want! (See \k{config-translation} for more
information.)
\H{using-x-forwarding} Using \i{X11 forwarding} in SSH \H{using-x-forwarding} Using \i{X11 forwarding} in SSH

14
putty.h
View File

@ -129,13 +129,23 @@ struct unicode_data {
#define LGTYP_PACKETS 3 /* logmode: SSH data packets */ #define LGTYP_PACKETS 3 /* logmode: SSH data packets */
typedef enum { typedef enum {
/* Actual special commands. Originally Telnet, but some codes have
* been re-used for similar specials in other protocols. */
TS_AYT, TS_BRK, TS_SYNCH, TS_EC, TS_EL, TS_GA, TS_NOP, TS_ABORT, TS_AYT, TS_BRK, TS_SYNCH, TS_EC, TS_EL, TS_GA, TS_NOP, TS_ABORT,
TS_AO, TS_IP, TS_SUSP, TS_EOR, TS_EOF, TS_LECHO, TS_RECHO, TS_PING, TS_AO, TS_IP, TS_SUSP, TS_EOR, TS_EOF, TS_LECHO, TS_RECHO, TS_PING,
TS_EOL TS_EOL,
/* POSIX-style signals. (not Telnet) */
TS_SIGABRT, TS_SIGALRM, TS_SIGFPE, TS_SIGHUP, TS_SIGILL,
TS_SIGINT, TS_SIGKILL, TS_SIGPIPE, TS_SIGQUIT, TS_SIGSEGV,
TS_SIGTERM, TS_SIGUSR1, TS_SIGUSR2,
/* Pseudo-specials used for constructing the specials menu. */
TS_SEP, /* Separator */
TS_SUBMENU, /* Start a new submenu with specified name */
TS_EXITMENU /* Exit current submenu or end of specials */
} Telnet_Special; } Telnet_Special;
struct telnet_special { struct telnet_special {
const char *name; /* NULL==end, ""==separator */ const char *name;
int code; int code;
}; };

223
ssh.c
View File

@ -460,7 +460,7 @@ struct ssh_channel {
struct ssh_agent_channel { struct ssh_agent_channel {
unsigned char *message; unsigned char *message;
unsigned char msglen[4]; unsigned char msglen[4];
int lensofar, totallen; unsigned lensofar, totallen;
} a; } a;
struct ssh_x11_channel { struct ssh_x11_channel {
Socket s; Socket s;
@ -524,6 +524,8 @@ static void ssh_throttle_all(Ssh ssh, int enable, int bufsize);
static void ssh2_set_window(struct ssh_channel *c, unsigned newwin); static void ssh2_set_window(struct ssh_channel *c, unsigned newwin);
static int ssh_sendbuffer(void *handle); static int ssh_sendbuffer(void *handle);
static void ssh_do_close(Ssh ssh); static void ssh_do_close(Ssh ssh);
static unsigned long ssh_pkt_getuint32(Ssh ssh);
static void ssh_pkt_getstring(Ssh ssh, char **p, int *length);
struct rdpkt1_state_tag { struct rdpkt1_state_tag {
long len, pad, biglen, to_read; long len, pad, biglen, to_read;
@ -972,15 +974,14 @@ static int ssh1_rdpkt(Ssh ssh, unsigned char **data, int *datalen)
} }
if (ssh->pktin.type == SSH1_MSG_DEBUG) { if (ssh->pktin.type == SSH1_MSG_DEBUG) {
/* log debug message */ char *buf, *msg;
char buf[512]; int msglen;
int stringlen = GET_32BIT(ssh->pktin.body);
strcpy(buf, "Remote debug message: "); ssh_pkt_getstring(ssh, &msg, &msglen);
if (stringlen > 480) buf = dupprintf("Remote debug message: %.*s", msglen, msg);
stringlen = 480;
memcpy(buf + 8, ssh->pktin.body + 4, stringlen);
buf[8 + stringlen] = '\0';
logevent(buf); logevent(buf);
sfree(buf);
goto next_packet; goto next_packet;
} else if (ssh->pktin.type == SSH1_MSG_IGNORE) { } else if (ssh->pktin.type == SSH1_MSG_IGNORE) {
/* do nothing */ /* do nothing */
@ -989,17 +990,12 @@ static int ssh1_rdpkt(Ssh ssh, unsigned char **data, int *datalen)
if (ssh->pktin.type == SSH1_MSG_DISCONNECT) { if (ssh->pktin.type == SSH1_MSG_DISCONNECT) {
/* log reason code in disconnect message */ /* log reason code in disconnect message */
char buf[256]; char *msg;
unsigned msglen = GET_32BIT(ssh->pktin.body); int msglen;
unsigned nowlen;
strcpy(buf, "Remote sent disconnect: "); ssh_pkt_getstring(ssh, &msg, &msglen);
nowlen = strlen(buf);
if (msglen > sizeof(buf) - nowlen - 1) bombout(("Server sent disconnect message:\n\"%.*s\"", msglen, msg));
msglen = sizeof(buf) - nowlen - 1;
memcpy(buf + nowlen, ssh->pktin.body + 4, msglen);
buf[nowlen + msglen] = '\0';
/* logevent(buf); (this is now done within the bombout macro) */
bombout(("Server sent disconnect message:\n\"%s\"", buf+nowlen));
crStop(0); crStop(0);
} }
@ -1168,10 +1164,11 @@ static int ssh2_rdpkt(Ssh ssh, unsigned char **data, int *datalen)
case SSH2_MSG_DISCONNECT: case SSH2_MSG_DISCONNECT:
{ {
/* log reason code in disconnect message */ /* log reason code in disconnect message */
char *buf; char *buf, *msg;
int nowlen; int nowlen, reason, msglen;
int reason = GET_32BIT(ssh->pktin.data + 6);
unsigned msglen = GET_32BIT(ssh->pktin.data + 10); reason = ssh_pkt_getuint32(ssh);
ssh_pkt_getstring(ssh, &msg, &msglen);
if (reason > 0 && reason < lenof(ssh2_disconnect_reasons)) { if (reason > 0 && reason < lenof(ssh2_disconnect_reasons)) {
buf = dupprintf("Received disconnect message (%s)", buf = dupprintf("Received disconnect message (%s)",
@ -1183,7 +1180,7 @@ static int ssh2_rdpkt(Ssh ssh, unsigned char **data, int *datalen)
logevent(buf); logevent(buf);
sfree(buf); sfree(buf);
buf = dupprintf("Disconnection message text: %n%.*s", buf = dupprintf("Disconnection message text: %n%.*s",
&nowlen, msglen, ssh->pktin.data + 14); &nowlen, msglen, msg);
logevent(buf); logevent(buf);
bombout(("Server sent disconnect message\ntype %d (%s):\n\"%s\"", bombout(("Server sent disconnect message\ntype %d (%s):\n\"%s\"",
reason, reason,
@ -1199,19 +1196,16 @@ static int ssh2_rdpkt(Ssh ssh, unsigned char **data, int *datalen)
case SSH2_MSG_DEBUG: case SSH2_MSG_DEBUG:
{ {
/* log the debug message */ /* log the debug message */
char buf[512]; char *buf, *msg;
/* int display = ssh->pktin.body[6]; */ int msglen;
int stringlen = GET_32BIT(ssh->pktin.data+7);
int prefix; ssh_pkt_getstring(ssh, &msg, &msglen);
strcpy(buf, "Remote debug message: ");
prefix = strlen(buf); buf = dupprintf("Remote debug message: %.*s", msglen, msg);
if (stringlen > (int)(sizeof(buf)-prefix-1))
stringlen = sizeof(buf)-prefix-1;
memcpy(buf + prefix, ssh->pktin.data + 11, stringlen);
buf[prefix + stringlen] = '\0';
logevent(buf); logevent(buf);
sfree(buf);
} }
goto next_packet; /* FIXME: print the debug message */ goto next_packet;
/* /*
* These packets we need do nothing about here. * These packets we need do nothing about here.
@ -5254,7 +5248,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, int ispkt)
ssh2_pkt_addstring_data(ssh, (char *)pub_blob, ssh2_pkt_addstring_data(ssh, (char *)pub_blob,
pub_blob_len); pub_blob_len);
ssh2_pkt_send(ssh); ssh2_pkt_send(ssh);
logevent("Offered public key"); /* FIXME */ logevent("Offered public key");
crWaitUntilV(ispkt); crWaitUntilV(ispkt);
if (ssh->pktin.type != SSH2_MSG_USERAUTH_PK_OK) { if (ssh->pktin.type != SSH2_MSG_USERAUTH_PK_OK) {
@ -6354,6 +6348,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, int ispkt)
unsigned localid; unsigned localid;
char *type; char *type;
int typelen, want_reply; int typelen, want_reply;
int reply = SSH2_MSG_CHANNEL_FAILURE; /* default */
struct ssh_channel *c; struct ssh_channel *c;
localid = ssh_pkt_getuint32(ssh); localid = ssh_pkt_getuint32(ssh);
@ -6385,18 +6380,94 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, int ispkt)
* the request type string to see if it's something * the request type string to see if it's something
* we recognise. * we recognise.
*/ */
if (typelen == 11 && !memcmp(type, "exit-status", 11) && if (c == ssh->mainchan) {
c == ssh->mainchan) { /*
/* We recognise "exit-status" on the primary channel. */ * We recognise "exit-status" and "exit-signal" on
char buf[100]; * the primary channel.
*/
if (typelen == 11 &&
!memcmp(type, "exit-status", 11)) {
ssh->exitcode = ssh_pkt_getuint32(ssh); ssh->exitcode = ssh_pkt_getuint32(ssh);
sprintf(buf, "Server sent command exit status %d", logeventf(ssh, "Server sent command exit status %d",
ssh->exitcode); ssh->exitcode);
logevent(buf); reply = SSH2_MSG_CHANNEL_SUCCESS;
if (want_reply) {
ssh2_pkt_init(ssh, SSH2_MSG_CHANNEL_SUCCESS); } else if (typelen == 11 &&
ssh2_pkt_adduint32(ssh, c->remoteid); !memcmp(type, "exit-signal", 11)) {
ssh2_pkt_send(ssh);
int is_plausible = TRUE, is_int = FALSE;
char *fmt_sig = "", *fmt_msg = "";
char *msg;
int msglen = 0, core = FALSE;
/* ICK: older versions of OpenSSH (e.g. 3.4p1)
* provide an `int' for the signal, despite its
* having been a `string' in the drafts since at
* least 2001. (Fixed in session.c 1.147.) Try to
* infer which we can safely parse it as. */
{
unsigned char *p = ssh->pktin.body +
ssh->pktin.savedpos;
long len = ssh->pktin.length - ssh->pktin.savedpos;
unsigned long num = GET_32BIT(p); /* what is it? */
/* If it's 0, it hardly matters; assume string */
if (num == 0) {
is_int = FALSE;
} else {
int maybe_int = FALSE, maybe_str = FALSE;
#define CHECK_HYPOTHESIS(offset, result) \
do { \
long q = offset; \
if (q >= 0 && q+4 <= len) { \
q = q + 4 + GET_32BIT(p+q); \
if (q >= 0 && q+4 <= len && \
(q = q + 4 + GET_32BIT(p+q)) && q == len) \
result = TRUE; \
} \
} while(0)
CHECK_HYPOTHESIS(4+1, maybe_int);
CHECK_HYPOTHESIS(4+num+1, maybe_str);
#undef CHECK_HYPOTHESIS
if (maybe_int && !maybe_str)
is_int = TRUE;
else if (!maybe_int && maybe_str)
is_int = FALSE;
else
/* Crikey. Either or neither. Panic. */
is_plausible = FALSE;
}
}
if (is_plausible) {
if (is_int) {
/* Old non-standard OpenSSH. */
int signum = ssh_pkt_getuint32(ssh);
fmt_sig = dupprintf(" %d", signum);
} else {
/* As per the drafts. */
char *sig;
int siglen;
ssh_pkt_getstring(ssh, &sig, &siglen);
/* Signal name isn't supposed to be blank, but
* let's cope gracefully if it is. */
if (siglen) {
fmt_sig = dupprintf(" \"%.*s\"",
siglen, sig);
}
}
core = ssh2_pkt_getbool(ssh);
ssh_pkt_getstring(ssh, &msg, &msglen);
if (msglen) {
fmt_msg = dupprintf(" (\"%.*s\")", msglen, msg);
}
/* ignore lang tag */
} /* else don't attempt to parse */
logeventf(ssh, "Server exited on signal%s%s%s",
fmt_sig, core ? " (core dumped)" : "",
fmt_msg);
if (*fmt_sig) sfree(fmt_sig);
if (*fmt_msg) sfree(fmt_msg);
reply = SSH2_MSG_CHANNEL_SUCCESS;
} }
} else { } else {
/* /*
@ -6405,12 +6476,13 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, int ispkt)
* or respond with CHANNEL_FAILURE, depending * or respond with CHANNEL_FAILURE, depending
* on want_reply. * on want_reply.
*/ */
reply = SSH2_MSG_CHANNEL_FAILURE;
}
if (want_reply) { if (want_reply) {
ssh2_pkt_init(ssh, SSH2_MSG_CHANNEL_FAILURE); ssh2_pkt_init(ssh, reply);
ssh2_pkt_adduint32(ssh, c->remoteid); ssh2_pkt_adduint32(ssh, c->remoteid);
ssh2_pkt_send(ssh); ssh2_pkt_send(ssh);
} }
}
} else if (ssh->pktin.type == SSH2_MSG_GLOBAL_REQUEST) { } else if (ssh->pktin.type == SSH2_MSG_GLOBAL_REQUEST) {
char *type; char *type;
int typelen, want_reply; int typelen, want_reply;
@ -6885,12 +6957,25 @@ static const struct telnet_special *ssh_get_specials(void *handle)
{"IGNORE message", TS_NOP}, {"IGNORE message", TS_NOP},
}; };
static const struct telnet_special ssh2_session_specials[] = { static const struct telnet_special ssh2_session_specials[] = {
{"", 0}, {NULL, TS_SEP},
{"Break", TS_BRK} {"Break", TS_BRK},
/* XXX we should also support signals */ /* These are the signal names defined by draft-ietf-secsh-connect-19.
* They include all the ISO C signals, but are a subset of the POSIX
* required signals. */
{"SIGINT (Interrupt)", TS_SIGINT},
{"SIGTERM (Terminate)", TS_SIGTERM},
{"SIGKILL (Kill)", TS_SIGKILL},
{"SIGQUIT (Quit)", TS_SIGQUIT},
{"SIGHUP (Hangup)", TS_SIGHUP},
{"More signals", TS_SUBMENU},
{"SIGABRT", TS_SIGABRT}, {"SIGALRM", TS_SIGALRM},
{"SIGFPE", TS_SIGFPE}, {"SIGILL", TS_SIGILL},
{"SIGPIPE", TS_SIGPIPE}, {"SIGSEGV", TS_SIGSEGV},
{"SIGUSR1", TS_SIGUSR1}, {"SIGUSR2", TS_SIGUSR2},
{NULL, TS_EXITMENU}
}; };
static const struct telnet_special specials_end[] = { static const struct telnet_special specials_end[] = {
{NULL, 0} {NULL, TS_EXITMENU}
}; };
static struct telnet_special ssh_specials[lenof(ignore_special) + static struct telnet_special ssh_specials[lenof(ignore_special) +
lenof(ssh2_session_specials) + lenof(ssh2_session_specials) +
@ -6978,7 +7063,37 @@ static void ssh_special(void *handle, Telnet_Special code)
ssh2_pkt_send(ssh); ssh2_pkt_send(ssh);
} }
} else { } else {
/* do nothing */ /* Is is a POSIX signal? */
char *signame = NULL;
if (code == TS_SIGABRT) signame = "ABRT";
if (code == TS_SIGALRM) signame = "ALRM";
if (code == TS_SIGFPE) signame = "FPE";
if (code == TS_SIGHUP) signame = "HUP";
if (code == TS_SIGILL) signame = "ILL";
if (code == TS_SIGINT) signame = "INT";
if (code == TS_SIGKILL) signame = "KILL";
if (code == TS_SIGPIPE) signame = "PIPE";
if (code == TS_SIGQUIT) signame = "QUIT";
if (code == TS_SIGSEGV) signame = "SEGV";
if (code == TS_SIGTERM) signame = "TERM";
if (code == TS_SIGUSR1) signame = "USR1";
if (code == TS_SIGUSR2) signame = "USR2";
/* The SSH-2 protocol does in principle support arbitrary named
* signals, including signame@domain, but we don't support those. */
if (signame) {
/* It's a signal. */
if (ssh->version == 2 && ssh->mainchan) {
ssh2_pkt_init(ssh, SSH2_MSG_CHANNEL_REQUEST);
ssh2_pkt_adduint32(ssh, ssh->mainchan->remoteid);
ssh2_pkt_addstring(ssh, "signal");
ssh2_pkt_addbool(ssh, 0);
ssh2_pkt_addstring(ssh, signame);
ssh2_pkt_send(ssh);
logeventf(ssh, "Sent signal SIG%s", signame);
}
} else {
/* Never heard of it. Do nothing */
}
} }
} }

134
sshzlib.c
View File

@ -40,7 +40,25 @@
#include <stdlib.h> #include <stdlib.h>
#include <assert.h> #include <assert.h>
#ifdef ZLIB_STANDALONE
/*
* This module also makes a handy zlib decoding tool for when
* you're picking apart Zip files or PDFs or PNGs. If you compile
* it with ZLIB_STANDALONE defined, it builds on its own and
* becomes a command-line utility.
*
* Therefore, here I provide a self-contained implementation of the
* macros required from the rest of the PuTTY sources.
*/
#define snew(type) ( (type *) malloc(sizeof(type)) )
#define snewn(n, type) ( (type *) malloc((n) * sizeof(type)) )
#define sresize(x, n, type) ( (type *) realloc((x), (n) * sizeof(type)) )
#define sfree(x) ( free((x)) )
#else
#include "ssh.h" #include "ssh.h"
#endif
#ifndef FALSE #ifndef FALSE
#define FALSE 0 #define FALSE 0
@ -1027,13 +1045,14 @@ int zlib_decompress_block(void *handle, unsigned char *block, int len,
{ {
struct zlib_decompress_ctx *dctx = (struct zlib_decompress_ctx *)handle; struct zlib_decompress_ctx *dctx = (struct zlib_decompress_ctx *)handle;
const coderecord *rec; const coderecord *rec;
int code, blktype, rep, dist, nlen; int code, blktype, rep, dist, nlen, header;
static const unsigned char lenlenmap[] = { static const unsigned char lenlenmap[] = {
16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15
}; };
dctx->outblk = NULL; dctx->outblk = snewn(256, unsigned char);
dctx->outsize = dctx->outlen = 0; dctx->outsize = 256;
dctx->outlen = 0;
while (len > 0 || dctx->nbits > 0) { while (len > 0 || dctx->nbits > 0) {
while (dctx->nbits < 24 && len > 0) { while (dctx->nbits < 24 && len > 0) {
@ -1043,10 +1062,35 @@ int zlib_decompress_block(void *handle, unsigned char *block, int len,
} }
switch (dctx->state) { switch (dctx->state) {
case START: case START:
/* Expect 16-bit zlib header, which we'll dishonourably ignore. */ /* Expect 16-bit zlib header. */
if (dctx->nbits < 16) if (dctx->nbits < 16)
goto finished; /* done all we can */ goto finished; /* done all we can */
/*
* The header is stored as a big-endian 16-bit integer,
* in contrast to the general little-endian policy in
* the rest of the format :-(
*/
header = (((dctx->bits & 0xFF00) >> 8) |
((dctx->bits & 0x00FF) << 8));
EATBITS(16); EATBITS(16);
/*
* Check the header:
*
* - bits 8-11 should be 1000 (Deflate/RFC1951)
* - bits 12-15 should be at most 0111 (window size)
* - bit 5 should be zero (no dictionary present)
* - we don't care about bits 6-7 (compression rate)
* - bits 0-4 should be set up to make the whole thing
* a multiple of 31 (checksum).
*/
if ((header & 0x0F00) != 0x0800 ||
(header & 0xF000) > 0x7000 ||
(header & 0x0020) != 0x0000 ||
(header % 31) != 0)
goto decode_error;
dctx->state = OUTSIDEBLK; dctx->state = OUTSIDEBLK;
break; break;
case OUTSIDEBLK: case OUTSIDEBLK:
@ -1242,6 +1286,86 @@ int zlib_decompress_block(void *handle, unsigned char *block, int len,
return 0; return 0;
} }
#ifdef ZLIB_STANDALONE
#include <stdio.h>
#include <string.h>
int main(int argc, char **argv)
{
unsigned char buf[16], *outbuf;
int ret, outlen;
void *handle;
int noheader = FALSE, opts = TRUE;
char *filename = NULL;
FILE *fp;
while (--argc) {
char *p = *++argv;
if (p[0] == '-' && opts) {
if (!strcmp(p, "-d"))
noheader = TRUE;
else if (!strcmp(p, "--"))
opts = FALSE; /* next thing is filename */
else {
fprintf(stderr, "unknown command line option '%s'\n", p);
return 1;
}
} else if (!filename) {
filename = p;
} else {
fprintf(stderr, "can only handle one filename\n");
return 1;
}
}
handle = zlib_decompress_init();
if (noheader) {
/*
* Provide missing zlib header if -d was specified.
*/
zlib_decompress_block(handle, "\x78\x9C", 2, &outbuf, &outlen);
assert(outlen == 0);
}
if (filename)
fp = fopen(filename, "rb");
else
fp = stdin;
if (!fp) {
assert(filename);
fprintf(stderr, "unable to open '%s'\n", filename);
return 1;
}
while (1) {
ret = fread(buf, 1, sizeof(buf), fp);
if (ret <= 0)
break;
zlib_decompress_block(handle, buf, ret, &outbuf, &outlen);
if (outbuf) {
if (outlen)
fwrite(outbuf, 1, outlen, stdout);
sfree(outbuf);
} else {
fprintf(stderr, "decoding error\n");
return 1;
}
}
zlib_decompress_cleanup(handle);
if (filename)
fclose(fp);
return 0;
}
#else
const struct ssh_compress ssh_zlib = { const struct ssh_compress ssh_zlib = {
"zlib", "zlib",
zlib_compress_init, zlib_compress_init,
@ -1253,3 +1377,5 @@ const struct ssh_compress ssh_zlib = {
zlib_disable_compression, zlib_disable_compression,
"zlib (RFC1950)" "zlib (RFC1950)"
}; };
#endif

View File

@ -963,6 +963,8 @@ static void telnet_special(void *handle, Telnet_Special code)
telnet->bufsize = sk_write(telnet->s, (char *)b, 2); telnet->bufsize = sk_write(telnet->s, (char *)b, 2);
} }
break; break;
default:
break; /* never heard of it */
} }
} }
@ -976,15 +978,15 @@ static const struct telnet_special *telnet_get_specials(void *handle)
{"Erase Line", TS_EL}, {"Erase Line", TS_EL},
{"Go Ahead", TS_GA}, {"Go Ahead", TS_GA},
{"No Operation", TS_NOP}, {"No Operation", TS_NOP},
{"", 0}, {NULL, TS_SEP},
{"Abort Process", TS_ABORT}, {"Abort Process", TS_ABORT},
{"Abort Output", TS_AO}, {"Abort Output", TS_AO},
{"Interrupt Process", TS_IP}, {"Interrupt Process", TS_IP},
{"Suspend Process", TS_SUSP}, {"Suspend Process", TS_SUSP},
{"", 0}, {NULL, TS_SEP},
{"End Of Record", TS_EOR}, {"End Of Record", TS_EOR},
{"End Of File", TS_EOF}, {"End Of File", TS_EOF},
{NULL, 0} {NULL, TS_EXITMENU}
}; };
return specials; return specials;
} }

View File

@ -3144,23 +3144,52 @@ void update_specials_menu(void *frontend)
else else
specials = NULL; specials = NULL;
/* I believe this disposes of submenus too. */
gtk_container_foreach(GTK_CONTAINER(inst->specialsmenu), gtk_container_foreach(GTK_CONTAINER(inst->specialsmenu),
(GtkCallback)gtk_widget_destroy, NULL); (GtkCallback)gtk_widget_destroy, NULL);
if (specials) { if (specials) {
int i; int i;
GtkWidget *menuitem; GtkWidget *menu = inst->specialsmenu;
for (i = 0; specials[i].name; i++) { /* A lame "stack" for submenus that will do for now. */
if (*specials[i].name) { GtkWidget *saved_menu = NULL;
int nesting = 1;
for (i = 0; nesting > 0; i++) {
GtkWidget *menuitem = NULL;
switch (specials[i].code) {
case TS_SUBMENU:
assert (nesting < 2);
saved_menu = menu; /* XXX lame stacking */
menu = gtk_menu_new();
menuitem = gtk_menu_item_new_with_label(specials[i].name);
gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), menu);
gtk_container_add(GTK_CONTAINER(saved_menu), menuitem);
gtk_widget_show(menuitem);
menuitem = NULL;
nesting++;
break;
case TS_EXITMENU:
nesting--;
if (nesting) {
menu = saved_menu; /* XXX lame stacking */
saved_menu = NULL;
}
break;
case TS_SEP:
menuitem = gtk_menu_item_new();
break;
default:
menuitem = gtk_menu_item_new_with_label(specials[i].name); menuitem = gtk_menu_item_new_with_label(specials[i].name);
gtk_object_set_data(GTK_OBJECT(menuitem), "user-data", gtk_object_set_data(GTK_OBJECT(menuitem), "user-data",
GINT_TO_POINTER(specials[i].code)); GINT_TO_POINTER(specials[i].code));
gtk_signal_connect(GTK_OBJECT(menuitem), "activate", gtk_signal_connect(GTK_OBJECT(menuitem), "activate",
GTK_SIGNAL_FUNC(special_menuitem), inst); GTK_SIGNAL_FUNC(special_menuitem), inst);
} else break;
menuitem = gtk_menu_item_new(); }
gtk_container_add(GTK_CONTAINER(inst->specialsmenu), menuitem); if (menuitem) {
gtk_container_add(GTK_CONTAINER(menu), menuitem);
gtk_widget_show(menuitem); gtk_widget_show(menuitem);
} }
}
gtk_widget_show(inst->specialsitem1); gtk_widget_show(inst->specialsitem1);
gtk_widget_show(inst->specialsitem2); gtk_widget_show(inst->specialsitem2);
} else { } else {

View File

@ -111,6 +111,7 @@ static struct unicode_data ucsdata;
static int session_closed; static int session_closed;
static const struct telnet_special *specials; static const struct telnet_special *specials;
static int n_specials;
static struct { static struct {
HMENU menu; HMENU menu;
@ -915,20 +916,48 @@ void update_specials_menu(void *frontend)
specials = NULL; specials = NULL;
if (specials) { if (specials) {
p = CreateMenu(); /* We can't use Windows to provide a stack for submenus, so
for (i = 0; specials[i].name; i++) { * here's a lame "stack" that will do for now. */
HMENU saved_menu = NULL;
int nesting = 1;
p = CreatePopupMenu();
for (i = 0; nesting > 0; i++) {
assert(IDM_SPECIAL_MIN + 0x10 * i < IDM_SPECIAL_MAX); assert(IDM_SPECIAL_MIN + 0x10 * i < IDM_SPECIAL_MAX);
if (*specials[i].name) switch (specials[i].code) {
case TS_SEP:
AppendMenu(p, MF_SEPARATOR, 0, 0);
break;
case TS_SUBMENU:
assert(nesting < 2);
nesting++;
saved_menu = p; /* XXX lame stacking */
p = CreatePopupMenu();
AppendMenu(saved_menu, MF_POPUP | MF_ENABLED,
(UINT) p, specials[i].name);
break;
case TS_EXITMENU:
nesting--;
if (nesting) {
p = saved_menu; /* XXX lame stacking */
saved_menu = NULL;
}
break;
default:
AppendMenu(p, MF_ENABLED, IDM_SPECIAL_MIN + 0x10 * i, AppendMenu(p, MF_ENABLED, IDM_SPECIAL_MIN + 0x10 * i,
specials[i].name); specials[i].name);
else break;
AppendMenu(p, MF_SEPARATOR, 0, 0);
} }
} else }
/* Squirrel the highest special. */
n_specials = i - 1;
} else {
p = NULL; p = NULL;
n_specials = 0;
}
for (j = 0; j < lenof(popup_menus); j++) { for (j = 0; j < lenof(popup_menus); j++) {
if (menu_already_exists) { if (menu_already_exists) {
/* XXX does this free up all submenus? */
DeleteMenu(popup_menus[j].menu, DeleteMenu(popup_menus[j].menu,
popup_menus[j].specials_submenu_pos, popup_menus[j].specials_submenu_pos,
MF_BYPOSITION); MF_BYPOSITION);
@ -2087,22 +2116,18 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
} }
if (wParam >= IDM_SPECIAL_MIN && wParam <= IDM_SPECIAL_MAX) { if (wParam >= IDM_SPECIAL_MIN && wParam <= IDM_SPECIAL_MAX) {
int i = (wParam - IDM_SPECIAL_MIN) / 0x10; int i = (wParam - IDM_SPECIAL_MIN) / 0x10;
int j;
/* /*
* Ensure we haven't been sent a bogus SYSCOMMAND * Ensure we haven't been sent a bogus SYSCOMMAND
* which would cause us to reference invalid memory * which would cause us to reference invalid memory
* and crash. Perhaps I'm just too paranoid here. * and crash. Perhaps I'm just too paranoid here.
*/ */
for (j = 0; j < i; j++) if (i >= n_specials)
if (!specials || !specials[j].name)
break; break;
if (j == i) {
if (back) if (back)
back->special(backhandle, specials[i].code); back->special(backhandle, specials[i].code);
net_pending_errors(); net_pending_errors();
} }
} }
}
break; break;
#define X_POS(l) ((int)(short)LOWORD(l)) #define X_POS(l) ((int)(short)LOWORD(l))