From b3a5cdc5fbab8884755690286211fa6b3425d9d3 Mon Sep 17 00:00:00 2001 From: Simon Tatham Date: Fri, 26 Jan 2007 14:06:08 +0000 Subject: [PATCH 001/124] Kai Jourdan spotted a rather embarrassing double-free, and Minefield confirms that it's a real problem. [originally from svn r7168] --- windows/window.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/windows/window.c b/windows/window.c index dca27a44..477e6333 100644 --- a/windows/window.c +++ b/windows/window.c @@ -816,9 +816,8 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) sfree(handles); if (must_close_session) close_session(); - } - - sfree(handles); + } else + sfree(handles); while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { if (msg.message == WM_QUIT) From aa67efd0fab280dff74dac684313f17844838e59 Mon Sep 17 00:00:00 2001 From: Simon Tatham Date: Fri, 26 Jan 2007 14:11:56 +0000 Subject: [PATCH 002/124] If I'd tested under Minefield before releasing, r7168 would have been committed before the release. Therefore, stick it on the checklist for next time. [originally from svn r7169] [r7168 == b3a5cdc5fbab8884755690286211fa6b3425d9d3] --- CHECKLST.txt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHECKLST.txt b/CHECKLST.txt index 6e775f9a..efca86d9 100644 --- a/CHECKLST.txt +++ b/CHECKLST.txt @@ -53,6 +53,11 @@ Before tagging a release containing the word XXX-REVIEW-BEFORE-RELEASE. (Any such comments should state clearly what needs to be done.) + - Also, do some testing of the Windows version with Minefield, and + of the Unix version with valgrind or efence or both. In + particular, any headline features for the release should get a + workout with memory checking enabled! + For a long time we got away with never checking the current version number in at all - all version numbers were passed into the build system on the compiler command line, and the _only_ place version From f042451e3ca6e501f4cc1dbf22fd241418a84054 Mon Sep 17 00:00:00 2001 From: Simon Tatham Date: Fri, 26 Jan 2007 19:43:15 +0000 Subject: [PATCH 003/124] Switch round the order of CFLAGS and XFLAGS, so that the latter comes last on the compiler command line. This makes it easier to override the normal compile options (since conflicting command-line options usually follow a last-wins policy) in order to compile (for example) the Unix version -g -O0. [originally from svn r7170] --- mkfiles.pl | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/mkfiles.pl b/mkfiles.pl index ca51b765..94a83c7c 100755 --- a/mkfiles.pl +++ b/mkfiles.pl @@ -429,7 +429,7 @@ if (defined $makefiles{'cygwin'}) { if ($d->{obj} =~ /\.res\.o$/) { print "\t\$(RC) \$(RCFL) \$(RCFLAGS) ".$d->{deps}->[0]." ".$d->{obj}."\n\n"; } else { - print "\t\$(CC) \$(COMPAT) \$(XFLAGS) \$(CFLAGS) -c ".$d->{deps}->[0]."\n\n"; + print "\t\$(CC) \$(COMPAT) \$(CFLAGS) \$(XFLAGS) -c ".$d->{deps}->[0]."\n\n"; } } print "\n"; @@ -486,7 +486,7 @@ if (defined $makefiles{'borland'}) { "\n". ".c.obj:\n". &splitline("\tbcc32 -w-aus -w-ccc -w-par -w-pia \$(COMPAT)". - " \$(XFLAGS) \$(CFLAGS) ". + " \$(CFLAGS) \$(XFLAGS) ". (join " ", map {"-I$dirpfx$_"} @srcdirs) . " /c \$*.c",69)."\n". ".rc.res:\n". @@ -615,7 +615,7 @@ if (defined $makefiles{'vc'}) { print &splitline(sprintf("%s: %s", $d->{obj}, join " ", @$extradeps, @{$d->{deps}})), "\n"; if ($d->{obj} =~ /.obj$/) { - print "\tcl \$(COMPAT) \$(XFLAGS) \$(CFLAGS) /c ".$d->{deps}->[0],"\n\n"; + print "\tcl \$(COMPAT) \$(CFLAGS) \$(XFLAGS) /c ".$d->{deps}->[0],"\n\n"; } else { print "\trc \$(RCFL) -r \$(RCFLAGS) ".$d->{deps}->[0],"\n\n"; } @@ -962,7 +962,7 @@ if (defined $makefiles{'gtk'}) { print &splitline(sprintf("%s: %s", $d->{obj}, join " ", @{$d->{deps}})), "\n"; } - print &splitline("\t\$(CC) \$(COMPAT) \$(XFLAGS) \$(CFLAGS) -c $d->{deps}->[0]\n"); + print &splitline("\t\$(CC) \$(COMPAT) \$(CFLAGS) \$(XFLAGS) -c $d->{deps}->[0]\n"); } print "\n"; print $makefile_extra{'gtk'}->{'end'}; @@ -1025,7 +1025,7 @@ if (defined $makefiles{'ac'}) { print &splitline(sprintf("%s: %s", $d->{obj}, join " ", @{$d->{deps}})), "\n"; } - print &splitline("\t\$(CC) \$(COMPAT) \$(XFLAGS) \$(CFLAGS) -c $d->{deps}->[0]\n"); + print &splitline("\t\$(CC) \$(COMPAT) \$(CFLAGS) \$(XFLAGS) -c $d->{deps}->[0]\n"); } print "\n"; print $makefile_extra{'gtk'}->{'end'}; @@ -1226,7 +1226,7 @@ if (defined $makefiles{'lcc'}) { } if ($d->{obj} =~ /\.obj$/) { print &splitline("\tlcc -O -p6 \$(COMPAT)". - " \$(XFLAGS) \$(CFLAGS) ".$d->{deps}->[0],69)."\n"; + " \$(CFLAGS) \$(XFLAGS) ".$d->{deps}->[0],69)."\n"; } else { print &splitline("\tlrc \$(RCFL) -r \$(RCFLAGS) ". $d->{deps}->[0],69)."\n"; @@ -1311,9 +1311,9 @@ if (defined $makefiles{'osx'}) { } $firstdep = $d->{deps}->[0]; if ($firstdep =~ /\.c$/) { - print "\t\$(CC) \$(COMPAT) \$(FWHACK) \$(XFLAGS) \$(CFLAGS) -c \$<\n"; + print "\t\$(CC) \$(COMPAT) \$(FWHACK) \$(CFLAGS) \$(XFLAGS) -c \$<\n"; } elsif ($firstdep =~ /\.m$/) { - print "\t\$(CC) -x objective-c \$(COMPAT) \$(FWHACK) \$(XFLAGS) \$(CFLAGS) -c \$<\n"; + print "\t\$(CC) -x objective-c \$(COMPAT) \$(FWHACK) \$(CFLAGS) \$(XFLAGS) -c \$<\n"; } } print "\n".$makefile_extra{'osx'}->{'end'}; From 7d6dae5e584bb5d84356ee3d7ea2db18733e0283 Mon Sep 17 00:00:00 2001 From: Jacob Nevins Date: Mon, 29 Jan 2007 20:10:51 +0000 Subject: [PATCH 004/124] In the cases where Setup asks to restart the computer, explain exactly why this is deemed necessary. [originally from svn r7179] --- windows/putty.iss | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/windows/putty.iss b/windows/putty.iss index 7b867027..8c404509 100644 --- a/windows/putty.iss +++ b/windows/putty.iss @@ -2,6 +2,7 @@ ; $Id$ ; ; -- Inno Setup installer script for PuTTY and its related tools. +; Last tested with Inno Setup 5.0.8. ; ; TODO for future releases: ; @@ -92,3 +93,14 @@ Root: HKCR; Subkey: "PuTTYPrivateKey\shell\edit\command"; ValueType: string; Val ; XXX: it would be nice if this task weren't run if a silent uninstall is ; requested, but "skipifsilent" is disallowed. Filename: "{app}\putty.exe"; Parameters: "-cleanup-during-uninstall"; RunOnceId: "PuTTYCleanup"; StatusMsg: "Cleaning up saved sessions etc (optional)..." + +[Messages] +; Since it's possible for the user to be asked to restart their computer, +; we should override the default messages to explain exactly why, so they +; can make an informed decision. (Especially as 95% of users won't need or +; want to restart; see rant above.) +FinishedRestartLabel=One or more [name] programs are still running. Setup will not replace these program files until you restart your computer. Would you like to restart now? +; This message is popped up in a message box on a /SILENT install. +FinishedRestartMessage=One or more [name] programs are still running.%nSetup will not replace these program files until you restart your computer.%n%nWould you like to restart now? +; ...and this comes up if you try to uninstall. +UninstalledAndNeedsRestart=One or more %1 programs are still running.%nThe program files will not be removed until your computer is restarted.%n%nWould you like to restart now? From b5df0a7732024a838adf79dbebb6f7dae62f0799 Mon Sep 17 00:00:00 2001 From: Simon Tatham Date: Wed, 31 Jan 2007 12:30:48 +0000 Subject: [PATCH 005/124] Colin Watson has fixed the disgusting icons on GTK1. His patch appears to merely fix the background colour (arranging for it to have transparency rather than being on some kind of default grey background), but it turns out to also fix the strange blurry behaviour I see in the GNOME Taskbar, for no very obvious reason. [originally from svn r7186] --- unix/gtkwin.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/unix/gtkwin.c b/unix/gtkwin.c index d0b46931..430e2b58 100644 --- a/unix/gtkwin.c +++ b/unix/gtkwin.c @@ -3306,6 +3306,7 @@ void set_window_icon(GtkWidget *window, const char *const *const *icon, int n_icon) { GdkPixmap *iconpm; + GdkBitmap *iconmask; #if GTK_CHECK_VERSION(2,0,0) GList *iconlist; int n; @@ -3315,9 +3316,9 @@ void set_window_icon(GtkWidget *window, const char *const *const *icon, return; gtk_widget_realize(window); - iconpm = gdk_pixmap_create_from_xpm_d(window->window, NULL, + iconpm = gdk_pixmap_create_from_xpm_d(window->window, &iconmask, NULL, (gchar **)icon[0]); - gdk_window_set_icon(window->window, NULL, iconpm, NULL); + gdk_window_set_icon(window->window, NULL, iconpm, iconmask); #if GTK_CHECK_VERSION(2,0,0) iconlist = NULL; From 0f366c32bb221e508de9f9c7214414ccd5111436 Mon Sep 17 00:00:00 2001 From: Jacob Nevins Date: Thu, 1 Feb 2007 23:24:30 +0000 Subject: [PATCH 006/124] Swap order of `Columns' and `Rows' in the config dialog, to make it consistent with sizetip.c (and more nebulous conventions). [originally from svn r7196] --- config.c | 8 ++++---- doc/config.but | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/config.c b/config.c index 1e5f8da1..d3e59aa5 100644 --- a/config.c +++ b/config.c @@ -1399,13 +1399,13 @@ void setup_config_box(struct controlbox *b, int midsession, s = ctrl_getset(b, "Window", "size", "Set the size of the window"); ctrl_columns(s, 2, 50, 50); - c = ctrl_editbox(s, "Rows", 'r', 100, - HELPCTX(window_size), - dlg_stdeditbox_handler, I(offsetof(Config,height)),I(-1)); - c->generic.column = 0; c = ctrl_editbox(s, "Columns", 'm', 100, HELPCTX(window_size), dlg_stdeditbox_handler, I(offsetof(Config,width)), I(-1)); + c->generic.column = 0; + c = ctrl_editbox(s, "Rows", 'r', 100, + HELPCTX(window_size), + dlg_stdeditbox_handler, I(offsetof(Config,height)),I(-1)); c->generic.column = 1; ctrl_columns(s, 1, 100); diff --git a/doc/config.but b/doc/config.but index c351a1b2..63aac0ad 100644 --- a/doc/config.but +++ b/doc/config.but @@ -1003,7 +1003,7 @@ The Window configuration panel allows you to control aspects of the \cfg{winhelp-topic}{window.size} -The \q{\ii{Rows}} and \q{\ii{Columns}} boxes let you set the PuTTY +The \q{\ii{Columns}} and \q{\ii{Rows}} boxes let you set the PuTTY window to a precise size. Of course you can also \I{window resizing}drag the window to a new size while a session is running. From 29ba6a6b873ae75dcf4b00e3a879fe17739cf8cf Mon Sep 17 00:00:00 2001 From: Simon Tatham Date: Sun, 4 Feb 2007 11:17:45 +0000 Subject: [PATCH 007/124] Build script for PuTTY using bob. [originally from svn r7205] --- Buildscr | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++++ doc/Makefile | 4 ++++ 2 files changed, 62 insertions(+) create mode 100644 Buildscr diff --git a/Buildscr b/Buildscr new file mode 100644 index 00000000..4681dc52 --- /dev/null +++ b/Buildscr @@ -0,0 +1,58 @@ +# -*- sh -*- +# Build script to construct a full distribution directory of PuTTY. + +# Set up the arguments for the main make command. +set Makeargs +ifneq "$(RELEASE)" "" set Makeargs VER="-DRELEASE=$(RELEASE)" +ifneq "$(SNAPSHOT)" "" set Makeargs VER="-DSNAPSHOT=$(SNAPSHOT)" +ifneq "$(XFLAGS)" "" set Makeargs $(makeargs) XFLAGS="$(XFLAGS)" +ifneq "$(MAKEARGS)" "" set Makeargs $(makeargs) $(MAKEARGS) + +# Set up the version string for the docs build. +set Docmakeargs +ifneq "$(RELEASE)" "" set Docmakeargs VERSION="PuTTY release $(RELEASE)" +ifneq "$(SNAPSHOT)" "" set Docmakeaargs VERSION="PuTTY development snapshot $(SNAPSHOT)" + +# Set up the version string for the installer. +set Iversion +ifneq "$(RELEASE)" "" set Iversion $(RELEASE) +ifneq "$(SNAPSHOT)" "" set Iversion $(SNAPSHOT) +# FIXME: what about the static version numbers in putty.iss? + +in putty do ./mksrcarc.sh +in putty do ./mkunxarc.sh $(RELEASE) +in putty do perl mkfiles.pl +in putty/doc do make $(Docmakeargs) putty.hlp +in putty/doc do make $(Docmakeargs) chm +delegate windows + # FIXME: Cygwin alternative? + in putty/windows do cmd /c 'vcvars32 & nmake -f Makefile.vc $(Makeargs)' + # Ignore exit code from hhc, in favour of seeing whether the .chm + # file was created. (Yuck; but hhc appears to return non-zero + # exit codes on whim.) + in putty/doc do hhc putty.hhp; test -f putty.chm + in putty/windows do iscc putty.iss + return putty/windows/*.exe + return putty/doc/putty.chm + return putty/windows/Output/setup.exe +enddelegate +in putty/doc do make mostlyclean +in putty/doc do make $(Docmakeargs) +in putty/windows do zip -k -j putty.zip `ls *.exe | grep -v puttytel` ../doc/putty.chm ../doc/putty.hlp ../doc/putty.cnt +in putty/doc do zip puttydoc.zip *.html + +deliver putty/windows/*.exe x86/$@ +deliver putty/windows/putty.zip x86/$@ +deliver putty/windows/Output/setup.exe x86/putty-$(Iversion)-installer.exe +deliver putty/doc/puttydoc.zip $@ +deliver putty/doc/putty.chm $@ +deliver putty/doc/putty.hlp $@ +deliver putty/doc/putty.cnt $@ +deliver putty/doc/puttydoc.txt $@ +deliver putty/doc/*.html htmldoc/$@ +deliver putty/putty-src.zip $@ +deliver putty/*.tar.gz $@ + +# Building the md5sums file is most easily done in the destination +# directory. +in-dest . do md5sum `\find * -type f -print` > md5sums diff --git a/doc/Makefile b/doc/Makefile index c089fa13..36e3672b 100644 --- a/doc/Makefile +++ b/doc/Makefile @@ -46,6 +46,10 @@ HALIBUT = halibut index.html: $(INPUTS) $(HALIBUT) --text --html --winhelp $(INPUTS) +# During formal builds it's useful to be able to build this one alone. +putty.hlp: $(INPUTS) + $(HALIBUT) --winhelp $(INPUTS) + putty.info: $(INPUTS) $(HALIBUT) --info $(INPUTS) From c183555bd5185498fae083881f892b0428456ecd Mon Sep 17 00:00:00 2001 From: Jacob Nevins Date: Sun, 4 Feb 2007 12:12:52 +0000 Subject: [PATCH 008/124] Document \\.\COM10 faff on Windows. References: (CreateFile() docs) describes the use of \\.\ ("Naming a File") lists the reserved filenames (COM1-COM9, LPT1-LPT9, CON, PRN, AUX, NUL) [originally from svn r7208] --- doc/config.but | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/doc/config.but b/doc/config.but index 63aac0ad..29259572 100644 --- a/doc/config.but +++ b/doc/config.but @@ -2975,8 +2975,10 @@ The \q{Serial line to connect to} box allows you to choose which serial line you want PuTTY to talk to, if your computer has more than one serial port. -On Windows, the first serial line is called \cw{COM1}, and if there -is a second it is called \cw{COM2}, and so on. +On Windows, the first serial line is called \i\cw{COM1}, and if there +is a second it is called \cw{COM2}, and so on. A serial line with +a name other than \cw{COM1} to \cw{COM9} can be specified by prefixing +its name with \cw{\\\\.\\} - for instance, \cw{\\\\.\\COM10}. This configuration setting is also visible on the Session panel, where it replaces the \q{Host Name} box (see \k{config-hostname}) if From ab795ba00889e6f1c9dc57b053d14ddf6bbc5fee Mon Sep 17 00:00:00 2001 From: Simon Tatham Date: Sun, 4 Feb 2007 12:30:39 +0000 Subject: [PATCH 009/124] Version management updates for the new bob build script. There's now a fourth class of PuTTY version tags in addition to release, snapshot and unidentified: we now have `Custom build r1234', indicating a build made from that SVN revision in a context other than that of a dated snapshot. The build script generates these when it doesn't know what else to do; `unidentified builds' will now only occur when you run nmake from the command line. Also, the build script now generates sensible version data in the installer to match this. So I _think_ we should now be set to use bob to generate installer builds of the nightly snapshots, although of course I'll have to wait until tomorrow to test one. [originally from svn r7211] --- Buildscr | 36 ++++++++++++++++++++++++++++-------- version.c | 5 +++++ windows/version.rc2 | 5 +++++ 3 files changed, 38 insertions(+), 8 deletions(-) diff --git a/Buildscr b/Buildscr index 4681dc52..90b66aab 100644 --- a/Buildscr +++ b/Buildscr @@ -2,28 +2,48 @@ # Build script to construct a full distribution directory of PuTTY. # Set up the arguments for the main make command. -set Makeargs -ifneq "$(RELEASE)" "" set Makeargs VER="-DRELEASE=$(RELEASE)" -ifneq "$(SNAPSHOT)" "" set Makeargs VER="-DSNAPSHOT=$(SNAPSHOT)" +set Makeargs VER="-DSVN_REV=$(revision)" +ifneq "$(RELEASE)" "" set Makeargs $(Makeargs) VER="-DRELEASE=$(RELEASE)" +ifneq "$(date)" "" set Makeargs $(Makeargs) VER="-DSNAPSHOT=$(date)" ifneq "$(XFLAGS)" "" set Makeargs $(makeargs) XFLAGS="$(XFLAGS)" ifneq "$(MAKEARGS)" "" set Makeargs $(makeargs) $(MAKEARGS) # Set up the version string for the docs build. -set Docmakeargs +set Docmakeargs VERSION="PuTTY revision $(revision)" ifneq "$(RELEASE)" "" set Docmakeargs VERSION="PuTTY release $(RELEASE)" -ifneq "$(SNAPSHOT)" "" set Docmakeaargs VERSION="PuTTY development snapshot $(SNAPSHOT)" +ifneq "$(date)" "" set Docmakeaargs VERSION="PuTTY development snapshot $(date)" + +# Set up the various version strings for the installer. +set Iversion r$(revision) +set Iname PuTTY revision $(revision) +set Ivertext Revision $(revision) +set Irev $(revision) +ifneq "$(RELEASE)" "" set Iversion $(RELEASE) +ifneq "$(RELEASE)" "" set Iname PuTTY version $(RELEASE) +ifneq "$(RELEASE)" "" set Ivertext Release $(RELEASE) +ifneq "$(RELEASE)" "" set Irev +ifneq "$(date)" "" set Iversion $(date):r$(revision) +ifneq "$(date)" "" set Iname PuTTY development snapshot $(date):r$(revision) +ifneq "$(date)" "" set Ivertext Development snapshot $(date):r$(revision) # Set up the version string for the installer. -set Iversion +set Iversion r$(revision) ifneq "$(RELEASE)" "" set Iversion $(RELEASE) -ifneq "$(SNAPSHOT)" "" set Iversion $(SNAPSHOT) -# FIXME: what about the static version numbers in putty.iss? +ifneq "$(date)" "" set Iversion $(date):r$(revision) in putty do ./mksrcarc.sh in putty do ./mkunxarc.sh $(RELEASE) in putty do perl mkfiles.pl in putty/doc do make $(Docmakeargs) putty.hlp in putty/doc do make $(Docmakeargs) chm + +# Munge the installer script locally so that it reports the version +# we're really building. +in putty/windows do perl -i~ -pe 'BEGIN{$$a=shift@ARGV;}s/^(AppVerName=).*$$/$$1$$a/' '$(Iname)' putty.iss +in putty/windows do perl -i~ -pe 'BEGIN{$$a=shift@ARGV;}s/^(VersionInfoTextVersion=).*$$/$$1$$a/' '$(Ivertext)' putty.iss +in putty/windows do perl -i~ -pe 'BEGIN{$$a=shift@ARGV;}s/^(AppVersion=).*$$/$$1$$a/' '$(Iversion)' putty.iss +in putty/windows do perl -i~ -pe 'BEGIN{$$a=shift@ARGV;$$a=~s/M//;}s/^(VersionInfoVersion=\d+\.\d+\.)\d+(\.\d+)\r?$$/$$1$$a$$2/' '$(Irev)' putty.iss + delegate windows # FIXME: Cygwin alternative? in putty/windows do cmd /c 'vcvars32 & nmake -f Makefile.vc $(Makeargs)' diff --git a/version.c b/version.c index da7e11ca..de658677 100644 --- a/version.c +++ b/version.c @@ -23,6 +23,11 @@ char sshver[] = "PuTTY-Snapshot-" SNAPSHOT_TEXT; char ver[] = "Release " STR(RELEASE); char sshver[] = "PuTTY-Release-" STR(RELEASE); +#elif defined SVN_REV + +char ver[] = "Custom build r" STR(SVN_REV); +char sshver[] = "PuTTY-Custom-r" STR(SVN_REV); + #else char ver[] = "Unidentified build, " __DATE__ " " __TIME__; diff --git a/windows/version.rc2 b/windows/version.rc2 index 8d478722..5bac1ca8 100644 --- a/windows/version.rc2 +++ b/windows/version.rc2 @@ -57,6 +57,11 @@ #define VERSION_TEXT "Release " STR(RELEASE) #define BINARY_VERSION BASE_VERSION,0,0 +#elif defined SVN_REV + +#define VERSION_TEXT "Custom build r" STR(SVN_REV) +#define BINARY_VERSION BASE_VERSION,SVN_REV,0 + #else /* We can't reliably get the same date and time as version.c, so From 64b4984354ccef36fda068ca17748aa082b282dd Mon Sep 17 00:00:00 2001 From: Simon Tatham Date: Sun, 4 Feb 2007 12:37:20 +0000 Subject: [PATCH 010/124] PuTTY builds should save the map files. [originally from svn r7212] --- Buildscr | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Buildscr b/Buildscr index 90b66aab..eabd159f 100644 --- a/Buildscr +++ b/Buildscr @@ -53,6 +53,7 @@ delegate windows in putty/doc do hhc putty.hhp; test -f putty.chm in putty/windows do iscc putty.iss return putty/windows/*.exe + return putty/windows/*.map return putty/doc/putty.chm return putty/windows/Output/setup.exe enddelegate @@ -62,6 +63,7 @@ in putty/windows do zip -k -j putty.zip `ls *.exe | grep -v puttytel` ../doc/put in putty/doc do zip puttydoc.zip *.html deliver putty/windows/*.exe x86/$@ +deliver putty/windows/*.map maps-x86/$@ deliver putty/windows/putty.zip x86/$@ deliver putty/windows/Output/setup.exe x86/putty-$(Iversion)-installer.exe deliver putty/doc/puttydoc.zip $@ From 9951498e63629357ff6f59ebae4f9f5264767bba Mon Sep 17 00:00:00 2001 From: Simon Tatham Date: Mon, 5 Feb 2007 08:02:01 +0000 Subject: [PATCH 011/124] Fix errors in $(Makeargs) which only occur when building development snapshots. [originally from svn r7220] --- Buildscr | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Buildscr b/Buildscr index eabd159f..6c01b2e8 100644 --- a/Buildscr +++ b/Buildscr @@ -2,9 +2,10 @@ # Build script to construct a full distribution directory of PuTTY. # Set up the arguments for the main make command. -set Makeargs VER="-DSVN_REV=$(revision)" -ifneq "$(RELEASE)" "" set Makeargs $(Makeargs) VER="-DRELEASE=$(RELEASE)" -ifneq "$(date)" "" set Makeargs $(Makeargs) VER="-DSNAPSHOT=$(date)" +set Makever -DSVN_REV=$(revision) +ifneq "$(RELEASE)" "" set Makever $(Makever) -DRELEASE=$(RELEASE) +ifneq "$(date)" "" set Makever $(Makever) -DSNAPSHOT=$(date) +set Makeargs VER="$(Makever)" ifneq "$(XFLAGS)" "" set Makeargs $(makeargs) XFLAGS="$(XFLAGS)" ifneq "$(MAKEARGS)" "" set Makeargs $(makeargs) $(MAKEARGS) @@ -46,7 +47,7 @@ in putty/windows do perl -i~ -pe 'BEGIN{$$a=shift@ARGV;$$a=~s/M//;}s/^(VersionIn delegate windows # FIXME: Cygwin alternative? - in putty/windows do cmd /c 'vcvars32 & nmake -f Makefile.vc $(Makeargs)' + in putty/windows do cmd /c vcvars32 \& nmake -f Makefile.vc $(Makeargs) # Ignore exit code from hhc, in favour of seeing whether the .chm # file was created. (Yuck; but hhc appears to return non-zero # exit codes on whim.) From 0a4bb7e711e1715d60111ec0bd2e12f5ecfcb7ee Mon Sep 17 00:00:00 2001 From: Simon Tatham Date: Mon, 5 Feb 2007 08:02:53 +0000 Subject: [PATCH 012/124] Be slightly more forgiving about the nature of SVN_REV; bob will be reliable at defining it, and it's useful to be able to pass `1234M'- type revisions in for testing purposes. [originally from svn r7221] --- windows/version.rc2 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/windows/version.rc2 b/windows/version.rc2 index 5bac1ca8..049dbe53 100644 --- a/windows/version.rc2 +++ b/windows/version.rc2 @@ -45,7 +45,7 @@ /* Make SVN_REV mandatory for snapshots, to avoid issuing binary * version numbers that look like full releases. */ -#if (!defined SVN_REV) || (SVN_REV == 0) +#ifndef SVN_REV #error SVN_REV not defined/nonzero for snapshot build #endif From a45f89cdd4a587e6ad3e2e0d2e5ca4fb558f1125 Mon Sep 17 00:00:00 2001 From: Simon Tatham Date: Mon, 5 Feb 2007 12:49:24 +0000 Subject: [PATCH 013/124] Fixes for snapshot building using bob. All of releases, snapshots and custom svn builds should now have appropriately named Unix source archives and installer binaries, plus .htaccess files providing redirects to them from totally standard filenames. I _think_ this now makes it feasible to switch the nightly builds to using bob. [originally from svn r7226] --- Buildscr | 32 ++++++++++++++++++++++++++++---- mkunxarc.sh | 8 +++++++- 2 files changed, 35 insertions(+), 5 deletions(-) diff --git a/Buildscr b/Buildscr index 6c01b2e8..acdfa1c6 100644 --- a/Buildscr +++ b/Buildscr @@ -1,6 +1,8 @@ # -*- sh -*- # Build script to construct a full distribution directory of PuTTY. +module putty + # Set up the arguments for the main make command. set Makever -DSVN_REV=$(revision) ifneq "$(RELEASE)" "" set Makever $(Makever) -DRELEASE=$(RELEASE) @@ -14,18 +16,26 @@ set Docmakeargs VERSION="PuTTY revision $(revision)" ifneq "$(RELEASE)" "" set Docmakeargs VERSION="PuTTY release $(RELEASE)" ifneq "$(date)" "" set Docmakeaargs VERSION="PuTTY development snapshot $(date)" +# Set up the version string for the Unix source archive. +set Unxver r$(revision) +ifneq "$(RELEASE)" "" set Unxver $(RELEASE) +ifneq "$(date)" "" set Unxver $(date) + # Set up the various version strings for the installer. set Iversion r$(revision) set Iname PuTTY revision $(revision) set Ivertext Revision $(revision) set Irev $(revision) +set Ifilename putty-$(Iversion)-installer.exe ifneq "$(RELEASE)" "" set Iversion $(RELEASE) ifneq "$(RELEASE)" "" set Iname PuTTY version $(RELEASE) ifneq "$(RELEASE)" "" set Ivertext Release $(RELEASE) -ifneq "$(RELEASE)" "" set Irev +ifneq "$(RELEASE)" "" set Irev 0 +ifneq "$(RELEASE)" "" set Ifilename putty-$(RELEASE)-installer.exe ifneq "$(date)" "" set Iversion $(date):r$(revision) ifneq "$(date)" "" set Iname PuTTY development snapshot $(date):r$(revision) ifneq "$(date)" "" set Ivertext Development snapshot $(date):r$(revision) +ifneq "$(date)" "" set Ifilename putty-$(date)-installer.exe # Set up the version string for the installer. set Iversion r$(revision) @@ -33,7 +43,7 @@ ifneq "$(RELEASE)" "" set Iversion $(RELEASE) ifneq "$(date)" "" set Iversion $(date):r$(revision) in putty do ./mksrcarc.sh -in putty do ./mkunxarc.sh $(RELEASE) +in putty do ./mkunxarc.sh $(Unxver) in putty do perl mkfiles.pl in putty/doc do make $(Docmakeargs) putty.hlp in putty/doc do make $(Docmakeargs) chm @@ -64,9 +74,8 @@ in putty/windows do zip -k -j putty.zip `ls *.exe | grep -v puttytel` ../doc/put in putty/doc do zip puttydoc.zip *.html deliver putty/windows/*.exe x86/$@ -deliver putty/windows/*.map maps-x86/$@ deliver putty/windows/putty.zip x86/$@ -deliver putty/windows/Output/setup.exe x86/putty-$(Iversion)-installer.exe +deliver putty/windows/Output/setup.exe x86/$(Ifilename) deliver putty/doc/puttydoc.zip $@ deliver putty/doc/putty.chm $@ deliver putty/doc/putty.hlp $@ @@ -79,3 +88,18 @@ deliver putty/*.tar.gz $@ # Building the md5sums file is most easily done in the destination # directory. in-dest . do md5sum `\find * -type f -print` > md5sums + +# Now deliver the map files _after_ we do that, so we don't md5sum +# them gratuitously. +deliver putty/windows/*.map maps-x86/$@ + +# And construct .htaccess files. One in the top-level directory, +# setting the MIME types for Windows help files and providing an +# appropriate link to the source archive: +in-dest . do echo "AddType application/octet-stream .chm" > .htaccess +in-dest . do echo "AddType application/octet-stream .hlp" >> .htaccess +in-dest . do echo "AddType application/octet-stream .cnt" >> .htaccess +in-dest . do set -- putty*.tar.gz; echo RedirectMatch temp '(.*/)'putty.tar.gz '$$1'"$$1" >> .htaccess +# And one in the x86 directory, providing a link for the installer. +in-dest x86 do set -- putty*installer.exe; echo RedirectMatch temp '(.*/)'putty-installer.exe '$$1'"$$1" > .htaccess + diff --git a/mkunxarc.sh b/mkunxarc.sh index 7e8d367c..7c69c2f6 100755 --- a/mkunxarc.sh +++ b/mkunxarc.sh @@ -4,7 +4,8 @@ # # Pass an argument of the form `2004-02-08' to have the archive # tagged as a development snapshot; of the form `0.54' to have it -# tagged as a release. +# tagged as a release; of the form `r1234' to have it tagged as a +# custom build. Otherwise it'll be tagged as unidentified. case "$1" in ????-??-??) @@ -13,6 +14,11 @@ case "$1" in ver="-DSNAPSHOT=$1" docver= ;; + r*) + arcsuffix="-$1" + ver="-DSVN_REV=$1" + docver= + ;; '') arcsuffix= ver= From 1c2c6f6c475c64e1e56beda2a86d51ad908fdac7 Mon Sep 17 00:00:00 2001 From: Simon Tatham Date: Mon, 5 Feb 2007 13:53:48 +0000 Subject: [PATCH 014/124] Script to generate signatures on the various PuTTY build outputs. Saves me having to remember all the fiddly gpg arguments every time. Should be usable for both releases (with manual passphrase input) and snapshots (run automatically). [originally from svn r7227] --- sign.sh | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100755 sign.sh diff --git a/sign.sh b/sign.sh new file mode 100755 index 00000000..ac6a62ca --- /dev/null +++ b/sign.sh @@ -0,0 +1,29 @@ +#!/bin/sh + +# Generate GPG signatures on a PuTTY release/snapshot directory as +# delivered by Buildscr. + +# Usage: sign.sh +# e.g. sign.sh build.out Snapshots +# or sign.sh 0.60 Releases + +set -e + +sign() { + # Check for the prior existence of the signature, so we can + # re-run this script if it encounters an error part way + # through. + echo "----- Signing $2 with '$keyname'" + test -f "$3" || \ + gpg --load-extension=idea "$1" -u "$keyname" -o "$3" "$2" +} + +cd "$1" +for t in DSA RSA; do + keyname="$2 ($t)" + echo "===== Signing with '$keyname'" + for i in putty*src.zip putty*.tar.gz x86/*.exe x86/*.zip; do + sign --detach-sign "$i" "$i.$t" + done + sign --clearsign md5sums md5sums.$t +done From 54dd6440ba778c6feb3469727957fa319c5b6feb Mon Sep 17 00:00:00 2001 From: Simon Tatham Date: Mon, 5 Feb 2007 14:23:34 +0000 Subject: [PATCH 015/124] HTTP redirects for the variably-named signatures as well as their base files. (The signatures aren't actually _generated_ by bob, of course, but the redirects are harmless in their absence.) [originally from svn r7228] --- Buildscr | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/Buildscr b/Buildscr index acdfa1c6..1709131b 100644 --- a/Buildscr +++ b/Buildscr @@ -96,10 +96,9 @@ deliver putty/windows/*.map maps-x86/$@ # And construct .htaccess files. One in the top-level directory, # setting the MIME types for Windows help files and providing an # appropriate link to the source archive: -in-dest . do echo "AddType application/octet-stream .chm" > .htaccess +in-dest . do echo "AddType application/octet-stream .chm" >> .htaccess in-dest . do echo "AddType application/octet-stream .hlp" >> .htaccess in-dest . do echo "AddType application/octet-stream .cnt" >> .htaccess -in-dest . do set -- putty*.tar.gz; echo RedirectMatch temp '(.*/)'putty.tar.gz '$$1'"$$1" >> .htaccess +in-dest . do set -- putty*.tar.gz; for k in '' .DSA .RSA; do echo RedirectMatch temp '(.*/)'putty.tar.gz$$k '$$1'"$$1$$k" >> .htaccess; done # And one in the x86 directory, providing a link for the installer. -in-dest x86 do set -- putty*installer.exe; echo RedirectMatch temp '(.*/)'putty-installer.exe '$$1'"$$1" > .htaccess - +in-dest x86 do set -- putty*installer.exe; for k in '' .DSA .RSA; do echo RedirectMatch temp '(.*/)'putty-installer.exe$$k '$$1'"$$1$$k" >> .htaccess; done From 0865939a56235aa682ce50fbe909f9a3550431ec Mon Sep 17 00:00:00 2001 From: Simon Tatham Date: Mon, 5 Feb 2007 15:01:28 +0000 Subject: [PATCH 016/124] Fiddle further with .htaccess: add some $s on the ends of the regexps to stop them matching the wrong files. [originally from svn r7229] --- Buildscr | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Buildscr b/Buildscr index 1709131b..090e89a0 100644 --- a/Buildscr +++ b/Buildscr @@ -99,6 +99,6 @@ deliver putty/windows/*.map maps-x86/$@ in-dest . do echo "AddType application/octet-stream .chm" >> .htaccess in-dest . do echo "AddType application/octet-stream .hlp" >> .htaccess in-dest . do echo "AddType application/octet-stream .cnt" >> .htaccess -in-dest . do set -- putty*.tar.gz; for k in '' .DSA .RSA; do echo RedirectMatch temp '(.*/)'putty.tar.gz$$k '$$1'"$$1$$k" >> .htaccess; done +in-dest . do set -- putty*.tar.gz; for k in '' .DSA .RSA; do echo RedirectMatch temp '(.*/)'putty.tar.gz$$k\$$ '$$1'"$$1$$k" >> .htaccess; done # And one in the x86 directory, providing a link for the installer. -in-dest x86 do set -- putty*installer.exe; for k in '' .DSA .RSA; do echo RedirectMatch temp '(.*/)'putty-installer.exe$$k '$$1'"$$1$$k" >> .htaccess; done +in-dest x86 do set -- putty*installer.exe; for k in '' .DSA .RSA; do echo RedirectMatch temp '(.*/)'putty-installer.exe$$k\$$ '$$1'"$$1$$k" >> .htaccess; done From a5d45db0c9fe7684951901f88307f32dcd19829d Mon Sep 17 00:00:00 2001 From: Simon Tatham Date: Mon, 5 Feb 2007 18:07:11 +0000 Subject: [PATCH 017/124] Avoid passing modified SVN revision numbers (of the form 1234M) to parts of the versioning code which might not like them. As a result of this checkin, bob builds from modified SVN working copies will still announce themselves as revision nnnnM in the textual version strings, but their binary version in the Windows VERSIONINFO will now be 0.0.0.0. [originally from svn r7231] --- Buildscr | 1 + windows/version.rc2 | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/Buildscr b/Buildscr index 090e89a0..d48f5a48 100644 --- a/Buildscr +++ b/Buildscr @@ -5,6 +5,7 @@ module putty # Set up the arguments for the main make command. set Makever -DSVN_REV=$(revision) +ifneq "$(!numeric $(revision))" "yes" set Makever $(Makever) -DMODIFIED ifneq "$(RELEASE)" "" set Makever $(Makever) -DRELEASE=$(RELEASE) ifneq "$(date)" "" set Makever $(Makever) -DSNAPSHOT=$(date) set Makeargs VER="$(Makever)" diff --git a/windows/version.rc2 b/windows/version.rc2 index 049dbe53..8474a62b 100644 --- a/windows/version.rc2 +++ b/windows/version.rc2 @@ -50,7 +50,11 @@ #endif #define VERSION_TEXT "Development snapshot " STR(SNAPSHOT) ":r" STR(SVN_REV) +#ifdef MODIFIED +#define BINARY_VERSION 0,0,0,0 +#else #define BINARY_VERSION BASE_VERSION,SVN_REV,0 +#endif #elif defined RELEASE @@ -60,7 +64,11 @@ #elif defined SVN_REV #define VERSION_TEXT "Custom build r" STR(SVN_REV) +#ifdef MODIFIED +#define BINARY_VERSION 0,0,0,0 +#else #define BINARY_VERSION BASE_VERSION,SVN_REV,0 +#endif #else From 6ee6a4d37926062f5b610de4cef2a6ce4fe63345 Mon Sep 17 00:00:00 2001 From: Simon Tatham Date: Mon, 5 Feb 2007 20:04:33 +0000 Subject: [PATCH 018/124] When calling TIOCSCTTY, it helps to pass it an fd that's still open, instead of one we closed two lines earlier. I apparently broke this in r7107. [originally from svn r7232] [r7107 == 32b25c13dae27bb4f485ab2d2c4737572fa28251] --- unix/uxpty.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unix/uxpty.c b/unix/uxpty.c index f79b974f..96b64e8d 100644 --- a/unix/uxpty.c +++ b/unix/uxpty.c @@ -775,7 +775,7 @@ static const char *pty_init(void *frontend, void **backend_handle, Config *cfg, close(slavefd); setsid(); #ifdef TIOCSCTTY - ioctl(slavefd, TIOCSCTTY, 1); + ioctl(0, TIOCSCTTY, 1); #endif pgrp = getpid(); tcsetpgrp(slavefd, pgrp); From 9c35141162c047546e5bb400b97c53cdd23a7dca Mon Sep 17 00:00:00 2001 From: Simon Tatham Date: Mon, 5 Feb 2007 20:14:17 +0000 Subject: [PATCH 019/124] Ahem; other half of r7232... [originally from svn r7233] [r7232 == 6ee6a4d37926062f5b610de4cef2a6ce4fe63345] --- unix/uxpty.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unix/uxpty.c b/unix/uxpty.c index 96b64e8d..708e82d6 100644 --- a/unix/uxpty.c +++ b/unix/uxpty.c @@ -778,7 +778,7 @@ static const char *pty_init(void *frontend, void **backend_handle, Config *cfg, ioctl(0, TIOCSCTTY, 1); #endif pgrp = getpid(); - tcsetpgrp(slavefd, pgrp); + tcsetpgrp(0, pgrp); setpgid(pgrp, pgrp); close(open(pty->name, O_WRONLY, 0)); setpgid(pgrp, pgrp); From 91694cb3a28a10e105430bf03ef0a37d4701637b Mon Sep 17 00:00:00 2001 From: Ben Harris Date: Tue, 6 Feb 2007 13:57:27 +0000 Subject: [PATCH 020/124] When emitting SSH_MSG_IGNORE to protect against known-IV attacks on CBC, remember to put an empty string in it rather than sending a completely empty packet. This should help with those servers (notably RomSShell) that actually check the contents of SSH_MSG_IGNORE. [originally from svn r7236] --- ssh.c | 1 + 1 file changed, 1 insertion(+) diff --git a/ssh.c b/ssh.c index 137e4607..d4d3d06a 100644 --- a/ssh.c +++ b/ssh.c @@ -1864,6 +1864,7 @@ static void ssh2_pkt_defer_noqueue(Ssh ssh, struct Packet *pkt, int noignore) * get encrypted with a known IV. */ struct Packet *ipkt = ssh2_pkt_init(SSH2_MSG_IGNORE); + ssh2_pkt_addstring_start(ipkt); ssh2_pkt_defer_noqueue(ssh, ipkt, TRUE); } len = ssh2_pkt_construct(ssh, pkt); From 762f341d56597700589b2d99bbafab350d7cd921 Mon Sep 17 00:00:00 2001 From: Jacob Nevins Date: Tue, 6 Feb 2007 22:39:15 +0000 Subject: [PATCH 021/124] `installer.ico' doesn't fit into 8.3, so gets truncated to INSTALLE.ICO in the Windows source Zips. Rename to `puttyins.ico'. [originally from svn r7241] --- icons/Makefile | 14 +++++++------- icons/mkicon.py | 2 +- windows/putty.iss | 2 +- windows/{installer.ico => puttyins.ico} | Bin 4 files changed, 9 insertions(+), 9 deletions(-) rename windows/{installer.ico => puttyins.ico} (100%) diff --git a/icons/Makefile b/icons/Makefile index 373816f3..1074303e 100644 --- a/icons/Makefile +++ b/icons/Makefile @@ -1,6 +1,6 @@ # Makefile for the PuTTY icon suite. -ICONS = putty puttycfg puttygen pscp pageant pterm ptermcfg installer +ICONS = putty puttycfg puttygen pscp pageant pterm ptermcfg puttyins SIZES = 16 32 48 MODE = # override to -it on command line for opaque testing @@ -10,7 +10,7 @@ MONOPNGS = $(foreach I,$(ICONS),$(foreach S,$(SIZES),$(I)-$(S)-mono.png)) TRUEPNGS = $(foreach I,$(ICONS),$(foreach S,$(SIZES),$(I)-$(S)-true.png)) ICOS = putty.ico puttygen.ico pscp.ico pageant.ico pageants.ico puttycfg.ico \ - installer.ico + puttyins.ico CICONS = xpmputty.c xpmpucfg.c xpmpterm.c xpmptcfg.c base: icos cicons @@ -63,11 +63,11 @@ pscp.ico: pscp-16.png pscp-32.png pscp-48.png \ # Because the installer icon makes heavy use of brown when drawing # the cardboard box, it's worth having 8-bit versions of it in # addition to the 4- and 1-bit ones. -installer.ico: installer-16.png installer-32.png installer-48.png \ - installer-16-mono.png installer-32-mono.png \ - installer-48-mono.png \ - installer-16-true.png installer-32-true.png \ - installer-48-true.png +puttyins.ico: puttyins-16.png puttyins-32.png puttyins-48.png \ + puttyins-16-mono.png puttyins-32-mono.png \ + puttyins-48-mono.png \ + puttyins-16-true.png puttyins-32-true.png \ + puttyins-48-true.png ./icon.pl -8 $(filter %-true.png, $^) \ -4 $(filter-out %-true.png, $(filter-out %-mono.png, $^)) \ -1 $(filter %-mono.png, $^) > $@ diff --git a/icons/mkicon.py b/icons/mkicon.py index cf076305..d40a9815 100755 --- a/icons/mkicon.py +++ b/icons/mkicon.py @@ -795,7 +795,7 @@ def puttygen_icon(size): def pscp_icon(size): return xybolt(document(size), computer(size), size) -def installer_icon(size): +def puttyins_icon(size): aret = {} # The box back goes behind the lightning bolt. canvas = xybolt(boxback(size), computer(size), size, boltoffx=-2, boltoffy=+1, aux=aret) diff --git a/windows/putty.iss b/windows/putty.iss index 8c404509..0724b201 100644 --- a/windows/putty.iss +++ b/windows/putty.iss @@ -23,7 +23,7 @@ AppPublisherURL=http://www.chiark.greenend.org.uk/~sgtatham/putty/ AppReadmeFile={app}\README.txt DefaultDirName={pf}\PuTTY DefaultGroupName=PuTTY -SetupIconFile=installer.ico +SetupIconFile=puttyins.ico UninstallDisplayIcon={app}\putty.exe ChangesAssociations=yes ;ChangesEnvironment=yes -- when PATH munging is sorted (probably) diff --git a/windows/installer.ico b/windows/puttyins.ico similarity index 100% rename from windows/installer.ico rename to windows/puttyins.ico From c61c0644e4e37d848ecf9035221b5d336baa066c Mon Sep 17 00:00:00 2001 From: Simon Tatham Date: Thu, 8 Feb 2007 09:24:08 +0000 Subject: [PATCH 022/124] The big payoff from bob (from my POV at least): the PuTTY release procedure is now a huge amount less painful. [originally from svn r7249] --- CHECKLST.txt | 81 ++++++++-------------------------------------------- 1 file changed, 12 insertions(+), 69 deletions(-) diff --git a/CHECKLST.txt b/CHECKLST.txt index efca86d9..ac5f3171 100644 --- a/CHECKLST.txt +++ b/CHECKLST.txt @@ -114,83 +114,26 @@ of the tag. ixion:src/putty/local/announce- in case it's needed again within days of the release going out. - - On my local machines, check out the release-tagged version of the - sources. Do this in a _clean_ directory; don't depend on my usual - source dir. - + Make sure to run mkfiles.pl _after_ this checkout, just in - case. + - Build the release: `bob putty-0.XX RELEASE=0.XX'. This should + generate a basically valid release directory as `build.out'. - - Build the source archives now, while the directory is still - pristine. - + run ./mksrcarc.sh to build the Windows source zip. - + run `./mkunxarc.sh X.YZ' to build the Unix tarball. + - Do a bit of checking that the release binaries basically work, + report their version numbers accurately, and so on. Test the + installer and the Unix source tarball. - - Build the Windows/x86 release binaries. Don't forget to supply - VER=/DRELEASE=. Run them, or at least one or two of them, to - ensure that they really do report their version number correctly, - and sanity-check the version info reported on the files by Windows. - + Save the release link maps. Currently I keep these on ixion, - in src/putty/local/maps-. + - Move the release link maps out of the build directory and save + them somewhere more useful. Currently I keep these on ixion, in + src/putty/local/maps-. - - Run Halibut to build the docs. Define VERSION on the make command - line to override the version strings, since Subversion revision - numbers are less meaningful on a tag. - + change into the doc subdir - + run `make VERSION="PuTTY release 0.XX" chm', then run `hhc - putty.hhp' to build the .CHM - + then run `make mostlyclean' (destroys the hhc input files but - _not_ the .CHM) - + then `make VERSION="PuTTY release 0.XX"' - - - Build the binary archive putty.zip: all the .exe files except - PuTTYtel, and the .hlp, .cnt and .chm files. - + zip -k putty.zip `ls *.exe | grep -v puttytel` putty.hlp putty.cnt putty.chm - - - Build the docs archive puttydoc.zip: it contains all the HTML - files output from Halibut. - + zip puttydoc.zip *.html - - - Build the installer. - - - Sign the release (gpg --detach-sign). - + Sign the locally built x86 binaries, the locally built x86 - binary zipfile, and the locally built x86 installer, with the - release keys. - + The source archive should be signed with the release keys. - + Don't forget to sign with both DSA and RSA keys for absolutely - everything. - for i in ; do for t in DSA RSA; do gpg --load-extension=idea --detach-sign -u "Releases ($t)" -o $i.$t $i; done; done - - - Begin to pull together the release directory structure. - + subdir `x86' containing the x86 binaries, x86 binary zip, x86 - installer, and all signatures on the above. - + top-level dir contains the Windows source zip (plus - signatures), the Unix source tarball (plus signatures), - puttydoc.txt, the .hlp, .cnt and .chm files, and puttydoc.zip. - - - Create subdir `htmldoc' in the release directory, which should - contain exactly the same set of HTML files that went into - puttydoc.zip. - + It also needs a copy of sitestyle.css, because the online - versions of the HTML docs will link to this (although the - zipped form should be self-contained). - - - Create and sign an md5sums file in the top-level directory. - + The md5sums files need not list the .DSA and .RSA signatures. - Easiest thing is to run this command: - md5sum `\find * -name '*SA' -o -type f -print` > md5sums - + Sign the md5sums file (gpg --clearsign). - for t in DSA RSA; do gpg --load-extension=idea --clearsign -u "Releases ($t)" -o md5sums.$t md5sums; done - - - Now double-check by verifying all the signatures on all the - files, and running md5sum -c on the md5sums file. + - Sign the release: type `./sign.sh build.out Releases', and enter + the passphrases a lot of times. - Now the whole release directory should be present and correct. Upload to ixion:www/putty/. - Do final checks on the release directory: - + verify all the signatures. In each directory: - for i in *.*SA; do case $i in md5sums*) gpg --verify $i;; *) gpg --verify $i `echo $i | sed 's/\..SA$//'`;; esac; done + + verify all the signatures: + for i in `find . -name '*.*SA'`; do case $i in *md5sums*) gpg --verify $i;; *) gpg --verify $i ${i%%.?SA};; esac; done + check the md5sums: md5sum -c md5sums From c4893477bdc563fd24cfeb406ff1925467670673 Mon Sep 17 00:00:00 2001 From: Simon Tatham Date: Thu, 8 Feb 2007 18:53:11 +0000 Subject: [PATCH 023/124] I've changed my mind about the PuTTY build script. It now delivers the release directory into a _subdirectory_ of the main build.out, and delivers the link maps and sign.sh alongside it. That simplifies both the nightly snapshot cron job (which now doesn't have to carefully move the maps out of the release directory or go looking in strange places for sign.sh) and my release procedure (for much the same reasons). [originally from svn r7258] --- Buildscr | 47 ++++++++++++++++++++++++++--------------------- CHECKLST.txt | 13 +++++++------ 2 files changed, 33 insertions(+), 27 deletions(-) diff --git a/Buildscr b/Buildscr index d48f5a48..568196a8 100644 --- a/Buildscr +++ b/Buildscr @@ -74,32 +74,37 @@ in putty/doc do make $(Docmakeargs) in putty/windows do zip -k -j putty.zip `ls *.exe | grep -v puttytel` ../doc/putty.chm ../doc/putty.hlp ../doc/putty.cnt in putty/doc do zip puttydoc.zip *.html -deliver putty/windows/*.exe x86/$@ -deliver putty/windows/putty.zip x86/$@ -deliver putty/windows/Output/setup.exe x86/$(Ifilename) -deliver putty/doc/puttydoc.zip $@ -deliver putty/doc/putty.chm $@ -deliver putty/doc/putty.hlp $@ -deliver putty/doc/putty.cnt $@ -deliver putty/doc/puttydoc.txt $@ -deliver putty/doc/*.html htmldoc/$@ -deliver putty/putty-src.zip $@ -deliver putty/*.tar.gz $@ +# Deliver the actual PuTTY release directory into a subdir `putty'. +deliver putty/windows/*.exe putty/x86/$@ +deliver putty/windows/putty.zip putty/x86/$@ +deliver putty/windows/Output/setup.exe putty/x86/$(Ifilename) +deliver putty/doc/puttydoc.zip putty/$@ +deliver putty/doc/putty.chm putty/$@ +deliver putty/doc/putty.hlp putty/$@ +deliver putty/doc/putty.cnt putty/$@ +deliver putty/doc/puttydoc.txt putty/$@ +deliver putty/doc/*.html putty/htmldoc/$@ +deliver putty/putty-src.zip putty/$@ +deliver putty/*.tar.gz putty/$@ + +# Deliver the map files alongside the `proper' release deliverables. +deliver putty/windows/*.map maps-x86/$@ + +# Deliver sign.sh, so that whoever has just built PuTTY (the +# snapshot scripts or me, depending) can conveniently sign it with +# whatever key they want. +deliver putty/sign.sh $@ # Building the md5sums file is most easily done in the destination # directory. -in-dest . do md5sum `\find * -type f -print` > md5sums - -# Now deliver the map files _after_ we do that, so we don't md5sum -# them gratuitously. -deliver putty/windows/*.map maps-x86/$@ +in-dest putty do md5sum `\find * -type f -print` > md5sums # And construct .htaccess files. One in the top-level directory, # setting the MIME types for Windows help files and providing an # appropriate link to the source archive: -in-dest . do echo "AddType application/octet-stream .chm" >> .htaccess -in-dest . do echo "AddType application/octet-stream .hlp" >> .htaccess -in-dest . do echo "AddType application/octet-stream .cnt" >> .htaccess -in-dest . do set -- putty*.tar.gz; for k in '' .DSA .RSA; do echo RedirectMatch temp '(.*/)'putty.tar.gz$$k\$$ '$$1'"$$1$$k" >> .htaccess; done +in-dest putty do echo "AddType application/octet-stream .chm" >> .htaccess +in-dest putty do echo "AddType application/octet-stream .hlp" >> .htaccess +in-dest putty do echo "AddType application/octet-stream .cnt" >> .htaccess +in-dest putty do set -- putty*.tar.gz; for k in '' .DSA .RSA; do echo RedirectMatch temp '(.*/)'putty.tar.gz$$k\$$ '$$1'"$$1$$k" >> .htaccess; done # And one in the x86 directory, providing a link for the installer. -in-dest x86 do set -- putty*installer.exe; for k in '' .DSA .RSA; do echo RedirectMatch temp '(.*/)'putty-installer.exe$$k\$$ '$$1'"$$1$$k" >> .htaccess; done +in-dest putty/x86 do set -- putty*installer.exe; for k in '' .DSA .RSA; do echo RedirectMatch temp '(.*/)'putty-installer.exe$$k\$$ '$$1'"$$1$$k" >> .htaccess; done diff --git a/CHECKLST.txt b/CHECKLST.txt index ac5f3171..37b2de61 100644 --- a/CHECKLST.txt +++ b/CHECKLST.txt @@ -115,21 +115,22 @@ of the tag. within days of the release going out. - Build the release: `bob putty-0.XX RELEASE=0.XX'. This should - generate a basically valid release directory as `build.out'. + generate a basically valid release directory as + `build.out/putty', and provide link maps and sign.sh alongside + that in build.out. - Do a bit of checking that the release binaries basically work, report their version numbers accurately, and so on. Test the installer and the Unix source tarball. - - Move the release link maps out of the build directory and save - them somewhere more useful. Currently I keep these on ixion, in + - Save the link maps. Currently I keep these on ixion, in src/putty/local/maps-. - - Sign the release: type `./sign.sh build.out Releases', and enter - the passphrases a lot of times. + - Sign the release: in the `build.out' directory, type `./sign.sh + putty Releases', and enter the passphrases a lot of times. - Now the whole release directory should be present and correct. - Upload to ixion:www/putty/. + Upload it to ixion:www/putty/. - Do final checks on the release directory: + verify all the signatures: From 5d76e00dac9220e8798e4d0f17a4069a58ae1667 Mon Sep 17 00:00:00 2001 From: Simon Tatham Date: Sat, 10 Feb 2007 17:02:41 +0000 Subject: [PATCH 024/124] Avoid launching a session from the Default Settings, even if they do represent a launchable session, unless the user can be construed to have really meant it. This means: - starting up PuTTY when the Default Settings are launchable still brings up the config box, and you have to hit Open to actually launch that session - double-clicking on Default Settings from the config box will load them but not launch them. On the other hand: - explicitly loading the Default Settings on the command line using `-load' _does_ still launch them. [originally from svn r7265] --- config.c | 17 ++++++++++++----- unix/gtkwin.c | 3 ++- windows/window.c | 3 ++- 3 files changed, 16 insertions(+), 7 deletions(-) diff --git a/config.c b/config.c index d3e59aa5..128660ed 100644 --- a/config.c +++ b/config.c @@ -383,7 +383,7 @@ struct sessionsaver_data { */ static int load_selected_session(struct sessionsaver_data *ssd, char *savedsession, - void *dlg, Config *cfg) + void *dlg, Config *cfg, int *maybe_launch) { int i = dlg_listbox_index(ssd->listbox, dlg); int isdef; @@ -397,8 +397,12 @@ static int load_selected_session(struct sessionsaver_data *ssd, strncpy(savedsession, ssd->sesslist.sessions[i], SAVEDSESSION_LEN); savedsession[SAVEDSESSION_LEN-1] = '\0'; + if (maybe_launch) + *maybe_launch = TRUE; } else { savedsession[0] = '\0'; + if (maybe_launch) + *maybe_launch = FALSE; } dlg_refresh(NULL, dlg); /* Restore the selection, which might have been clobbered by @@ -464,6 +468,7 @@ static void sessionsaver_handler(union control *ctrl, void *dlg, dlg_listbox_select(ssd->listbox, dlg, top); } } else if (event == EVENT_ACTION) { + int mbl = FALSE; if (!ssd->midsession && (ctrl == ssd->listbox || (ssd->loadbutton && ctrl == ssd->loadbutton))) { @@ -474,8 +479,8 @@ static void sessionsaver_handler(union control *ctrl, void *dlg, * double-click on the list box _and_ that session * contains a hostname. */ - if (load_selected_session(ssd, savedsession, dlg, cfg) && - (ctrl == ssd->listbox && cfg_launchable(cfg))) { + if (load_selected_session(ssd, savedsession, dlg, cfg, &mbl) && + (mbl && ctrl == ssd->listbox && cfg_launchable(cfg))) { dlg_end(dlg, 1); /* it's all over, and succeeded */ } } else if (ctrl == ssd->savebutton) { @@ -533,12 +538,14 @@ static void sessionsaver_handler(union control *ctrl, void *dlg, if (dlg_last_focused(ctrl, dlg) == ssd->listbox && !cfg_launchable(cfg)) { Config cfg2; - if (!load_selected_session(ssd, savedsession, dlg, &cfg2)) { + int mbl = FALSE; + if (!load_selected_session(ssd, savedsession, dlg, + &cfg2, &mbl)) { dlg_beep(dlg); return; } /* If at this point we have a valid session, go! */ - if (*cfg2.host) { + if (mbl && cfg_launchable(&cfg2)) { *cfg = cfg2; /* structure copy */ cfg->remote_cmd_ptr = NULL; dlg_end(dlg, 1); diff --git a/unix/gtkwin.c b/unix/gtkwin.c index 430e2b58..816211a5 100644 --- a/unix/gtkwin.c +++ b/unix/gtkwin.c @@ -3486,7 +3486,8 @@ int pt_main(int argc, char **argv) cmdline_run_saved(&inst->cfg); - if (!cfg_launchable(&inst->cfg) && !cfgbox(&inst->cfg)) + if ((!loaded_session || !cfg_launchable(&inst->cfg)) && + !cfgbox(&inst->cfg)) exit(0); /* config box hit Cancel */ } diff --git a/windows/window.c b/windows/window.c index 477e6333..b7561cf2 100644 --- a/windows/window.c +++ b/windows/window.c @@ -539,7 +539,8 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) cmdline_run_saved(&cfg); - if (!cfg_launchable(&cfg) && !do_config()) { + if ((!loaded_session || !cfg_launchable(&cfg)) && + !do_config()) { cleanup_exit(0); } From 856ed4ae73576ef5725f1ea4999f40907ab13589 Mon Sep 17 00:00:00 2001 From: Simon Tatham Date: Sat, 10 Feb 2007 17:12:06 +0000 Subject: [PATCH 025/124] Since we're now able to cope with Default Settings describing a launchable session without getting confused by it, we can relax the restriction on storing a host name in DS, which has attracted a steady stream of complaints over the past six or seven years. [originally from svn r7266] --- config.c | 4 ++-- mac/macdlg.c | 6 +++--- putty.h | 8 ++++---- settings.c | 24 +++++++++--------------- 4 files changed, 18 insertions(+), 24 deletions(-) diff --git a/config.c b/config.c index 128660ed..d080cbe2 100644 --- a/config.c +++ b/config.c @@ -392,7 +392,7 @@ static int load_selected_session(struct sessionsaver_data *ssd, return 0; } isdef = !strcmp(ssd->sesslist.sessions[i], "Default Settings"); - load_settings(ssd->sesslist.sessions[i], !isdef, cfg); + load_settings(ssd->sesslist.sessions[i], cfg); if (!isdef) { strncpy(savedsession, ssd->sesslist.sessions[i], SAVEDSESSION_LEN); @@ -501,7 +501,7 @@ static void sessionsaver_handler(union control *ctrl, void *dlg, } } { - char *errmsg = save_settings(savedsession, !isdef, cfg); + char *errmsg = save_settings(savedsession, cfg); if (errmsg) { dlg_error_msg(dlg, errmsg); sfree(errmsg); diff --git a/mac/macdlg.c b/mac/macdlg.c index 0a7aebaa..0b28cf9f 100644 --- a/mac/macdlg.c +++ b/mac/macdlg.c @@ -237,7 +237,7 @@ static OSErr mac_opensessionfrom(FSSpec *fss) err = -9999; goto fail; } - load_open_settings(sesshandle, TRUE, &s->cfg); + load_open_settings(sesshandle, &s->cfg); close_settings_r(sesshandle); mac_startsession(s); @@ -321,7 +321,7 @@ void mac_savesession(void) assert(s->hasfile); sesshandle = open_settings_w_fsp(&s->savefile); if (sesshandle == NULL) return; /* XXX report error */ - save_open_settings(sesshandle, TRUE, &s->cfg); + save_open_settings(sesshandle, &s->cfg); close_settings_w(sesshandle); } @@ -342,7 +342,7 @@ void mac_savesessionas(void) } sesshandle = open_settings_w_fsp(&sfr.sfFile); if (sesshandle == NULL) return; /* XXX report error */ - save_open_settings(sesshandle, TRUE, &s->cfg); + save_open_settings(sesshandle, &s->cfg); close_settings_w(sesshandle); s->hasfile = TRUE; s->savefile = sfr.sfFile; diff --git a/putty.h b/putty.h index b003652c..b9f11ceb 100644 --- a/putty.h +++ b/putty.h @@ -777,10 +777,10 @@ void random_destroy_seed(void); /* * Exports from settings.c. */ -char *save_settings(char *section, int do_host, Config * cfg); -void save_open_settings(void *sesskey, int do_host, Config *cfg); -void load_settings(char *section, int do_host, Config * cfg); -void load_open_settings(void *sesskey, int do_host, Config *cfg); +char *save_settings(char *section, Config * cfg); +void save_open_settings(void *sesskey, Config *cfg); +void load_settings(char *section, Config * cfg); +void load_open_settings(void *sesskey, Config *cfg); void get_sesslist(struct sesslist *, int allocate); void do_defaults(char *, Config *); void registry_cleanup(void); diff --git a/settings.c b/settings.c index 0d3798aa..10f3573f 100644 --- a/settings.c +++ b/settings.c @@ -231,7 +231,7 @@ static void wprefs(void *sesskey, char *name, write_setting_s(sesskey, name, buf); } -char *save_settings(char *section, int do_host, Config * cfg) +char *save_settings(char *section, Config * cfg) { void *sesskey; char *errmsg; @@ -239,20 +239,18 @@ char *save_settings(char *section, int do_host, Config * cfg) sesskey = open_settings_w(section, &errmsg); if (!sesskey) return errmsg; - save_open_settings(sesskey, do_host, cfg); + save_open_settings(sesskey, cfg); close_settings_w(sesskey); return NULL; } -void save_open_settings(void *sesskey, int do_host, Config *cfg) +void save_open_settings(void *sesskey, Config *cfg) { int i; char *p; write_setting_i(sesskey, "Present", 1); - if (do_host) { - write_setting_s(sesskey, "HostName", cfg->host); - } + write_setting_s(sesskey, "HostName", cfg->host); write_setting_filename(sesskey, "LogFileName", cfg->logfilename); write_setting_i(sesskey, "LogType", cfg->logtype); write_setting_i(sesskey, "LogFileClash", cfg->logxfovr); @@ -447,16 +445,16 @@ void save_open_settings(void *sesskey, int do_host, Config *cfg) write_setting_i(sesskey, "SerialFlowControl", cfg->serflow); } -void load_settings(char *section, int do_host, Config * cfg) +void load_settings(char *section, Config * cfg) { void *sesskey; sesskey = open_settings_r(section); - load_open_settings(sesskey, do_host, cfg); + load_open_settings(sesskey, cfg); close_settings_r(sesskey); } -void load_open_settings(void *sesskey, int do_host, Config *cfg) +void load_open_settings(void *sesskey, Config *cfg) { int i; char prot[10]; @@ -466,11 +464,7 @@ void load_open_settings(void *sesskey, int do_host, Config *cfg) cfg->remote_cmd_ptr2 = NULL; cfg->ssh_nc_host[0] = '\0'; - if (do_host) { - gpps(sesskey, "HostName", "", cfg->host, sizeof(cfg->host)); - } else { - cfg->host[0] = '\0'; /* blank hostname */ - } + gpps(sesskey, "HostName", "", cfg->host, sizeof(cfg->host)); gppfile(sesskey, "LogFileName", &cfg->logfilename); gppi(sesskey, "LogType", 0, &cfg->logtype); gppi(sesskey, "LogFileClash", LGXF_ASK, &cfg->logxfovr); @@ -785,7 +779,7 @@ void load_open_settings(void *sesskey, int do_host, Config *cfg) void do_defaults(char *session, Config * cfg) { - load_settings(session, (session != NULL && *session), cfg); + load_settings(session, cfg); } static int sessioncmp(const void *av, const void *bv) From f947275c64f73a8ea84fe5dc535b1c10446633f7 Mon Sep 17 00:00:00 2001 From: Jacob Nevins Date: Sun, 11 Feb 2007 18:09:03 +0000 Subject: [PATCH 026/124] Typo. [originally from svn r7271] --- Buildscr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Buildscr b/Buildscr index 568196a8..df2244df 100644 --- a/Buildscr +++ b/Buildscr @@ -15,7 +15,7 @@ ifneq "$(MAKEARGS)" "" set Makeargs $(makeargs) $(MAKEARGS) # Set up the version string for the docs build. set Docmakeargs VERSION="PuTTY revision $(revision)" ifneq "$(RELEASE)" "" set Docmakeargs VERSION="PuTTY release $(RELEASE)" -ifneq "$(date)" "" set Docmakeaargs VERSION="PuTTY development snapshot $(date)" +ifneq "$(date)" "" set Docmakeargs VERSION="PuTTY development snapshot $(date)" # Set up the version string for the Unix source archive. set Unxver r$(revision) From c8ac23705d7c2707238ed8b9094c5b74c70432f5 Mon Sep 17 00:00:00 2001 From: Jacob Nevins Date: Sun, 11 Feb 2007 20:27:05 +0000 Subject: [PATCH 027/124] Note that htmlhelp.h from HTML Help Workshop works perfectly well with Cygwin. [originally from svn r7273] --- Recipe | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Recipe b/Recipe index 5c58d496..79ea3b0b 100644 --- a/Recipe +++ b/Recipe @@ -81,7 +81,8 @@ # # Note that this definition is always enabled in the Cygwin # build, since at the time of writing this is -# known not to be available in Cygwin. +# known not to be available in Cygwin (although you can use +# the htmlhelp.h supplied with HTML Help Workshop). # # - RCFL=/DNO_MANIFESTS (Windows only) # Disables inclusion of XML application manifests in the PuTTY From 3d78bf9b261b4bfd30ae00d5ba43744c4144c934 Mon Sep 17 00:00:00 2001 From: Jacob Nevins Date: Tue, 13 Feb 2007 22:57:19 +0000 Subject: [PATCH 028/124] It turns out that HH_INITIALIZE and HH_UNINITIALIZE are optional, and are for putting HTML Help into "single-threaded" mode. Furthermore, this requires extra work from the application (message pumping via HH_PRETRANSLATEMESSAGE). Thus, remove them and run Help in a secondary thread. This means that keyboard input into the Index and Search tabs now works. [originally from svn r7285] --- windows/winhelp.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/windows/winhelp.c b/windows/winhelp.c index cc36bee5..49a0fae6 100644 --- a/windows/winhelp.c +++ b/windows/winhelp.c @@ -21,7 +21,6 @@ static int help_has_contents; #ifndef NO_HTMLHELP typedef HWND (CALLBACK *htmlhelp_t)(HWND, LPCSTR, UINT, DWORD); static char *chm_path; -static DWORD html_help_cookie; static htmlhelp_t htmlhelp; #endif /* NO_HTMLHELP */ @@ -63,9 +62,7 @@ void init_help(void) if (!htmlhelp) FreeLibrary(dllHH); } - if (htmlhelp) - htmlhelp(NULL, NULL, HH_INITIALIZE, (DWORD)&html_help_cookie); - else + if (!htmlhelp) chm_path = NULL; } #endif /* NO_HTMLHELP */ @@ -73,10 +70,9 @@ void init_help(void) void shutdown_help(void) { -#ifndef NO_HTMLHELP - if (chm_path) - htmlhelp(NULL, NULL, HH_UNINITIALIZE, html_help_cookie); -#endif /* NO_HTMLHELP */ + /* Nothing to do currently. + * (If we were running HTML Help single-threaded, this is where we'd + * call HH_UNINITIALIZE.) */ } int has_help(void) From f634340275cf9a002cf64f04dfc73ee6da1df077 Mon Sep 17 00:00:00 2001 From: Jacob Nevins Date: Thu, 15 Feb 2007 23:27:29 +0000 Subject: [PATCH 029/124] LICENCE in the installer should have CP/M line endings. [originally from svn r7290] --- Buildscr | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Buildscr b/Buildscr index df2244df..4775b85f 100644 --- a/Buildscr +++ b/Buildscr @@ -56,6 +56,9 @@ in putty/windows do perl -i~ -pe 'BEGIN{$$a=shift@ARGV;}s/^(VersionInfoTextVersi in putty/windows do perl -i~ -pe 'BEGIN{$$a=shift@ARGV;}s/^(AppVersion=).*$$/$$1$$a/' '$(Iversion)' putty.iss in putty/windows do perl -i~ -pe 'BEGIN{$$a=shift@ARGV;$$a=~s/M//;}s/^(VersionInfoVersion=\d+\.\d+\.)\d+(\.\d+)\r?$$/$$1$$a$$2/' '$(Irev)' putty.iss +# Windowsify LICENCE, since it's going in the Windows installer. +in putty do perl -i~ -pe 'y/\015//d;s/$$/\015/' LICENCE + delegate windows # FIXME: Cygwin alternative? in putty/windows do cmd /c vcvars32 \& nmake -f Makefile.vc $(Makeargs) From 062b5ab3e412435b9c452b371b51beeaa026608e Mon Sep 17 00:00:00 2001 From: Simon Tatham Date: Fri, 16 Feb 2007 18:44:07 +0000 Subject: [PATCH 030/124] r7265 broke the legacy `putty @sessionname' construction, which I wouldn't care about except for the fact that it's still used to implement the Saved Sessions menu item in PuTTY and Pageant. [originally from svn r7291] [r7265 == 5d76e00dac9220e8798e4d0f17a4069a58ae1667] --- windows/window.c | 1 + 1 file changed, 1 insertion(+) diff --git a/windows/window.c b/windows/window.c index b7561cf2..cb110647 100644 --- a/windows/window.c +++ b/windows/window.c @@ -397,6 +397,7 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) if (!cfg_launchable(&cfg) && !do_config()) { cleanup_exit(0); } + loaded_session = TRUE; /* allow it to be launched directly */ } else if (*p == '&') { /* * An initial & means we've been given a command line From 8230ce9fa0c204ad9d0a27f2d3ecd524e9380018 Mon Sep 17 00:00:00 2001 From: Jacob Nevins Date: Sat, 17 Feb 2007 17:44:24 +0000 Subject: [PATCH 031/124] Unbreak "Duplicate session" on Windows, in a similar way to r7291. [originally from svn r7292] [r7291 == 062b5ab3e412435b9c452b371b51beeaa026608e] --- windows/window.c | 1 + 1 file changed, 1 insertion(+) diff --git a/windows/window.c b/windows/window.c index cb110647..ceeaa767 100644 --- a/windows/window.c +++ b/windows/window.c @@ -413,6 +413,7 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) cfg = *cp; UnmapViewOfFile(cp); CloseHandle(filemap); + loaded_session = TRUE; } else if (!do_config()) { cleanup_exit(0); } From f69a0cf005dab818839bdcbbe0c8a6cdeead7023 Mon Sep 17 00:00:00 2001 From: Ben Harris Date: Sat, 17 Feb 2007 22:15:57 +0000 Subject: [PATCH 032/124] ssh2_set_window checks whether the channel is being closed, so there's no need to check that before calling it. [originally from svn r7293] --- ssh.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ssh.c b/ssh.c index d4d3d06a..1d17882e 100644 --- a/ssh.c +++ b/ssh.c @@ -8927,8 +8927,7 @@ static void ssh_unthrottle(void *handle, int bufsize) ssh1_throttle(ssh, -1); } } else { - if (ssh->mainchan && ssh->mainchan->closes == 0) - ssh2_set_window(ssh->mainchan, OUR_V2_WINSIZE - bufsize); + ssh2_set_window(ssh->mainchan, OUR_V2_WINSIZE - bufsize); } } From 3ffd1fbe38f2bece92498334bd0949dd1b28ebfe Mon Sep 17 00:00:00 2001 From: Ben Harris Date: Sat, 17 Feb 2007 22:33:11 +0000 Subject: [PATCH 033/124] Use preprocessor trickery to make the signal translation mechanism a little less hideous. The output of the preprocessor should be basically unchanged. [originally from svn r7294] --- ssh.c | 56 +++++++++++++++++--------------------------------------- 1 file changed, 17 insertions(+), 39 deletions(-) diff --git a/ssh.c b/ssh.c index 1d17882e..bcc1dd1b 100644 --- a/ssh.c +++ b/ssh.c @@ -6398,71 +6398,49 @@ static void ssh2_msg_channel_request(Ssh ssh, struct Packet *pktin) if (0) ; +#define TRANSLATE_SIGNAL(s) \ + else if (siglen == lenof(#s)-1 && !memcmp(sig, #s, siglen)) \ + ssh->exitcode = 128 + SIG ## s #ifdef SIGABRT - else if (siglen == lenof("ABRT")-1 && - !memcmp(sig, "ABRT", siglen)) - ssh->exitcode = 128 + SIGABRT; + TRANSLATE_SIGNAL(ABRT); #endif #ifdef SIGALRM - else if (siglen == lenof("ALRM")-1 && - !memcmp(sig, "ALRM", siglen)) - ssh->exitcode = 128 + SIGALRM; + TRANSLATE_SIGNAL(ALRM); #endif #ifdef SIGFPE - else if (siglen == lenof("FPE")-1 && - !memcmp(sig, "FPE", siglen)) - ssh->exitcode = 128 + SIGFPE; + TRANSLATE_SIGNAL(FPE); #endif #ifdef SIGHUP - else if (siglen == lenof("HUP")-1 && - !memcmp(sig, "HUP", siglen)) - ssh->exitcode = 128 + SIGHUP; + TRANSLATE_SIGNAL(HUP); #endif #ifdef SIGILL - else if (siglen == lenof("ILL")-1 && - !memcmp(sig, "ILL", siglen)) - ssh->exitcode = 128 + SIGILL; + TRANSLATE_SIGNAL(ILL); #endif #ifdef SIGINT - else if (siglen == lenof("INT")-1 && - !memcmp(sig, "INT", siglen)) - ssh->exitcode = 128 + SIGINT; + TRANSLATE_SIGNAL(INT); #endif #ifdef SIGKILL - else if (siglen == lenof("KILL")-1 && - !memcmp(sig, "KILL", siglen)) - ssh->exitcode = 128 + SIGKILL; + TRANSLATE_SIGNAL(KILL); #endif #ifdef SIGPIPE - else if (siglen == lenof("PIPE")-1 && - !memcmp(sig, "PIPE", siglen)) - ssh->exitcode = 128 + SIGPIPE; + TRANSLATE_SIGNAL(PIPE); #endif #ifdef SIGQUIT - else if (siglen == lenof("QUIT")-1 && - !memcmp(sig, "QUIT", siglen)) - ssh->exitcode = 128 + SIGQUIT; + TRANSLATE_SIGNAL(QUIT); #endif #ifdef SIGSEGV - else if (siglen == lenof("SEGV")-1 && - !memcmp(sig, "SEGV", siglen)) - ssh->exitcode = 128 + SIGSEGV; + TRANSLATE_SIGNAL(SEGV); #endif #ifdef SIGTERM - else if (siglen == lenof("TERM")-1 && - !memcmp(sig, "TERM", siglen)) - ssh->exitcode = 128 + SIGTERM; + TRANSLATE_SIGNAL(TERM); #endif #ifdef SIGUSR1 - else if (siglen == lenof("USR1")-1 && - !memcmp(sig, "USR1", siglen)) - ssh->exitcode = 128 + SIGUSR1; + TRANSLATE_SIGNAL(USR1); #endif #ifdef SIGUSR2 - else if (siglen == lenof("USR2")-1 && - !memcmp(sig, "USR2", siglen)) - ssh->exitcode = 128 + SIGUSR2; + TRANSLATE_SIGNAL(USR2); #endif +#undef TRANSLATE_SIGNAL else ssh->exitcode = 128; } From c5374da822d48a28eb28adb18320d420044976b5 Mon Sep 17 00:00:00 2001 From: Jacob Nevins Date: Sun, 18 Feb 2007 14:02:39 +0000 Subject: [PATCH 034/124] Ctrl-Break now sends a Break signal (previously it was equivalent to Ctrl-C). [originally from svn r7295] [this svn revision also touched putty-wishlist] --- doc/using.but | 3 +++ unix/gtkwin.c | 9 ++++----- windows/window.c | 11 ++++++----- 3 files changed, 13 insertions(+), 10 deletions(-) diff --git a/doc/using.but b/doc/using.but index 17fdce5f..adf5ee9a 100644 --- a/doc/using.but +++ b/doc/using.but @@ -128,6 +128,9 @@ connection in addition to normal data. Their precise effect is usually up to the server. Currently only Telnet, SSH, and serial connections have special commands. +The \q{break} signal can also be invoked from the keyboard with +\i{Ctrl-Break}. + The following \I{Telnet special commands}special commands are available in Telnet: diff --git a/unix/gtkwin.c b/unix/gtkwin.c index 816211a5..03ecc35a 100644 --- a/unix/gtkwin.c +++ b/unix/gtkwin.c @@ -663,13 +663,12 @@ gint key_event(GtkWidget *widget, GdkEventKey *event, gpointer data) end = 2; } - /* Control-Break is the same as Control-C */ + /* Control-Break sends a Break special to the backend */ if (event->keyval == GDK_Break && (event->state & GDK_CONTROL_MASK)) { - output[1] = '\003'; - use_ucsoutput = FALSE; - end = 2; - special = TRUE; + if (inst->back) + inst->back->special(inst->backhandle, TS_BRK); + return TRUE; } /* We handle Return ourselves, because it needs to be flagged as diff --git a/windows/window.c b/windows/window.c index ceeaa767..f654d2ad 100644 --- a/windows/window.c +++ b/windows/window.c @@ -3566,8 +3566,9 @@ int char_width(Context ctx, int uc) { /* * Translate a WM_(SYS)?KEY(UP|DOWN) message into a string of ASCII - * codes. Returns number of bytes used or zero to drop the message - * or -1 to forward the message to windows. + * codes. Returns number of bytes used, zero to drop the message, + * -1 to forward the message to Windows, or another negative number + * to indicate a NUL-terminated "special" string. */ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, unsigned char *output) @@ -3987,9 +3988,9 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, return p - output; } if (wParam == VK_CANCEL && shift_state == 2) { /* Ctrl-Break */ - *p++ = 3; - *p++ = 0; - return -2; + if (back) + back->special(backhandle, TS_BRK); + return 0; } if (wParam == VK_PAUSE) { /* Break/Pause */ *p++ = 26; From 4d51d0d38017084e56ac8b0e6b264923306993b7 Mon Sep 17 00:00:00 2001 From: Simon Tatham Date: Sun, 18 Feb 2007 15:59:38 +0000 Subject: [PATCH 035/124] Bring the OS X front end up to date with recent changes to the main code base. [originally from svn r7296] --- macosx/osxclass.h | 2 ++ macosx/osxdlg.m | 2 +- macosx/osxwin.m | 18 +++++++++++++++++- testback.c | 10 +++++----- 4 files changed, 25 insertions(+), 7 deletions(-) diff --git a/macosx/osxclass.h b/macosx/osxclass.h index e147bd99..e79290df 100644 --- a/macosx/osxclass.h +++ b/macosx/osxclass.h @@ -64,10 +64,12 @@ struct alert_queue { - (void)doText:(wchar_t *)text len:(int)len x:(int)x y:(int)y attr:(unsigned long)attr lattr:(int)lattr; - (int)fromBackend:(const char *)data len:(int)len isStderr:(int)is_stderr; +- (int)fromBackendUntrusted:(const char *)data len:(int)len; - (void)startAlert:(NSAlert *)alert withCallback:(void (*)(void *, int))callback andCtx:(void *)ctx; - (void)endSession:(int)clean; - (void)notifyRemoteExit; +- (Terminal *)term; @end /* diff --git a/macosx/osxdlg.m b/macosx/osxdlg.m index 094c9555..295b6755 100644 --- a/macosx/osxdlg.m +++ b/macosx/osxdlg.m @@ -126,7 +126,7 @@ ctrlbox = ctrl_new_box(); setup_config_box(ctrlbox, FALSE /*midsession*/, aCfg.protocol, 0 /* protcfginfo */); - unix_setup_config_box(ctrlbox, FALSE /*midsession*/); + unix_setup_config_box(ctrlbox, FALSE /*midsession*/, aCfg.protocol); cfg = aCfg; /* structure copy */ diff --git a/macosx/osxwin.m b/macosx/osxwin.m index a4fac71d..361f548f 100644 --- a/macosx/osxwin.m +++ b/macosx/osxwin.m @@ -794,6 +794,11 @@ return term_data(term, is_stderr, data, len); } +- (int)fromBackendUntrusted:(const char *)data len:(int)len +{ + return term_data_untrusted(term, data, len); +} + - (void)startAlert:(NSAlert *)alert withCallback:(void (*)(void *, int))callback andCtx:(void *)ctx { @@ -885,6 +890,11 @@ // FIXME: else show restart menu item } +- (Terminal *)term +{ + return term; +} + @end int from_backend(void *frontend, int is_stderr, const char *data, int len) @@ -893,6 +903,12 @@ int from_backend(void *frontend, int is_stderr, const char *data, int len) return [win fromBackend:data len:len isStderr:is_stderr]; } +int from_backend_untrusted(void *frontend, const char *data, int len) +{ + SessionWindow *win = (SessionWindow *)frontend; + return [win fromBackendUntrusted:data len:len]; +} + int get_userpass_input(prompts_t *p, unsigned char *in, int inlen) { SessionWindow *win = (SessionWindow *)p->frontend; @@ -922,7 +938,7 @@ void ldisc_update(void *frontend, int echo, int edit) char *get_ttymode(void *frontend, const char *mode) { - SessionWindow *win = (SessionWindow *)ctx; + SessionWindow *win = (SessionWindow *)frontend; Terminal *term = [win term]; return term_get_ttymode(term, mode); } diff --git a/testback.c b/testback.c index ac0d2067..159cdc54 100644 --- a/testback.c +++ b/testback.c @@ -46,7 +46,7 @@ static int null_sendbuffer(void *); static void null_size(void *, int, int); static void null_special(void *, Telnet_Special); static const struct telnet_special *null_get_specials(void *handle); -static Socket null_socket(void *); +static int null_connected(void *); static int null_exitcode(void *); static int null_sendok(void *); static int null_ldisc(void *, int); @@ -57,14 +57,14 @@ static int null_cfg_info(void *); Backend null_backend = { null_init, null_free, null_reconfig, null_send, null_sendbuffer, null_size, - null_special, null_get_specials, null_socket, null_exitcode, null_sendok, + null_special, null_get_specials, null_connected, null_exitcode, null_sendok, null_ldisc, null_provide_ldisc, null_provide_logctx, null_unthrottle, null_cfg_info, 0 }; Backend loop_backend = { loop_init, loop_free, null_reconfig, loop_send, null_sendbuffer, null_size, - null_special, null_get_specials, null_socket, null_exitcode, null_sendok, + null_special, null_get_specials, null_connected, null_exitcode, null_sendok, null_ldisc, null_provide_ldisc, null_provide_logctx, null_unthrottle, null_cfg_info, 0 }; @@ -134,9 +134,9 @@ static const struct telnet_special *null_get_specials (void *handle) { return NULL; } -static Socket null_socket(void *handle) { +static int null_connected(void *handle) { - return NULL; + return 0; } static int null_exitcode(void *handle) { From 31382c02df2a1c888822ed52afa3d9408c2721ab Mon Sep 17 00:00:00 2001 From: Jacob Nevins Date: Sun, 18 Feb 2007 19:50:41 +0000 Subject: [PATCH 036/124] Allow dlg_listbox_index() to be called on multi-selection list boxes. [originally from svn r7297] --- windows/winctrls.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/windows/winctrls.c b/windows/winctrls.c index 9e8e0e7d..a618a696 100644 --- a/windows/winctrls.c +++ b/windows/winctrls.c @@ -2194,8 +2194,13 @@ int dlg_listbox_index(union control *ctrl, void *dlg) struct dlgparam *dp = (struct dlgparam *)dlg; struct winctrl *c = dlg_findbyctrl(dp, ctrl); int msg, ret; - assert(c && c->ctrl->generic.type == CTRL_LISTBOX && - !c->ctrl->listbox.multisel); + assert(c && c->ctrl->generic.type == CTRL_LISTBOX); + if (c->ctrl->listbox.multisel) { + assert(c->ctrl->listbox.height != 0); /* not combo box */ + ret = SendDlgItemMessage(dp->hwnd, c->base_id+1, LB_GETSELCOUNT, 0, 0); + if (ret == LB_ERR || ret > 1) + return -1; + } msg = (c->ctrl->listbox.height != 0 ? LB_GETCURSEL : CB_GETCURSEL); ret = SendDlgItemMessage(dp->hwnd, c->base_id+1, msg, 0, 0); if (ret == LB_ERR) From 0407960951fa2bb3b9c1a3976201f2176c36c23e Mon Sep 17 00:00:00 2001 From: Jacob Nevins Date: Sun, 18 Feb 2007 19:56:16 +0000 Subject: [PATCH 037/124] In controls where a list of entries is manipulated by Add/Remove buttons (SSH tunnels, TTY modes, and environment variables), when the Remove button is pressed, populate the edit controls from the entry that has just been deleted. Several users have requested this, as it makes editing an entry easier (read- modify-write) in the cases where order is unimportant, and also provides a degree of undo-ability. [originally from svn r7298] --- config.c | 79 ++++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 74 insertions(+), 5 deletions(-) diff --git a/config.c b/config.c index d080cbe2..477dc4d4 100644 --- a/config.c +++ b/config.c @@ -799,7 +799,29 @@ static void ttymodes_handler(union control *ctrl, void *dlg, char *p = cfg->ttymodes; int i = 0, len = lenof(cfg->ttymodes); while (*p) { + int multisel = dlg_listbox_index(td->listbox, dlg) < 0; if (dlg_listbox_issel(td->listbox, dlg, i)) { + if (!multisel) { + /* Populate controls with entry we're about to + * delete, for ease of editing. + * (If multiple entries were selected, don't + * touch the controls.) */ + char *val = strchr(p, '\t'); + if (val) { + int ind = 0; + val++; + while (ttymodes[ind]) { + if (strlen(ttymodes[ind]) == val-p-1 && + !strncmp(ttymodes[ind], p, val-p-1)) + break; + ind++; + } + dlg_listbox_select(td->modelist, dlg, ind); + dlg_radiobutton_set(td->valradio, dlg, + (*val == 'V')); + dlg_editbox_set(td->valbox, dlg, val+1); + } + } memmove(p, p+strlen(p)+1, len - (strlen(p)+1)); i++; continue; @@ -873,7 +895,7 @@ static void environ_handler(union control *ctrl, void *dlg, if (i < 0) { dlg_beep(dlg); } else { - char *p, *q; + char *p, *q, *str; dlg_listbox_del(ed->listbox, dlg, i); p = cfg->environmt; @@ -888,8 +910,20 @@ static void environ_handler(union control *ctrl, void *dlg, q = p; if (!*p) goto disaster; - while (*p) - p++; + /* Populate controls with the entry we're about to delete + * for ease of editing */ + str = p; + p = strchr(p, '\t'); + if (!p) + goto disaster; + *p = '\0'; + dlg_editbox_set(ed->varbox, dlg, str); + p++; + str = p; + dlg_editbox_set(ed->valbox, dlg, str); + p = strchr(p, '\0'); + if (!p) + goto disaster; p++; while (*p) { while (*p) @@ -1002,7 +1036,8 @@ static void portfwd_handler(union control *ctrl, void *dlg, if (i < 0) dlg_beep(dlg); else { - char *p, *q; + char *p, *q, *src, *dst; + char dir; dlg_listbox_del(pfd->listbox, dlg, i); p = cfg->portfwd; @@ -1017,8 +1052,42 @@ static void portfwd_handler(union control *ctrl, void *dlg, q = p; if (!*p) goto disaster2; - while (*p) + /* Populate the controls with the entry we're about to + * delete, for ease of editing. */ + { + static const char *const afs = "A46"; + char *afp = strchr(afs, *p); + int idx = afp ? afp-afs : 0; + if (afp) + p++; +#ifndef NO_IPV6 + dlg_radiobutton_set(pfd->addressfamily, dlg, idx); +#endif + } + { + static const char *const dirs = "LRD"; + dir = *p; + dlg_radiobutton_set(pfd->direction, dlg, + strchr(dirs, dir) - dirs); + } + p++; + if (dir != 'D') { + src = p; + p = strchr(p, '\t'); + if (!p) + goto disaster2; + *p = '\0'; p++; + dst = p; + } else { + src = p; + dst = ""; + } + p = strchr(p, '\0'); + if (!p) + goto disaster2; + dlg_editbox_set(pfd->sourcebox, dlg, src); + dlg_editbox_set(pfd->destbox, dlg, dst); p++; while (*p) { while (*p) From 0f013108c31d7f4ea09582dfe9ff06ccfd6a4516 Mon Sep 17 00:00:00 2001 From: Jacob Nevins Date: Sun, 18 Feb 2007 22:05:45 +0000 Subject: [PATCH 038/124] This reordering of the Unix Makefiles (requested by Michael Shigorin) allows use of -Wl,--as-needed. [originally from svn r7299] --- mkfiles.pl | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/mkfiles.pl b/mkfiles.pl index 94a83c7c..d43551b3 100755 --- a/mkfiles.pl +++ b/mkfiles.pl @@ -929,8 +929,8 @@ if (defined $makefiles{'gtk'}) { (join " ", map {"-I$dirpfx$_"} @srcdirs) . " `gtk-config --cflags`"). " -D _FILE_OFFSET_BITS=64\n". - "XLDFLAGS = `gtk-config --libs`\n". - "ULDFLAGS =#\n". + "XLDFLAGS = \$(LDFLAGS) `gtk-config --libs`\n". + "ULDFLAGS = \$(LDFLAGS)\n". "INSTALL=install\n", "INSTALL_PROGRAM=\$(INSTALL)\n", "INSTALL_DATA=\$(INSTALL)\n", @@ -952,8 +952,8 @@ if (defined $makefiles{'gtk'}) { $objstr = &objects($p, "X.o", undef, undef); print &splitline($prog . ": " . $objstr), "\n"; $libstr = &objects($p, undef, undef, "-lX"); - print &splitline("\t\$(CC)" . $mw . " \$(${type}LDFLAGS) -o \$@ " . - $objstr . " $libstr", 69), "\n\n"; + print &splitline("\t\$(CC)" . $mw . " -o \$@ " . + $objstr . " \$(${type}LDFLAGS) $libstr", 69), "\n\n"; } foreach $d (&deps("X.o", undef, $dirpfx, "/", "gtk")) { if ($forceobj{$d->{obj_orig}}) { @@ -1015,8 +1015,8 @@ if (defined $makefiles{'ac'}) { $objstr = &objects($p, "X.o", undef, undef); print &splitline($prog . ": " . $objstr), "\n"; $libstr = &objects($p, undef, undef, "-lX"); - print &splitline("\t\$(CC)" . $mw . " \$(${type}LDFLAGS) -o \$@ " . - $objstr . " $libstr", 69), "\n\n"; + print &splitline("\t\$(CC)" . $mw . " -o \$@ " . + $objstr . " \$(${type}LDFLAGS) $libstr", 69), "\n\n"; } foreach $d (&deps("X.o", undef, $dirpfx, "/", "gtk")) { if ($forceobj{$d->{obj_orig}}) { From b897c90dd3f0be2f951ddd78741bc8ee5f0d9f01 Mon Sep 17 00:00:00 2001 From: Simon Tatham Date: Sat, 24 Feb 2007 13:36:11 +0000 Subject: [PATCH 039/124] Gareth pointed out yesterday that the Unix terminal front end treats BELL_DISABLED as BELL_DEFAULT. How embarrassing. [originally from svn r7316] --- unix/gtkwin.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unix/gtkwin.c b/unix/gtkwin.c index 03ecc35a..311cf38a 100644 --- a/unix/gtkwin.c +++ b/unix/gtkwin.c @@ -1867,7 +1867,7 @@ void sys_cursor(void *frontend, int x, int y) */ void do_beep(void *frontend, int mode) { - if (mode != BELL_VISUAL) + if (mode == BELL_DEFAULT) gdk_beep(); } From fe1909e0e21a4f6e619eb93c883cdc28d4dbf6ad Mon Sep 17 00:00:00 2001 From: Jacob Nevins Date: Sat, 24 Feb 2007 22:43:57 +0000 Subject: [PATCH 040/124] "-noagent" and friends should be marked SAVEABLE, to ensure they're not clobbered by "-load". [originally from svn r7320] --- cmdline.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cmdline.c b/cmdline.c index 79f29816..3e890b78 100644 --- a/cmdline.c +++ b/cmdline.c @@ -322,12 +322,14 @@ int cmdline_process_param(char *p, char *value, int need_save, Config *cfg) !strcmp(p, "-pageant")) { RETURN(1); UNAVAILABLE_IN(TOOLTYPE_NONNETWORK); + SAVEABLE(0); cfg->tryagent = TRUE; } if (!strcmp(p, "-noagent") || !strcmp(p, "-nopagent") || !strcmp(p, "-nopageant")) { RETURN(1); UNAVAILABLE_IN(TOOLTYPE_NONNETWORK); + SAVEABLE(0); cfg->tryagent = FALSE; } From befd797f97078d7e5486fce9c715f12c3be0825b Mon Sep 17 00:00:00 2001 From: Jacob Nevins Date: Sun, 25 Feb 2007 00:50:24 +0000 Subject: [PATCH 041/124] Since r7265, a user could not launch a PuTTY session to a specific host by simply specifying a hostname on the command line -- this would bring up the config dialog. Use a slightly more sophisticated notion of whether the user meant to launch a session. [originally from svn r7321] [r7265 == 5d76e00dac9220e8798e4d0f17a4069a58ae1667] --- unix/gtkwin.c | 18 +++++++++++++----- unix/unix.h | 2 +- unix/uxpterm.c | 2 +- unix/uxputty.c | 4 +++- windows/window.c | 14 ++++++++++---- 5 files changed, 28 insertions(+), 12 deletions(-) diff --git a/unix/gtkwin.c b/unix/gtkwin.c index 311cf38a..388d4225 100644 --- a/unix/gtkwin.c +++ b/unix/gtkwin.c @@ -2408,7 +2408,7 @@ static void help(FILE *fp) { } } -int do_cmdline(int argc, char **argv, int do_everything, +int do_cmdline(int argc, char **argv, int do_everything, int *allow_launch, struct gui_data *inst, Config *cfg) { int err = 0; @@ -2614,7 +2614,8 @@ int do_cmdline(int argc, char **argv, int do_everything, exit(1); } else if(p[0] != '-' && (!do_everything || - process_nonoption_arg(p, cfg))) { + process_nonoption_arg(p, cfg, + allow_launch))) { /* do nothing */ } else { @@ -3477,15 +3478,22 @@ int pt_main(int argc, char **argv) /* Splatter this argument so it doesn't clutter a ps listing */ memset(argv[1], 0, strlen(argv[1])); } else { - if (do_cmdline(argc, argv, 0, inst, &inst->cfg)) + /* By default, we bring up the config dialog, rather than launching + * a session. This gets set to TRUE if something happens to change + * that (e.g., a hostname is specified on the command-line). */ + int allow_launch = FALSE; + if (do_cmdline(argc, argv, 0, &allow_launch, inst, &inst->cfg)) exit(1); /* pre-defaults pass to get -class */ do_defaults(NULL, &inst->cfg); - if (do_cmdline(argc, argv, 1, inst, &inst->cfg)) + if (do_cmdline(argc, argv, 1, &allow_launch, inst, &inst->cfg)) exit(1); /* post-defaults, do everything */ cmdline_run_saved(&inst->cfg); - if ((!loaded_session || !cfg_launchable(&inst->cfg)) && + if (loaded_session) + allow_launch = TRUE; + + if ((!allow_launch || !cfg_launchable(&inst->cfg)) && !cfgbox(&inst->cfg)) exit(0); /* config box hit Cancel */ } diff --git a/unix/unix.h b/unix/unix.h index 9c52de6d..90faf5a6 100644 --- a/unix/unix.h +++ b/unix/unix.h @@ -80,7 +80,7 @@ int reallyclose(void *frontend); /* Things pterm.c needs from {ptermm,uxputty}.c */ char *make_default_wintitle(char *hostname); -int process_nonoption_arg(char *arg, Config *cfg); +int process_nonoption_arg(char *arg, Config *cfg, int *allow_launch); /* pterm.c needs this special function in xkeysym.c */ int keysym_to_unicode(int keysym); diff --git a/unix/uxpterm.c b/unix/uxpterm.c index 50fdad92..c20c14a2 100644 --- a/unix/uxpterm.c +++ b/unix/uxpterm.c @@ -33,7 +33,7 @@ void cleanup_exit(int code) exit(code); } -int process_nonoption_arg(char *arg, Config *cfg) +int process_nonoption_arg(char *arg, Config *cfg, int *allow_launch) { return 0; /* pterm doesn't have any. */ } diff --git a/unix/uxputty.c b/unix/uxputty.c index 0abc1ff8..54e0d71b 100644 --- a/unix/uxputty.c +++ b/unix/uxputty.c @@ -53,7 +53,7 @@ static int got_host = 0; const int use_event_log = 1, new_session = 1, saved_sessions = 1; -int process_nonoption_arg(char *arg, Config *cfg) +int process_nonoption_arg(char *arg, Config *cfg, int *allow_launch) { char *p, *q = arg; @@ -104,6 +104,8 @@ int process_nonoption_arg(char *arg, Config *cfg) cfg->host[sizeof(cfg->host) - 1] = '\0'; got_host = 1; } + if (got_host) + *allow_launch = TRUE; return 1; } diff --git a/windows/window.c b/windows/window.c index f654d2ad..1df5feeb 100644 --- a/windows/window.c +++ b/windows/window.c @@ -361,6 +361,10 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) { char *p; int got_host = 0; + /* By default, we bring up the config dialog, rather than launching + * a session. This gets set to TRUE if something happens to change + * that (e.g., a hostname is specified on the command-line). */ + int allow_launch = FALSE; default_protocol = be_default_protocol; /* Find the appropriate default port. */ @@ -397,7 +401,7 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) if (!cfg_launchable(&cfg) && !do_config()) { cleanup_exit(0); } - loaded_session = TRUE; /* allow it to be launched directly */ + allow_launch = TRUE; /* allow it to be launched directly */ } else if (*p == '&') { /* * An initial & means we've been given a command line @@ -413,10 +417,10 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) cfg = *cp; UnmapViewOfFile(cp); CloseHandle(filemap); - loaded_session = TRUE; } else if (!do_config()) { cleanup_exit(0); } + allow_launch = TRUE; } else { /* * Otherwise, break up the command line and deal with @@ -541,8 +545,10 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) cmdline_run_saved(&cfg); - if ((!loaded_session || !cfg_launchable(&cfg)) && - !do_config()) { + if (loaded_session || got_host) + allow_launch = TRUE; + + if ((!allow_launch || !cfg_launchable(&cfg)) && !do_config()) { cleanup_exit(0); } From 917b32dd8b3be37a70d282865a197ae7db691f19 Mon Sep 17 00:00:00 2001 From: Jacob Nevins Date: Sun, 25 Feb 2007 00:51:38 +0000 Subject: [PATCH 042/124] Delay evaluating the "-pw" option, so we can criticise the user's choice of backend, bailing out if anything other than SSH is in use. [originally from svn r7322] --- cmdline.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/cmdline.c b/cmdline.c index 3e890b78..bdfa1aa2 100644 --- a/cmdline.c +++ b/cmdline.c @@ -315,7 +315,14 @@ int cmdline_process_param(char *p, char *value, int need_save, Config *cfg) if (!strcmp(p, "-pw")) { RETURN(2); UNAVAILABLE_IN(TOOLTYPE_NONNETWORK); - cmdline_password = value; + SAVEABLE(1); + /* We delay evaluating this until after the protocol is decided, + * so that we can warn if it's of no use with the selected protocol */ + if (cfg->protocol != PROT_SSH) + cmdline_error("The -pw option can only be used with the " + "SSH protocol"); + else + cmdline_password = value; } if (!strcmp(p, "-agent") || !strcmp(p, "-pagent") || From 01612d38e4bb0b5d4bd8d9c2edc7e6ca819426aa Mon Sep 17 00:00:00 2001 From: Jacob Nevins Date: Sun, 25 Feb 2007 02:15:20 +0000 Subject: [PATCH 043/124] Attempt to scrub -pw's argument in argv[], to make it less obvious. [originally from svn r7323] --- cmdline.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/cmdline.c b/cmdline.c index bdfa1aa2..033df64a 100644 --- a/cmdline.c +++ b/cmdline.c @@ -319,10 +319,15 @@ int cmdline_process_param(char *p, char *value, int need_save, Config *cfg) /* We delay evaluating this until after the protocol is decided, * so that we can warn if it's of no use with the selected protocol */ if (cfg->protocol != PROT_SSH) - cmdline_error("The -pw option can only be used with the " + cmdline_error("the -pw option can only be used with the " "SSH protocol"); - else - cmdline_password = value; + else { + cmdline_password = dupstr(value); + /* Assuming that `value' is directly from argv, make a good faith + * attempt to trample it, to stop it showing up in `ps' output + * on Unix-like systems. Not guaranteed, of course. */ + memset(value, 0, strlen(value)); + } } if (!strcmp(p, "-agent") || !strcmp(p, "-pagent") || From a3ff37885dfe3705230f4349d770c7d83a212af0 Mon Sep 17 00:00:00 2001 From: Jacob Nevins Date: Wed, 28 Feb 2007 21:30:06 +0000 Subject: [PATCH 044/124] Prepend \\.\ to configured serial line string, to allow easy access to ports above COM9. [originally from svn r7345] [this svn revision also touched putty-wishlist] --- doc/config.but | 4 +--- windows/winser.c | 35 +++++++++++++++++++++++++++++++++-- 2 files changed, 34 insertions(+), 5 deletions(-) diff --git a/doc/config.but b/doc/config.but index 29259572..3b744356 100644 --- a/doc/config.but +++ b/doc/config.but @@ -2976,9 +2976,7 @@ serial line you want PuTTY to talk to, if your computer has more than one serial port. On Windows, the first serial line is called \i\cw{COM1}, and if there -is a second it is called \cw{COM2}, and so on. A serial line with -a name other than \cw{COM1} to \cw{COM9} can be specified by prefixing -its name with \cw{\\\\.\\} - for instance, \cw{\\\\.\\COM10}. +is a second it is called \cw{COM2}, and so on. This configuration setting is also visible on the Session panel, where it replaces the \q{Host Name} box (see \k{config-hostname}) if diff --git a/windows/winser.c b/windows/winser.c index cd31b5af..9e6415e3 100644 --- a/windows/winser.c +++ b/windows/winser.c @@ -221,8 +221,39 @@ static const char *serial_init(void *frontend_handle, void **backend_handle, logevent(serial->frontend, msg); } - serport = CreateFile(cfg->serline, GENERIC_READ | GENERIC_WRITE, 0, NULL, - OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); + { + /* + * Munge the string supplied by the user into a Windows filename. + * + * Windows supports opening a few "legacy" devices (including + * COM1-9) by specifying their names verbatim as a filename to + * open. (Thus, no files can ever have these names. See + * + * ("Naming a File") for the complete list of reserved names.) + * + * However, this doesn't let you get at devices COM10 and above. + * For that, you need to specify a filename like "\\.\COM10". + * This is also necessary for special serial and serial-like + * devices such as \\.\WCEUSBSH001. It also works for the "legacy" + * names, so you can do \\.\COM1 (verified as far back as Win95). + * See + * (CreateFile() docs). + * + * So, we believe that prepending "\\.\" should always be the + * Right Thing. However, just in case someone finds something to + * talk to that doesn't exist under there, if the serial line + * contains a backslash, we use it verbatim. (This also lets + * existing configurations using \\.\ continue working.) + */ + char *serfilename = + dupprintf("%s%s", + strchr(cfg->serline, '\\') ? "" : "\\\\.\\", + cfg->serline); + serport = CreateFile(serfilename, GENERIC_READ | GENERIC_WRITE, 0, NULL, + OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); + sfree(serfilename); + } + if (serport == INVALID_HANDLE_VALUE) return "Unable to open serial port"; From 2b0d1b02243e2d7f2b64ee1c69a39df5555a75ba Mon Sep 17 00:00:00 2001 From: Jacob Nevins Date: Wed, 28 Feb 2007 23:31:49 +0000 Subject: [PATCH 045/124] Process -t/-T later than -m, so that they can override -m's default behaviour of no pty. [originally from svn r7348] --- cmdline.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmdline.c b/cmdline.c index 033df64a..5b294578 100644 --- a/cmdline.c +++ b/cmdline.c @@ -374,13 +374,13 @@ int cmdline_process_param(char *p, char *value, int need_save, Config *cfg) if (!strcmp(p, "-t")) { RETURN(1); UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK); - SAVEABLE(0); + SAVEABLE(1); /* lower priority than -m */ cfg->nopty = 0; } if (!strcmp(p, "-T")) { RETURN(1); UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK); - SAVEABLE(0); + SAVEABLE(1); cfg->nopty = 1; } From 36db0d6f72e81c449317e66624c7cebf651ccb3c Mon Sep 17 00:00:00 2001 From: Jacob Nevins Date: Tue, 13 Mar 2007 14:43:14 +0000 Subject: [PATCH 046/124] get_random_data() can return NULL (for instance, if we can't open /dev/random on Unix), yet cmdgen failed to deal with this. Spotted by Darren Tucker. [originally from svn r7396] --- cmdgen.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/cmdgen.c b/cmdgen.c index d8d5a946..9aa585f3 100644 --- a/cmdgen.c +++ b/cmdgen.c @@ -640,6 +640,11 @@ int main(int argc, char **argv) random_ref(); entropy = get_random_data(bits / 8); + if (!entropy) { + fprintf(stderr, "puttygen: failed to collect entropy, " + "could not generate key\n"); + return 1; + } random_add_heavynoise(entropy, bits / 8); memset(entropy, 0, bits/8); sfree(entropy); From d1df3e226a7385f948196d62edf119ee82028100 Mon Sep 17 00:00:00 2001 From: Ben Harris Date: Mon, 19 Mar 2007 12:05:34 +0000 Subject: [PATCH 047/124] Fix a stupid one-character typo that was breaking 256-colour support on GTK. [originally from svn r7403] --- unix/gtkwin.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unix/gtkwin.c b/unix/gtkwin.c index 388d4225..a377ad65 100644 --- a/unix/gtkwin.c +++ b/unix/gtkwin.c @@ -1434,7 +1434,7 @@ void palette_reset(void *frontend) int r = i / 36, g = (i / 6) % 6, b = i % 6; inst->cols[i+16].red = r ? r * 0x2828 + 0x3737 : 0; inst->cols[i+16].green = g ? g * 0x2828 + 0x3737 : 0; - inst->cols[i+16].blue = b ? b + 0x2828 + 0x3737 : 0; + inst->cols[i+16].blue = b ? b * 0x2828 + 0x3737 : 0; } else { int shade = i - 216; shade = shade * 0x0a0a + 0x0808; From 7e4eb1f4042323d42e9b7753714a6bf1becd9b5e Mon Sep 17 00:00:00 2001 From: Simon Tatham Date: Tue, 27 Mar 2007 18:16:36 +0000 Subject: [PATCH 048/124] Patch from John Sullivan: process double-clicks in the session list box on button-up rather than button-down. The effect of this is that if a saved session is already selected in the list box and then you double-click it, it will open rather than beeping annoyingly. [originally from svn r7414] --- unix/gtkdlg.c | 35 +++++++++++++++++++++++++++-------- 1 file changed, 27 insertions(+), 8 deletions(-) diff --git a/unix/gtkdlg.c b/unix/gtkdlg.c index 0a870ca3..10138e27 100644 --- a/unix/gtkdlg.c +++ b/unix/gtkdlg.c @@ -57,6 +57,7 @@ struct uctrl { GtkWidget *label; /* for dlg_label_change */ GtkAdjustment *adj; /* for the scrollbar in a list box */ guint textsig; + int nclicks; }; struct dlgparam { @@ -95,8 +96,10 @@ static int listitem_single_key(GtkWidget *item, GdkEventKey *event, gpointer data); static int listitem_multi_key(GtkWidget *item, GdkEventKey *event, gpointer data); -static int listitem_button(GtkWidget *item, GdkEventButton *event, - gpointer data); +static int listitem_button_press(GtkWidget *item, GdkEventButton *event, + gpointer data); +static int listitem_button_release(GtkWidget *item, GdkEventButton *event, + gpointer data); static void menuitem_activate(GtkMenuItem *item, gpointer data); static void coloursel_ok(GtkButton *button, gpointer data); static void coloursel_cancel(GtkButton *button, gpointer data); @@ -439,7 +442,9 @@ void dlg_listbox_addwithid(union control *ctrl, void *dlg, gtk_signal_connect(GTK_OBJECT(listitem), "focus_in_event", GTK_SIGNAL_FUNC(widget_focus), dp); gtk_signal_connect(GTK_OBJECT(listitem), "button_press_event", - GTK_SIGNAL_FUNC(listitem_button), dp); + GTK_SIGNAL_FUNC(listitem_button_press), dp); + gtk_signal_connect(GTK_OBJECT(listitem), "button_release_event", + GTK_SIGNAL_FUNC(listitem_button_release), dp); gtk_object_set_data(GTK_OBJECT(listitem), "user-data", GINT_TO_POINTER(id)); } else { @@ -1083,13 +1088,26 @@ static int listitem_multi_key(GtkWidget *item, GdkEventKey *event, return listitem_key(item, event, data, TRUE); } -static int listitem_button(GtkWidget *item, GdkEventButton *event, - gpointer data) +static int listitem_button_press(GtkWidget *item, GdkEventButton *event, + gpointer data) { struct dlgparam *dp = (struct dlgparam *)data; - if (event->type == GDK_2BUTTON_PRESS || - event->type == GDK_3BUTTON_PRESS) { - struct uctrl *uc = dlg_find_bywidget(dp, GTK_WIDGET(item)); + struct uctrl *uc = dlg_find_bywidget(dp, GTK_WIDGET(item)); + switch (event->type) { + default: + case GDK_BUTTON_PRESS: uc->nclicks = 1; break; + case GDK_2BUTTON_PRESS: uc->nclicks = 2; break; + case GDK_3BUTTON_PRESS: uc->nclicks = 3; break; + } + return FALSE; +} + +static int listitem_button_release(GtkWidget *item, GdkEventButton *event, + gpointer data) +{ + struct dlgparam *dp = (struct dlgparam *)data; + struct uctrl *uc = dlg_find_bywidget(dp, GTK_WIDGET(item)); + if (uc->nclicks>1) { uc->ctrl->generic.handler(uc->ctrl, dp, dp->data, EVENT_ACTION); return TRUE; } @@ -1367,6 +1385,7 @@ GtkWidget *layout_ctrls(struct dlgparam *dp, struct Shortcuts *scs, uc->entry = uc->list = uc->menu = NULL; uc->button = uc->optmenu = uc->text = NULL; uc->label = NULL; + uc->nclicks = 0; switch (ctrl->generic.type) { case CTRL_BUTTON: From 702a92ceb848394f722292cd4a54c414d4e0be67 Mon Sep 17 00:00:00 2001 From: Simon Tatham Date: Tue, 27 Mar 2007 18:49:59 +0000 Subject: [PATCH 049/124] Windows apparently sends ERROR_BROKEN_PIPE when a pipe we're reading from is closed normally from the writing end. This is ludicrous; if that situation isn't a natural EOF, _nothing_ is. So if we get that particular error, we pretend it's EOF. [originally from svn r7415] --- windows/winhandl.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/windows/winhandl.c b/windows/winhandl.c index f515ee57..9f120dc0 100644 --- a/windows/winhandl.c +++ b/windows/winhandl.c @@ -134,13 +134,25 @@ static DWORD WINAPI handle_input_threadfunc(void *param) } ctx->readret = ReadFile(ctx->h, ctx->buffer, readlen, &ctx->len, povl); - if (povl && !ctx->readret && GetLastError() == ERROR_IO_PENDING) { + if (!ctx->readret) + error = GetLastError(); + if (povl && !ctx->readret && error == ERROR_IO_PENDING) { WaitForSingleObject(povl->hEvent, INFINITE); ctx->readret = GetOverlappedResult(ctx->h, povl, &ctx->len, FALSE); } - if (!ctx->readret) + if (!ctx->readret) { + /* + * Windows apparently sends ERROR_BROKEN_PIPE when a + * pipe we're reading from is closed normally from the + * writing end. This is ludicrous; if that situation + * isn't a natural EOF, _nothing_ is. So if we get that + * particular error, we pretend it's EOF. + */ + if (error == ERROR_BROKEN_PIPE) + ctx->readret = 1; ctx->len = 0; + } if (ctx->readret && ctx->len == 0 && (ctx->flags & HANDLE_FLAG_IGNOREEOF)) From 5f410ef051252d9961065c5441051c2395cc84eb Mon Sep 17 00:00:00 2001 From: Simon Tatham Date: Tue, 27 Mar 2007 19:10:10 +0000 Subject: [PATCH 050/124] In the wake of r7415, let's have some better error reporting. Instead of passing -1 to its gotdata and sentdata callbacks on error, winhandl.c will now pass the negation of the Windows error number; and the Plink front end will now format that into an error message and pass it on to the user. [originally from svn r7416] [r7415 == 702a92ceb848394f722292cd4a54c414d4e0be67] --- windows/winhandl.c | 59 +++++++++++++++++++++++++++++----------------- windows/winplink.c | 18 +++++++++++--- 2 files changed, 52 insertions(+), 25 deletions(-) diff --git a/windows/winhandl.c b/windows/winhandl.c index 9f120dc0..f300962c 100644 --- a/windows/winhandl.c +++ b/windows/winhandl.c @@ -96,7 +96,7 @@ struct handle_input { */ char buffer[4096]; /* the data read from the handle */ DWORD len; /* how much data that was */ - int readret; /* lets us know about read errors */ + int readerr; /* lets us know about read errors */ /* * Callback function called by this module when data arrives on @@ -113,7 +113,7 @@ static DWORD WINAPI handle_input_threadfunc(void *param) struct handle_input *ctx = (struct handle_input *) param; OVERLAPPED ovl, *povl; HANDLE oev; - int readlen; + int readret, readlen; if (ctx->flags & HANDLE_FLAG_OVERLAPPED) { povl = &ovl; @@ -132,16 +132,21 @@ static DWORD WINAPI handle_input_threadfunc(void *param) memset(povl, 0, sizeof(OVERLAPPED)); povl->hEvent = oev; } - ctx->readret = ReadFile(ctx->h, ctx->buffer, readlen, - &ctx->len, povl); - if (!ctx->readret) - error = GetLastError(); - if (povl && !ctx->readret && error == ERROR_IO_PENDING) { + readret = ReadFile(ctx->h, ctx->buffer,readlen, &ctx->len, povl); + if (!readret) + ctx->readerr = GetLastError(); + else + ctx->readerr = 0; + if (povl && !readret && ctx->readerr == ERROR_IO_PENDING) { WaitForSingleObject(povl->hEvent, INFINITE); - ctx->readret = GetOverlappedResult(ctx->h, povl, &ctx->len, FALSE); + readret = GetOverlappedResult(ctx->h, povl, &ctx->len, FALSE); + if (!readret) + ctx->readerr = GetLastError(); + else + ctx->readerr = 0; } - if (!ctx->readret) { + if (!readret) { /* * Windows apparently sends ERROR_BROKEN_PIPE when a * pipe we're reading from is closed normally from the @@ -149,12 +154,12 @@ static DWORD WINAPI handle_input_threadfunc(void *param) * isn't a natural EOF, _nothing_ is. So if we get that * particular error, we pretend it's EOF. */ - if (error == ERROR_BROKEN_PIPE) - ctx->readret = 1; + if (ctx->readerr == ERROR_BROKEN_PIPE) + ctx->readerr = 0; ctx->len = 0; } - if (ctx->readret && ctx->len == 0 && + if (readret && ctx->len == 0 && (ctx->flags & HANDLE_FLAG_IGNOREEOF)) continue; @@ -239,7 +244,7 @@ struct handle_output { * and read by the main thread after receiving that signal. */ DWORD lenwritten; /* how much data we actually wrote */ - int writeret; /* return value from WriteFile */ + int writeerr; /* return value from WriteFile */ /* * Data only ever read or written by the main thread. @@ -257,6 +262,7 @@ static DWORD WINAPI handle_output_threadfunc(void *param) { struct handle_output *ctx = (struct handle_output *) param; OVERLAPPED ovl, *povl; + int writeret; if (ctx->flags & HANDLE_FLAG_OVERLAPPED) povl = &ovl; @@ -271,14 +277,23 @@ static DWORD WINAPI handle_output_threadfunc(void *param) } if (povl) memset(povl, 0, sizeof(OVERLAPPED)); - ctx->writeret = WriteFile(ctx->h, ctx->buffer, ctx->len, - &ctx->lenwritten, povl); - if (povl && !ctx->writeret && GetLastError() == ERROR_IO_PENDING) - ctx->writeret = GetOverlappedResult(ctx->h, povl, - &ctx->lenwritten, TRUE); + writeret = WriteFile(ctx->h, ctx->buffer, ctx->len, + &ctx->lenwritten, povl); + if (!writeret) + ctx->writeerr = GetLastError(); + else + ctx->writeerr = 0; + if (povl && !writeret && GetLastError() == ERROR_IO_PENDING) { + writeret = GetOverlappedResult(ctx->h, povl, + &ctx->lenwritten, TRUE); + if (!writeret) + ctx->writeerr = GetLastError(); + else + ctx->writeerr = 0; + } SetEvent(ctx->ev_to_main); - if (!ctx->writeret) + if (!writeret) break; } @@ -524,7 +539,7 @@ void handle_got_event(HANDLE event) /* * EOF, or (nearly equivalently) read error. */ - h->u.i.gotdata(h, NULL, (h->u.i.readret ? 0 : -1)); + h->u.i.gotdata(h, NULL, -h->u.i.readerr); h->u.i.defunct = TRUE; } else { backlog = h->u.i.gotdata(h, h->u.i.buffer, h->u.i.len); @@ -538,13 +553,13 @@ void handle_got_event(HANDLE event) * write. Call the callback to indicate that the output * buffer size has decreased, or to indicate an error. */ - if (!h->u.o.writeret) { + if (h->u.o.writeerr) { /* * Write error. Send a negative value to the callback, * and mark the thread as defunct (because the output * thread is terminating by now). */ - h->u.o.sentdata(h, -1); + h->u.o.sentdata(h, -h->u.o.writeerr); h->u.o.defunct = TRUE; } else { bufchain_consume(&h->u.o.queued_data, h->u.o.lenwritten); diff --git a/windows/winplink.c b/windows/winplink.c index 0819934c..42426000 100644 --- a/windows/winplink.c +++ b/windows/winplink.c @@ -228,7 +228,13 @@ int stdin_gotdata(struct handle *h, void *data, int len) /* * Special case: report read error. */ - fprintf(stderr, "Unable to read from standard input\n"); + char buf[4096]; + FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, -len, 0, + buf, lenof(buf), NULL); + buf[lenof(buf)-1] = '\0'; + if (buf[strlen(buf)-1] == '\n') + buf[strlen(buf)-1] = '\0'; + fprintf(stderr, "Unable to read from standard input: %s\n", buf); cleanup_exit(0); } noise_ultralight(len); @@ -249,8 +255,14 @@ void stdouterr_sent(struct handle *h, int new_backlog) /* * Special case: report write error. */ - fprintf(stderr, "Unable to write to standard %s\n", - (h == stdout_handle ? "output" : "error")); + char buf[4096]; + FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, -new_backlog, 0, + buf, lenof(buf), NULL); + buf[lenof(buf)-1] = '\0'; + if (buf[strlen(buf)-1] == '\n') + buf[strlen(buf)-1] = '\0'; + fprintf(stderr, "Unable to write to standard %s: %s\n", + (h == stdout_handle ? "output" : "error"), buf); cleanup_exit(0); } if (connopen && back->connected(backhandle)) { From 0a3c07d15e8888ec0fc13c755a753d215b35ebd1 Mon Sep 17 00:00:00 2001 From: Simon Tatham Date: Mon, 2 Apr 2007 08:44:00 +0000 Subject: [PATCH 051/124] When the comments say `if we're in restart mode', the code in question should actually be conditional on restart mode! [originally from svn r7438] --- psftp.c | 68 ++++++++++++++++++++++++++++++--------------------------- 1 file changed, 36 insertions(+), 32 deletions(-) diff --git a/psftp.c b/psftp.c index f77a88ed..f8a2240e 100644 --- a/psftp.c +++ b/psftp.c @@ -321,22 +321,24 @@ int sftp_get_file(char *fname, char *outfname, int recurse, int restart) * If none of them exists, of course, we start at 0. */ i = 0; - while (i < nnames) { - char *nextoutfname; - int ret; - if (outfname) - nextoutfname = dir_file_cat(outfname, - ournames[i]->filename); - else - nextoutfname = dupstr(ournames[i]->filename); - ret = (file_type(nextoutfname) == FILE_TYPE_NONEXISTENT); - sfree(nextoutfname); - if (ret) - break; - i++; - } - if (i > 0) - i--; + if (restart) { + while (i < nnames) { + char *nextoutfname; + int ret; + if (outfname) + nextoutfname = dir_file_cat(outfname, + ournames[i]->filename); + else + nextoutfname = dupstr(ournames[i]->filename); + ret = (file_type(nextoutfname) == FILE_TYPE_NONEXISTENT); + sfree(nextoutfname); + if (ret) + break; + i++; + } + if (i > 0) + i--; + } /* * Now we're ready to recurse. Starting at ournames[i] @@ -568,23 +570,25 @@ int sftp_put_file(char *fname, char *outfname, int recurse, int restart) * If none of them exists, of course, we start at 0. */ i = 0; - while (i < nnames) { - char *nextoutfname; - nextoutfname = dupcat(outfname, "/", ournames[i], NULL); - sftp_register(req = fxp_stat_send(nextoutfname)); - rreq = sftp_find_request(pktin = sftp_recv()); - assert(rreq == req); - result = fxp_stat_recv(pktin, rreq, &attrs); - sfree(nextoutfname); - if (!result) - break; - i++; - } - if (i > 0) - i--; + if (restart) { + while (i < nnames) { + char *nextoutfname; + nextoutfname = dupcat(outfname, "/", ournames[i], NULL); + sftp_register(req = fxp_stat_send(nextoutfname)); + rreq = sftp_find_request(pktin = sftp_recv()); + assert(rreq == req); + result = fxp_stat_recv(pktin, rreq, &attrs); + sfree(nextoutfname); + if (!result) + break; + i++; + } + if (i > 0) + i--; + } - /* - * Now we're ready to recurse. Starting at ournames[i] + /* + * Now we're ready to recurse. Starting at ournames[i] * and continuing on to the end of the list, we * construct a new source and target file name, and * call sftp_put_file again. From 7df108f2fd0d8311a312bb9feee3cfe0c8fe79c5 Mon Sep 17 00:00:00 2001 From: Ben Harris Date: Tue, 10 Apr 2007 21:46:44 +0000 Subject: [PATCH 052/124] When we get an error writing to a local file, stop the download rather than pretending we just got -1 bytes. Not actually tested, but it looks pretty obvious. Bug reported by dking wang. [originally from svn r7459] --- psftp.c | 1 + 1 file changed, 1 insertion(+) diff --git a/psftp.c b/psftp.c index f8a2240e..e41112aa 100644 --- a/psftp.c +++ b/psftp.c @@ -463,6 +463,7 @@ int sftp_get_file(char *fname, char *outfname, int recurse, int restart) printf("error while writing local file\n"); ret = 0; xfer_set_error(xfer); + break; } wpos += wlen; } From ce6349bd89e4876e4bf531f12bc8d4789946f2ca Mon Sep 17 00:00:00 2001 From: Simon Tatham Date: Sun, 22 Apr 2007 08:56:31 +0000 Subject: [PATCH 053/124] Capitalisation error. [originally from svn r7476] --- Buildscr | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Buildscr b/Buildscr index 4775b85f..02d4a9c6 100644 --- a/Buildscr +++ b/Buildscr @@ -9,8 +9,8 @@ ifneq "$(!numeric $(revision))" "yes" set Makever $(Makever) -DMODIFIED ifneq "$(RELEASE)" "" set Makever $(Makever) -DRELEASE=$(RELEASE) ifneq "$(date)" "" set Makever $(Makever) -DSNAPSHOT=$(date) set Makeargs VER="$(Makever)" -ifneq "$(XFLAGS)" "" set Makeargs $(makeargs) XFLAGS="$(XFLAGS)" -ifneq "$(MAKEARGS)" "" set Makeargs $(makeargs) $(MAKEARGS) +ifneq "$(XFLAGS)" "" set Makeargs $(Makeargs) XFLAGS="$(XFLAGS)" +ifneq "$(MAKEARGS)" "" set Makeargs $(Makeargs) $(MAKEARGS) # Set up the version string for the docs build. set Docmakeargs VERSION="PuTTY revision $(revision)" From ec1e37fb55c65c4a81023837115cdfa145c8bbc9 Mon Sep 17 00:00:00 2001 From: Simon Tatham Date: Sun, 22 Apr 2007 14:39:01 +0000 Subject: [PATCH 054/124] Avoid creating the Session/hostport control set in mid-session. [originally from svn r7477] --- sercfg.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/sercfg.c b/sercfg.c index 752953e5..cde0a90d 100644 --- a/sercfg.c +++ b/sercfg.c @@ -104,16 +104,18 @@ void ser_setup_config_box(struct controlbox *b, int midsession, struct controlset *s; union control *c; - /* - * Add the serial back end to the protocols list at the top of - * the config box. - */ - s = ctrl_getset(b, "Session", "hostport", - "Specify your connection by host name or IP address"); - { + if (!midsession) { int i; extern void config_protocolbuttons_handler(union control *, void *, void *, int); + + /* + * Add the serial back end to the protocols list at the + * top of the config box. + */ + s = ctrl_getset(b, "Session", "hostport", + "Specify the destination you want to connect to"); + for (i = 0; i < s->ncontrols; i++) { c = s->ctrls[i]; if (c->generic.type == CTRL_RADIO && From 3f9d53aa9e58be5a6764862f0c15c1c4979ac19f Mon Sep 17 00:00:00 2001 From: Simon Tatham Date: Sun, 29 Apr 2007 11:28:54 +0000 Subject: [PATCH 055/124] Update version numbers for 0.60 release. [originally from svn r7488] --- LATEST.VER | 2 +- doc/plink.but | 2 +- doc/pscp.but | 2 +- mac/version.r | 2 +- windows/putty.iss | 8 ++++---- windows/version.rc2 | 2 +- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/LATEST.VER b/LATEST.VER index 435f14b1..08072c18 100644 --- a/LATEST.VER +++ b/LATEST.VER @@ -1 +1 @@ -0.59 +0.60 diff --git a/doc/plink.but b/doc/plink.but index dbda8843..a70f54f6 100644 --- a/doc/plink.but +++ b/doc/plink.but @@ -43,7 +43,7 @@ use Plink: \c Z:\sysosd>plink \c PuTTY Link: command-line connection utility -\c Release 0.59 +\c Release 0.60 \c Usage: plink [options] [user@]host [command] \c ("host" can also be a PuTTY saved session name) \c Options: diff --git a/doc/pscp.but b/doc/pscp.but index f2cfcbf0..74669f86 100644 --- a/doc/pscp.but +++ b/doc/pscp.but @@ -41,7 +41,7 @@ use PSCP: \c Z:\owendadmin>pscp \c PuTTY Secure Copy client -\c Release 0.59 +\c Release 0.60 \c Usage: pscp [options] [user@]host:source target \c pscp [options] source [source...] [user@]host:target \c pscp [options] -ls [user@]host:filespec diff --git a/mac/version.r b/mac/version.r index 49860b66..8770d1f6 100644 --- a/mac/version.r +++ b/mac/version.r @@ -2,7 +2,7 @@ * Current PuTTY version number. Minor is in BCD */ #define VERSION_MAJOR 0x00 -#define VERSION_MINOR 0x59 +#define VERSION_MINOR 0x60 resource 'vers' (1, purgeable) { #ifdef RELEASE diff --git a/windows/putty.iss b/windows/putty.iss index 0724b201..c6d885da 100644 --- a/windows/putty.iss +++ b/windows/putty.iss @@ -14,10 +14,10 @@ [Setup] AppName=PuTTY -AppVerName=PuTTY version 0.59 -VersionInfoTextVersion=Release 0.59 -AppVersion=0.59 -VersionInfoVersion=0.59.0.0 +AppVerName=PuTTY version 0.60 +VersionInfoTextVersion=Release 0.60 +AppVersion=0.60 +VersionInfoVersion=0.60.0.0 AppPublisher=Simon Tatham AppPublisherURL=http://www.chiark.greenend.org.uk/~sgtatham/putty/ AppReadmeFile={app}\README.txt diff --git a/windows/version.rc2 b/windows/version.rc2 index 8474a62b..3c7d1b57 100644 --- a/windows/version.rc2 +++ b/windows/version.rc2 @@ -39,7 +39,7 @@ /* We keep this around even for snapshots, for monotonicity of version * numbering. It needs to be kept up to date. NB _comma_-separated. */ -#define BASE_VERSION 0,59 +#define BASE_VERSION 0,60 #if defined SNAPSHOT From 9f7f5157fe921dadc837f65a38a2f50e416ecb7f Mon Sep 17 00:00:00 2001 From: Ben Harris Date: Mon, 30 Apr 2007 20:09:58 +0000 Subject: [PATCH 056/124] Create installations directories before installing into them, like GNU packages do. Problem reported by Manfred Pausch. [originally from svn r7494] --- Recipe | 1 + 1 file changed, 1 insertion(+) diff --git a/Recipe b/Recipe index 79ea3b0b..efdfabab 100644 --- a/Recipe +++ b/Recipe @@ -190,6 +190,7 @@ RCFLAGS += $(VER) # `make install' target for Unix. !begin gtk install: + mkdir -p $(DESTDIR)$(bindir) $(DESTDIR)$(man1dir) $(INSTALL_PROGRAM) -m 755 plink $(DESTDIR)$(bindir)/plink $(INSTALL_PROGRAM) -m 755 pscp $(DESTDIR)$(bindir)/pscp $(INSTALL_PROGRAM) -m 755 psftp $(DESTDIR)$(bindir)/psftp From dad558a1e59c059b7f3436a4865b5e4e17c59539 Mon Sep 17 00:00:00 2001 From: Ben Harris Date: Mon, 30 Apr 2007 22:09:26 +0000 Subject: [PATCH 057/124] Add support for RFC 4432 RSA key exchange, the patch for which has been lying around in my home directory for _years_. [originally from svn r7496] --- Recipe | 10 ++-- config.c | 1 + doc/config.but | 4 ++ putty.h | 1 + settings.c | 5 +- ssh.c | 138 ++++++++++++++++++++++++++++++++++++++++---- ssh.h | 23 +++++--- sshdh.c | 8 +-- sshrsa.c | 153 +++++++++++++++++++++++++++++++++++++++++++++++++ 9 files changed, 314 insertions(+), 29 deletions(-) diff --git a/Recipe b/Recipe index efdfabab..8bb3d169 100644 --- a/Recipe +++ b/Recipe @@ -312,8 +312,8 @@ pageant : [G] winpgnt sshrsa sshpubk sshdes sshbn sshmd5 version tree234 puttygen : [G] winpgen sshrsag sshdssg sshprime sshdes sshbn sshmd5 version + sshrand winnoise sshsha winstore misc winctrls sshrsa sshdss winmisc - + sshpubk sshaes sshsh512 import winutils puttygen.res tree234 - + notiming winhelp LIBS wintime + + sshpubk sshaes sshsh256 sshsh512 import winutils puttygen.res + + tree234 notiming winhelp LIBS wintime pterm : [X] GTKTERM uxmisc misc ldisc settings uxpty uxsel BE_NONE uxstore + uxsignal CHARSET cmdline uxpterm version time xpmpterm xpmptcfg @@ -328,8 +328,8 @@ plink : [U] uxplink uxcons NONSSH UXSSH U_BE_ALL logging UXMISC uxsignal puttygen : [U] cmdgen sshrsag sshdssg sshprime sshdes sshbn sshmd5 version + sshrand uxnoise sshsha misc sshrsa sshdss uxcons uxstore uxmisc - + sshpubk sshaes sshsh512 import puttygen.res time tree234 uxgen - + notiming + + sshpubk sshaes sshsh256 sshsh512 import puttygen.res time tree234 + + uxgen notiming pscp : [U] pscp uxsftp uxcons UXSSH BE_SSH SFTP wildcard UXMISC psftp : [U] psftp uxsftp uxcons UXSSH BE_SSH SFTP wildcard UXMISC @@ -342,7 +342,7 @@ PuTTYtel : [M] terminal wcwidth ldiscucs logging BE_NOSSH mac macdlg + CHARSET stricmp vsnprint dialog config macctrls minibidi PuTTYgen : [M] macpgen sshrsag sshdssg sshprime sshdes sshbn sshmd5 version + sshrand macnoise sshsha macstore misc sshrsa sshdss macmisc sshpubk - + sshaes sshsh512 import macpgen.rsrc macpgkey macabout + + sshaes sshsh256 sshsh512 import macpgen.rsrc macpgkey macabout PuTTY : [MX] osxmain OSXTERM OSXMISC CHARSET U_BE_ALL NONSSH UXSSH + ux_x11 uxpty uxsignal testback putty.icns info.plist diff --git a/config.c b/config.c index 477dc4d4..41bb4943 100644 --- a/config.c +++ b/config.c @@ -256,6 +256,7 @@ static void kexlist_handler(union control *ctrl, void *dlg, { "Diffie-Hellman group 1", KEX_DHGROUP1 }, { "Diffie-Hellman group 14", KEX_DHGROUP14 }, { "Diffie-Hellman group exchange", KEX_DHGEX }, + { "RSA-based key exchange", KEX_RSA }, { "-- warn below here --", KEX_WARN } }; diff --git a/doc/config.but b/doc/config.but index 3b744356..e5d42e9e 100644 --- a/doc/config.but +++ b/doc/config.but @@ -2282,6 +2282,10 @@ exchange; the server can avoid groups known to be weak, and possibly invent new ones over time, without any changes required to PuTTY's configuration. We recommend use of this method, if possible. +In addition, PuTTY supports \i{RSA key exchange}, which requires much less +computational effort on the part of the client, and somewhat less on +the part of the server, than Diffie-Hellman key exchange. + If the first algorithm PuTTY finds is below the \q{warn below here} line, you will see a warning box when you make the connection, similar to that for cipher selection (see \k{config-ssh-encryption}). diff --git a/putty.h b/putty.h index b9f11ceb..3583ecf0 100644 --- a/putty.h +++ b/putty.h @@ -252,6 +252,7 @@ enum { KEX_DHGROUP1, KEX_DHGROUP14, KEX_DHGEX, + KEX_RSA, KEX_MAX }; diff --git a/settings.c b/settings.c index 10f3573f..c937d3c9 100644 --- a/settings.c +++ b/settings.c @@ -27,6 +27,7 @@ static const struct keyval kexnames[] = { { "dh-gex-sha1", KEX_DHGEX }, { "dh-group14-sha1", KEX_DHGROUP14 }, { "dh-group1-sha1", KEX_DHGROUP1 }, + { "rsa", KEX_RSA }, { "WARN", KEX_WARN } }; @@ -571,9 +572,9 @@ void load_open_settings(void *sesskey, Config *cfg) char *default_kexes; gppi(sesskey, "BugDHGEx2", 0, &i); i = 2-i; if (i == FORCE_ON) - default_kexes = "dh-group14-sha1,dh-group1-sha1,WARN,dh-gex-sha1"; + default_kexes = "dh-group14-sha1,dh-group1-sha1,rsa,WARN,dh-gex-sha1"; else - default_kexes = "dh-gex-sha1,dh-group14-sha1,dh-group1-sha1,WARN"; + default_kexes = "dh-gex-sha1,dh-group14-sha1,dh-group1-sha1,rsa,WARN"; gprefs(sesskey, "KEX", default_kexes, kexnames, KEX_MAX, cfg->ssh_kexlist); } diff --git a/ssh.c b/ssh.c index bcc1dd1b..64a069ef 100644 --- a/ssh.c +++ b/ssh.c @@ -83,6 +83,9 @@ #define SSH2_MSG_KEX_DH_GEX_GROUP 31 /* 0x1f */ #define SSH2_MSG_KEX_DH_GEX_INIT 32 /* 0x20 */ #define SSH2_MSG_KEX_DH_GEX_REPLY 33 /* 0x21 */ +#define SSH2_MSG_KEXRSA_PUBKEY 30 /* 0x1e */ +#define SSH2_MSG_KEXRSA_SECRET 31 /* 0x1f */ +#define SSH2_MSG_KEXRSA_DONE 32 /* 0x20 */ #define SSH2_MSG_USERAUTH_REQUEST 50 /* 0x32 */ #define SSH2_MSG_USERAUTH_FAILURE 51 /* 0x33 */ #define SSH2_MSG_USERAUTH_SUCCESS 52 /* 0x34 */ @@ -112,6 +115,7 @@ */ #define SSH2_PKTCTX_DHGROUP 0x0001 #define SSH2_PKTCTX_DHGEX 0x0002 +#define SSH2_PKTCTX_RSAKEX 0x0004 #define SSH2_PKTCTX_KEX_MASK 0x000F #define SSH2_PKTCTX_PUBLICKEY 0x0010 #define SSH2_PKTCTX_PASSWORD 0x0020 @@ -339,6 +343,9 @@ static char *ssh2_pkt_type(int pkt_ctx, int type) translatec(SSH2_MSG_KEX_DH_GEX_GROUP, SSH2_PKTCTX_DHGEX); translatec(SSH2_MSG_KEX_DH_GEX_INIT, SSH2_PKTCTX_DHGEX); translatec(SSH2_MSG_KEX_DH_GEX_REPLY, SSH2_PKTCTX_DHGEX); + translatec(SSH2_MSG_KEXRSA_PUBKEY, SSH2_PKTCTX_RSAKEX); + translatec(SSH2_MSG_KEXRSA_SECRET, SSH2_PKTCTX_RSAKEX); + translatec(SSH2_MSG_KEXRSA_DONE, SSH2_PKTCTX_RSAKEX); translate(SSH2_MSG_USERAUTH_REQUEST); translate(SSH2_MSG_USERAUTH_FAILURE); translate(SSH2_MSG_USERAUTH_SUCCESS); @@ -5105,9 +5112,10 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen, const struct ssh_mac *scmac_tobe; const struct ssh_compress *cscomp_tobe; const struct ssh_compress *sccomp_tobe; - char *hostkeydata, *sigdata, *keystr, *fingerprint; - int hostkeylen, siglen; + char *hostkeydata, *sigdata, *rsakeydata, *keystr, *fingerprint; + int hostkeylen, siglen, rsakeylen; void *hkey; /* actual host key */ + void *rsakey; /* for RSA kex */ unsigned char exchange_hash[SSH2_KEX_MAX_HASH_LEN]; int n_preferred_kex; const struct ssh_kexes *preferred_kex[KEX_MAX]; @@ -5161,6 +5169,10 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen, s->preferred_kex[s->n_preferred_kex++] = &ssh_diffiehellman_group1; break; + case KEX_RSA: + s->preferred_kex[s->n_preferred_kex++] = + &ssh_rsa_kex; + break; case KEX_WARN: /* Flag for later. Don't bother if it's the last in * the list. */ @@ -5560,6 +5572,8 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen, crWaitUntil(pktin); /* Ignore packet */ } + if (ssh->kex->main_type == KEXTYPE_DH) { + /* XXX The lines below should be reindented before this is committed.*/ /* * Work out the number of bits of key we will need from the key * exchange. We start with the maximum key length of either @@ -5635,6 +5649,7 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen, } set_busy_status(ssh->frontend, BUSY_CPU); /* cogitate */ ssh_pkt_getstring(pktin, &s->hostkeydata, &s->hostkeylen); + s->hkey = ssh->hostkey->newkey(s->hostkeydata, s->hostkeylen); s->f = ssh2_pkt_getmp(pktin); if (!s->f) { bombout(("unable to parse key exchange reply packet")); @@ -5656,11 +5671,120 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen, } hash_mpint(ssh->kex->hash, ssh->exhash, s->e); hash_mpint(ssh->kex->hash, ssh->exhash, s->f); + + dh_cleanup(ssh->kex_ctx); + freebn(s->f); + if (!ssh->kex->pdata) { + freebn(s->g); + freebn(s->p); + } + /* XXX end incorrectly-indented section */ + } else { + logeventf(ssh, "Doing RSA key exchange with hash %s", + ssh->kex->hash->text_name); + ssh->pkt_ctx |= SSH2_PKTCTX_RSAKEX; + /* + * RSA key exchange. First expect a KEXRSA_PUBKEY packet + * from the server. + */ + crWaitUntil(pktin); + if (pktin->type != SSH2_MSG_KEXRSA_PUBKEY) { + bombout(("expected RSA public key packet from server")); + crStop(0); + } + + ssh_pkt_getstring(pktin, &s->hostkeydata, &s->hostkeylen); + hash_string(ssh->kex->hash, ssh->exhash, + s->hostkeydata, s->hostkeylen); + s->hkey = ssh->hostkey->newkey(s->hostkeydata, s->hostkeylen); + + { + char *keydata; + ssh_pkt_getstring(pktin, &keydata, &s->rsakeylen); + s->rsakeydata = snewn(s->rsakeylen, char); + memcpy(s->rsakeydata, keydata, s->rsakeylen); + } + + s->rsakey = ssh_rsakex_newkey(s->rsakeydata, s->rsakeylen); + if (!s->rsakey) { + sfree(s->rsakeydata); + bombout(("unable to parse RSA public key from server")); + crStop(0); + } + + hash_string(ssh->kex->hash, ssh->exhash, s->rsakeydata, s->rsakeylen); + + /* + * Next, set up a shared secret K, of precisely KLEN - + * 2*HLEN - 49 bits, where KLEN is the bit length of the + * RSA key modulus and HLEN is the bit length of the hash + * we're using. + */ + { + int klen = ssh_rsakex_klen(s->rsakey); + int nbits = klen - (2*ssh->kex->hash->hlen*8 + 49); + int i, byte = 0; + unsigned char *kstr1, *kstr2, *outstr; + int kstr1len, kstr2len, outstrlen; + + s->K = bn_power_2(nbits - 1); + + for (i = 0; i < nbits; i++) { + if ((i & 7) == 0) { + byte = random_byte(); + } + bignum_set_bit(s->K, i, (byte >> (i & 7)) & 1); + } + + /* + * Encode this as an mpint. + */ + kstr1 = ssh2_mpint_fmt(s->K, &kstr1len); + kstr2 = snewn(kstr2len = 4 + kstr1len, unsigned char); + PUT_32BIT(kstr2, kstr1len); + memcpy(kstr2 + 4, kstr1, kstr1len); + + /* + * Encrypt it with the given RSA key. + */ + outstrlen = (klen + 7) / 8; + outstr = snewn(outstrlen, unsigned char); + ssh_rsakex_encrypt(ssh->kex->hash, kstr2, kstr2len, + outstr, outstrlen, s->rsakey); + + /* + * And send it off in a return packet. + */ + s->pktout = ssh2_pkt_init(SSH2_MSG_KEXRSA_SECRET); + ssh2_pkt_addstring_start(s->pktout); + ssh2_pkt_addstring_data(s->pktout, outstr, outstrlen); + ssh2_pkt_send_noqueue(ssh, s->pktout); + + hash_string(ssh->kex->hash, ssh->exhash, outstr, outstrlen); + + sfree(kstr2); + sfree(kstr1); + sfree(outstr); + } + + ssh_rsakex_freekey(s->rsakey); + + crWaitUntil(pktin); + if (pktin->type != SSH2_MSG_KEXRSA_DONE) { + sfree(s->rsakeydata); + bombout(("expected signature packet from server")); + crStop(0); + } + + ssh_pkt_getstring(pktin, &s->sigdata, &s->siglen); + + sfree(s->rsakeydata); + } + hash_mpint(ssh->kex->hash, ssh->exhash, s->K); assert(ssh->kex->hash->hlen <= sizeof(s->exchange_hash)); ssh->kex->hash->final(ssh->exhash, s->exchange_hash); - dh_cleanup(ssh->kex_ctx); ssh->kex_ctx = NULL; #if 0 @@ -5668,7 +5792,6 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen, dmemdump(s->exchange_hash, ssh->kex->hash->hlen); #endif - s->hkey = ssh->hostkey->newkey(s->hostkeydata, s->hostkeylen); if (!s->hkey || !ssh->hostkey->verifysig(s->hkey, s->sigdata, s->siglen, (char *)s->exchange_hash, @@ -5850,14 +5973,9 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen, ssh->sccomp->text_name); /* - * Free key exchange data. + * Free shared secret. */ - freebn(s->f); freebn(s->K); - if (!ssh->kex->pdata) { - freebn(s->g); - freebn(s->p); - } /* * Key exchange is over. Loop straight back round if we have a diff --git a/ssh.h b/ssh.h index fede9968..30311412 100644 --- a/ssh.h +++ b/ssh.h @@ -82,6 +82,17 @@ void crcda_free_context(void *handle); int detect_attack(void *handle, unsigned char *buf, uint32 len, unsigned char *IV); +/* + * SSH2 RSA key exchange functions + */ +struct ssh_hash; +void *ssh_rsakex_newkey(char *data, int len); +void ssh_rsakex_freekey(void *key); +int ssh_rsakex_klen(void *key); +void ssh_rsakex_encrypt(const struct ssh_hash *h, unsigned char *in, int inlen, + unsigned char *out, int outlen, + void *key); + typedef struct { uint32 h[4]; } MD5_Core_State; @@ -194,15 +205,10 @@ struct ssh_hash { }; struct ssh_kex { - /* - * Plugging in another KEX algorithm requires structural chaos, - * so it's hard to abstract them into nice little structures - * like this. Fortunately, all our KEXes are basically - * Diffie-Hellman at the moment, so in this structure I simply - * parametrise the DH exchange a bit. - */ char *name, *groupname; - const unsigned char *pdata, *gdata;/* NULL means use group exchange */ + enum { KEXTYPE_DH, KEXTYPE_RSA } main_type; + /* For DH */ + const unsigned char *pdata, *gdata; /* NULL means group exchange */ int plen, glen; const struct ssh_hash *hash; }; @@ -268,6 +274,7 @@ extern const struct ssh_hash ssh_sha256; extern const struct ssh_kexes ssh_diffiehellman_group1; extern const struct ssh_kexes ssh_diffiehellman_group14; extern const struct ssh_kexes ssh_diffiehellman_gex; +extern const struct ssh_kexes ssh_rsa_kex; extern const struct ssh_signkey ssh_dss; extern const struct ssh_signkey ssh_rsa; extern const struct ssh_mac ssh_hmac_md5; diff --git a/sshdh.c b/sshdh.c index f0abd3ec..c733b61f 100644 --- a/sshdh.c +++ b/sshdh.c @@ -52,7 +52,7 @@ static const unsigned char G[] = { 2 }; static const struct ssh_kex ssh_diffiehellman_group1_sha1 = { "diffie-hellman-group1-sha1", "group1", - P1, G, lenof(P1), lenof(G), &ssh_sha1 + KEXTYPE_DH, P1, G, lenof(P1), lenof(G), &ssh_sha1 }; static const struct ssh_kex *const group1_list[] = { @@ -66,7 +66,7 @@ const struct ssh_kexes ssh_diffiehellman_group1 = { static const struct ssh_kex ssh_diffiehellman_group14_sha1 = { "diffie-hellman-group14-sha1", "group14", - P14, G, lenof(P14), lenof(G), &ssh_sha1 + KEXTYPE_DH, P14, G, lenof(P14), lenof(G), &ssh_sha1 }; static const struct ssh_kex *const group14_list[] = { @@ -80,12 +80,12 @@ const struct ssh_kexes ssh_diffiehellman_group14 = { static const struct ssh_kex ssh_diffiehellman_gex_sha256 = { "diffie-hellman-group-exchange-sha256", NULL, - NULL, NULL, 0, 0, &ssh_sha256 + KEXTYPE_DH, NULL, NULL, 0, 0, &ssh_sha256 }; static const struct ssh_kex ssh_diffiehellman_gex_sha1 = { "diffie-hellman-group-exchange-sha1", NULL, - NULL, NULL, 0, 0, &ssh_sha1 + KEXTYPE_DH, NULL, NULL, 0, 0, &ssh_sha1 }; static const struct ssh_kex *const gex_list[] = { diff --git a/sshrsa.c b/sshrsa.c index b862d3f6..6db265ee 100644 --- a/sshrsa.c +++ b/sshrsa.c @@ -836,3 +836,156 @@ const struct ssh_signkey ssh_rsa = { "ssh-rsa", "rsa2" }; + +void *ssh_rsakex_newkey(char *data, int len) +{ + return rsa2_newkey(data, len); +} + +void ssh_rsakex_freekey(void *key) +{ + rsa2_freekey(key); +} + +int ssh_rsakex_klen(void *key) +{ + struct RSAKey *rsa = (struct RSAKey *) key; + + return bignum_bitcount(rsa->modulus); +} + +static void oaep_mask(const struct ssh_hash *h, void *seed, int seedlen, + void *vdata, int datalen) +{ + unsigned char *data = (unsigned char *)vdata; + unsigned count = 0; + + while (datalen > 0) { + int i, max = (datalen > h->hlen ? h->hlen : datalen); + void *s; + unsigned char counter[4], hash[h->hlen]; + + PUT_32BIT(counter, count); + s = h->init(); + h->bytes(s, seed, seedlen); + h->bytes(s, counter, 4); + h->final(s, hash); + count++; + + for (i = 0; i < max; i++) + data[i] ^= hash[i]; + + data += max; + datalen -= max; + } +} + +void ssh_rsakex_encrypt(const struct ssh_hash *h, unsigned char *in, int inlen, + unsigned char *out, int outlen, + void *key) +{ + Bignum b1, b2; + struct RSAKey *rsa = (struct RSAKey *) key; + int k, i; + char *p; + const int HLEN = h->hlen; + + /* + * Here we encrypt using RSAES-OAEP. Essentially this means: + * + * - we have a SHA-based `mask generation function' which + * creates a pseudo-random stream of mask data + * deterministically from an input chunk of data. + * + * - we have a random chunk of data called a seed. + * + * - we use the seed to generate a mask which we XOR with our + * plaintext. + * + * - then we use _the masked plaintext_ to generate a mask + * which we XOR with the seed. + * + * - then we concatenate the masked seed and the masked + * plaintext, and RSA-encrypt that lot. + * + * The result is that the data input to the encryption function + * is random-looking and (hopefully) contains no exploitable + * structure such as PKCS1-v1_5 does. + * + * For a precise specification, see RFC 3447, section 7.1.1. + * Some of the variable names below are derived from that, so + * it'd probably help to read it anyway. + */ + + /* k denotes the length in octets of the RSA modulus. */ + k = (7 + bignum_bitcount(rsa->modulus)) / 8; + + /* The length of the input data must be at most k - 2hLen - 2. */ + assert(inlen > 0 && inlen <= k - 2*HLEN - 2); + + /* The length of the output data wants to be precisely k. */ + assert(outlen == k); + + /* + * Now perform EME-OAEP encoding. First set up all the unmasked + * output data. + */ + /* Leading byte zero. */ + out[0] = 0; + /* At position 1, the seed: HLEN bytes of random data. */ + for (i = 0; i < HLEN; i++) + out[i + 1] = random_byte(); + /* At position 1+HLEN, the data block DB, consisting of: */ + /* The hash of the label (we only support an empty label here) */ + h->final(h->init(), out + HLEN + 1); + /* A bunch of zero octets */ + memset(out + 2*HLEN + 1, 0, outlen - (2*HLEN + 1)); + /* A single 1 octet, followed by the input message data. */ + out[outlen - inlen - 1] = 1; + memcpy(out + outlen - inlen, in, inlen); + + /* + * Now use the seed data to mask the block DB. + */ + oaep_mask(h, out+1, HLEN, out+HLEN+1, outlen-HLEN-1); + + /* + * And now use the masked DB to mask the seed itself. + */ + oaep_mask(h, out+HLEN+1, outlen-HLEN-1, out+1, HLEN); + + /* + * Now `out' contains precisely the data we want to + * RSA-encrypt. + */ + b1 = bignum_from_bytes(out, outlen); + b2 = modpow(b1, rsa->exponent, rsa->modulus); + p = out; + for (i = outlen; i--;) { + *p++ = bignum_byte(b2, i); + } + freebn(b1); + freebn(b2); + + /* + * And we're done. + */ +} + +static const struct ssh_kex ssh_rsa_kex_sha1 = { + "rsa1024-sha1", NULL, KEXTYPE_RSA, NULL, NULL, 0, 0, &ssh_sha1 +}; + +static const struct ssh_kex ssh_rsa_kex_sha256 = { + "rsa2048-sha256", NULL, KEXTYPE_RSA, NULL, NULL, 0, 0, &ssh_sha256 +}; + +static const struct ssh_kex *const rsa_kex_list[] = { + &ssh_rsa_kex_sha256, + &ssh_rsa_kex_sha1 +}; + +const struct ssh_kexes ssh_rsa_kex = { + sizeof(rsa_kex_list) / sizeof(*rsa_kex_list), + rsa_kex_list +}; From 723d834a13fd1b89beafe55254e776ef82640b53 Mon Sep 17 00:00:00 2001 From: Simon Tatham Date: Tue, 1 May 2007 12:26:44 +0000 Subject: [PATCH 058/124] Reindent the section that was marked `XXX The lines below should be reindented before this is committed'. Unfortunately not before it was committed, but you can't have everything :-) [originally from svn r7499] --- ssh.c | 192 +++++++++++++++++++++++++++++----------------------------- 1 file changed, 95 insertions(+), 97 deletions(-) diff --git a/ssh.c b/ssh.c index 64a069ef..21fcd2d9 100644 --- a/ssh.c +++ b/ssh.c @@ -5573,112 +5573,110 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen, } if (ssh->kex->main_type == KEXTYPE_DH) { - /* XXX The lines below should be reindented before this is committed.*/ - /* - * Work out the number of bits of key we will need from the key - * exchange. We start with the maximum key length of either - * cipher... - */ - { - int csbits, scbits; + /* + * Work out the number of bits of key we will need from the + * key exchange. We start with the maximum key length of + * either cipher... + */ + { + int csbits, scbits; - csbits = s->cscipher_tobe->keylen; - scbits = s->sccipher_tobe->keylen; - s->nbits = (csbits > scbits ? csbits : scbits); - } - /* The keys only have hlen-bit entropy, since they're based on - * a hash. So cap the key size at hlen bits. */ - if (s->nbits > ssh->kex->hash->hlen * 8) - s->nbits = ssh->kex->hash->hlen * 8; + csbits = s->cscipher_tobe->keylen; + scbits = s->sccipher_tobe->keylen; + s->nbits = (csbits > scbits ? csbits : scbits); + } + /* The keys only have hlen-bit entropy, since they're based on + * a hash. So cap the key size at hlen bits. */ + if (s->nbits > ssh->kex->hash->hlen * 8) + s->nbits = ssh->kex->hash->hlen * 8; - /* - * If we're doing Diffie-Hellman group exchange, start by - * requesting a group. - */ - if (!ssh->kex->pdata) { - logevent("Doing Diffie-Hellman group exchange"); - ssh->pkt_ctx |= SSH2_PKTCTX_DHGEX; - /* - * Work out how big a DH group we will need to allow that - * much data. - */ - s->pbits = 512 << ((s->nbits - 1) / 64); - s->pktout = ssh2_pkt_init(SSH2_MSG_KEX_DH_GEX_REQUEST); - ssh2_pkt_adduint32(s->pktout, s->pbits); - ssh2_pkt_send_noqueue(ssh, s->pktout); + /* + * If we're doing Diffie-Hellman group exchange, start by + * requesting a group. + */ + if (!ssh->kex->pdata) { + logevent("Doing Diffie-Hellman group exchange"); + ssh->pkt_ctx |= SSH2_PKTCTX_DHGEX; + /* + * Work out how big a DH group we will need to allow that + * much data. + */ + s->pbits = 512 << ((s->nbits - 1) / 64); + s->pktout = ssh2_pkt_init(SSH2_MSG_KEX_DH_GEX_REQUEST); + ssh2_pkt_adduint32(s->pktout, s->pbits); + ssh2_pkt_send_noqueue(ssh, s->pktout); - crWaitUntil(pktin); - if (pktin->type != SSH2_MSG_KEX_DH_GEX_GROUP) { - bombout(("expected key exchange group packet from server")); - crStop(0); - } - s->p = ssh2_pkt_getmp(pktin); - s->g = ssh2_pkt_getmp(pktin); - if (!s->p || !s->g) { - bombout(("unable to read mp-ints from incoming group packet")); - crStop(0); - } - ssh->kex_ctx = dh_setup_gex(s->p, s->g); - s->kex_init_value = SSH2_MSG_KEX_DH_GEX_INIT; - s->kex_reply_value = SSH2_MSG_KEX_DH_GEX_REPLY; - } else { - ssh->pkt_ctx |= SSH2_PKTCTX_DHGROUP; - ssh->kex_ctx = dh_setup_group(ssh->kex); - s->kex_init_value = SSH2_MSG_KEXDH_INIT; - s->kex_reply_value = SSH2_MSG_KEXDH_REPLY; - logeventf(ssh, "Using Diffie-Hellman with standard group \"%s\"", - ssh->kex->groupname); - } + crWaitUntil(pktin); + if (pktin->type != SSH2_MSG_KEX_DH_GEX_GROUP) { + bombout(("expected key exchange group packet from server")); + crStop(0); + } + s->p = ssh2_pkt_getmp(pktin); + s->g = ssh2_pkt_getmp(pktin); + if (!s->p || !s->g) { + bombout(("unable to read mp-ints from incoming group packet")); + crStop(0); + } + ssh->kex_ctx = dh_setup_gex(s->p, s->g); + s->kex_init_value = SSH2_MSG_KEX_DH_GEX_INIT; + s->kex_reply_value = SSH2_MSG_KEX_DH_GEX_REPLY; + } else { + ssh->pkt_ctx |= SSH2_PKTCTX_DHGROUP; + ssh->kex_ctx = dh_setup_group(ssh->kex); + s->kex_init_value = SSH2_MSG_KEXDH_INIT; + s->kex_reply_value = SSH2_MSG_KEXDH_REPLY; + logeventf(ssh, "Using Diffie-Hellman with standard group \"%s\"", + ssh->kex->groupname); + } - logeventf(ssh, "Doing Diffie-Hellman key exchange with hash %s", - ssh->kex->hash->text_name); - /* - * Now generate and send e for Diffie-Hellman. - */ - set_busy_status(ssh->frontend, BUSY_CPU); /* this can take a while */ - s->e = dh_create_e(ssh->kex_ctx, s->nbits * 2); - s->pktout = ssh2_pkt_init(s->kex_init_value); - ssh2_pkt_addmp(s->pktout, s->e); - ssh2_pkt_send_noqueue(ssh, s->pktout); + logeventf(ssh, "Doing Diffie-Hellman key exchange with hash %s", + ssh->kex->hash->text_name); + /* + * Now generate and send e for Diffie-Hellman. + */ + set_busy_status(ssh->frontend, BUSY_CPU); /* this can take a while */ + s->e = dh_create_e(ssh->kex_ctx, s->nbits * 2); + s->pktout = ssh2_pkt_init(s->kex_init_value); + ssh2_pkt_addmp(s->pktout, s->e); + ssh2_pkt_send_noqueue(ssh, s->pktout); - set_busy_status(ssh->frontend, BUSY_WAITING); /* wait for server */ - crWaitUntil(pktin); - if (pktin->type != s->kex_reply_value) { - bombout(("expected key exchange reply packet from server")); - crStop(0); - } - set_busy_status(ssh->frontend, BUSY_CPU); /* cogitate */ - ssh_pkt_getstring(pktin, &s->hostkeydata, &s->hostkeylen); - s->hkey = ssh->hostkey->newkey(s->hostkeydata, s->hostkeylen); - s->f = ssh2_pkt_getmp(pktin); - if (!s->f) { - bombout(("unable to parse key exchange reply packet")); - crStop(0); - } - ssh_pkt_getstring(pktin, &s->sigdata, &s->siglen); + set_busy_status(ssh->frontend, BUSY_WAITING); /* wait for server */ + crWaitUntil(pktin); + if (pktin->type != s->kex_reply_value) { + bombout(("expected key exchange reply packet from server")); + crStop(0); + } + set_busy_status(ssh->frontend, BUSY_CPU); /* cogitate */ + ssh_pkt_getstring(pktin, &s->hostkeydata, &s->hostkeylen); + s->hkey = ssh->hostkey->newkey(s->hostkeydata, s->hostkeylen); + s->f = ssh2_pkt_getmp(pktin); + if (!s->f) { + bombout(("unable to parse key exchange reply packet")); + crStop(0); + } + ssh_pkt_getstring(pktin, &s->sigdata, &s->siglen); - s->K = dh_find_K(ssh->kex_ctx, s->f); + s->K = dh_find_K(ssh->kex_ctx, s->f); - /* We assume everything from now on will be quick, and it might - * involve user interaction. */ - set_busy_status(ssh->frontend, BUSY_NOT); + /* We assume everything from now on will be quick, and it might + * involve user interaction. */ + set_busy_status(ssh->frontend, BUSY_NOT); - hash_string(ssh->kex->hash, ssh->exhash, s->hostkeydata, s->hostkeylen); - if (!ssh->kex->pdata) { - hash_uint32(ssh->kex->hash, ssh->exhash, s->pbits); - hash_mpint(ssh->kex->hash, ssh->exhash, s->p); - hash_mpint(ssh->kex->hash, ssh->exhash, s->g); - } - hash_mpint(ssh->kex->hash, ssh->exhash, s->e); - hash_mpint(ssh->kex->hash, ssh->exhash, s->f); + hash_string(ssh->kex->hash, ssh->exhash, s->hostkeydata, s->hostkeylen); + if (!ssh->kex->pdata) { + hash_uint32(ssh->kex->hash, ssh->exhash, s->pbits); + hash_mpint(ssh->kex->hash, ssh->exhash, s->p); + hash_mpint(ssh->kex->hash, ssh->exhash, s->g); + } + hash_mpint(ssh->kex->hash, ssh->exhash, s->e); + hash_mpint(ssh->kex->hash, ssh->exhash, s->f); - dh_cleanup(ssh->kex_ctx); - freebn(s->f); - if (!ssh->kex->pdata) { - freebn(s->g); - freebn(s->p); - } - /* XXX end incorrectly-indented section */ + dh_cleanup(ssh->kex_ctx); + freebn(s->f); + if (!ssh->kex->pdata) { + freebn(s->g); + freebn(s->p); + } } else { logeventf(ssh, "Doing RSA key exchange with hash %s", ssh->kex->hash->text_name); From f2f717bb47a041c476b99192d355eddbac2bb5db Mon Sep 17 00:00:00 2001 From: Jacob Nevins Date: Tue, 1 May 2007 13:14:23 +0000 Subject: [PATCH 059/124] Since r7496, Pageant needs sshsh256 to build (although it doesn't need SHA-256 to actually do its job). [originally from svn r7500] [r7496 == dad558a1e59c059b7f3436a4865b5e4e17c59539] --- Recipe | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Recipe b/Recipe index 8bb3d169..fab512bc 100644 --- a/Recipe +++ b/Recipe @@ -307,8 +307,8 @@ psftp : [C] psftp winsftp wincons WINSSH BE_SSH SFTP wildcard WINMISC + psftp.res LIBS pageant : [G] winpgnt sshrsa sshpubk sshdes sshbn sshmd5 version tree234 - + misc sshaes sshsha winpgntc sshdss sshsh512 winutils winmisc - + winhelp pageant.res LIBS + + misc sshaes sshsha winpgntc sshdss sshsh256 sshsh512 winutils + + winmisc winhelp pageant.res LIBS puttygen : [G] winpgen sshrsag sshdssg sshprime sshdes sshbn sshmd5 version + sshrand winnoise sshsha winstore misc winctrls sshrsa sshdss winmisc From 3814ef0725dfef8d6b7e3bb74d62ddcf72ab16fc Mon Sep 17 00:00:00 2001 From: Ben Harris Date: Tue, 1 May 2007 20:29:11 +0000 Subject: [PATCH 060/124] Make bounds of automatic array constant. [originally from svn r7503] --- sshrsa.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sshrsa.c b/sshrsa.c index 6db265ee..2dc09d1c 100644 --- a/sshrsa.c +++ b/sshrsa.c @@ -863,8 +863,9 @@ static void oaep_mask(const struct ssh_hash *h, void *seed, int seedlen, while (datalen > 0) { int i, max = (datalen > h->hlen ? h->hlen : datalen); void *s; - unsigned char counter[4], hash[h->hlen]; + unsigned char counter[4], hash[SSH2_KEX_MAX_HASH_LEN]; + assert(h->hlen <= SSH2_KEX_MAX_HASH_LEN); PUT_32BIT(counter, count); s = h->init(); h->bytes(s, seed, seedlen); From 7eaa5e1c44d03efba2d07a58dde13877557f9444 Mon Sep 17 00:00:00 2001 From: Ben Harris Date: Wed, 9 May 2007 21:35:24 +0000 Subject: [PATCH 061/124] These days, you _can_ save a host name in Default Settings. [originally from svn r7566] --- doc/config.but | 7 ------- 1 file changed, 7 deletions(-) diff --git a/doc/config.but b/doc/config.but index e5d42e9e..5f74a2cf 100644 --- a/doc/config.but +++ b/doc/config.but @@ -61,13 +61,6 @@ you want them saved. Then come back to the Session panel. Select the \q{\i{Default Settings}} entry in the saved sessions list, with a single click. Then press the \q{Save} button. -\lcont{ -Note that PuTTY does not allow you to save a host name into the -Default Settings entry. This ensures that when PuTTY is started up, -the host name box is always empty, so a user can always just type in -a host name and connect. -} - If there is a specific host you want to store the details of how to connect to, you should create a saved session, which will be separate from the Default Settings. From aeee77998ee67ead931e6ddc5257bc0c6c84cbb2 Mon Sep 17 00:00:00 2001 From: Simon Tatham Date: Tue, 22 May 2007 18:37:17 +0000 Subject: [PATCH 062/124] Retire the e-gold link. Nobody's used it in years; I honestly don't know how I'd go about retrieving money from them any more because my last exchange transaction went through a company who subsequently turned out to be dodgy; and a user points out that e-gold is in legal trouble, which suggests that avoiding it is probably wise. [originally from svn r7604] --- doc/faq.but | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/doc/faq.but b/doc/faq.but index 75bc831b..bdc1a70f 100644 --- a/doc/faq.but +++ b/doc/faq.but @@ -1191,11 +1191,8 @@ asking for any. Having said all that, if you still really \e{want} to give us money, we won't argue :-) The easiest way for us to accept donations is if you send money to \cw{} using PayPal -(\W{http://www.paypal.com/}\cw{www.paypal.com}). Alternatively, if -you don't trust PayPal, you could donate through e-gold -(\W{http://www.e-gold.com}\cw{www.e-gold.com}): deposit your -donation in account number 174769, then send us e-mail to let us -know you've done so (otherwise we might not notice for months!). +(\W{http://www.paypal.com/}\cw{www.paypal.com}). If you don't like +PayPal, talk to us; we can probably arrange some alternative means. Small donations (tens of dollars or tens of euros) will probably be spent on beer or curry, which helps motivate our volunteer team to From 3a79eff8e2dcc7a7855e535e29e1ce3512087e18 Mon Sep 17 00:00:00 2001 From: Jacob Nevins Date: Tue, 29 May 2007 20:01:32 +0000 Subject: [PATCH 063/124] Explicitly spell out that "incorrect MAC" type errors can be caused by data corruption in the network. [originally from svn r7609] --- doc/errors.but | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/doc/errors.but b/doc/errors.but index 03392454..2369b4ce 100644 --- a/doc/errors.but +++ b/doc/errors.but @@ -209,6 +209,14 @@ encryption or decryption process. It's difficult to tell from this error message whether the problem is in the client, in the server, or in between. +In particular, if the network is corrupting data at the TCP level, it +may only be obvious with cryptographic protocols such as SSH, which +explicitly check the integrity of the transferred data and complain +loudly if the checks fail. Corruption of protocols without integrity +protection (such as HTTP) will manifest in more subtle failures (such +as misdisplayed text or images in a web browser) which may not be +noticed. + A known server problem which can cause this error is described in \k{faq-openssh-bad-openssl} in the FAQ. From c2bb01fa9348c364c5e875a147c46db7f4b94a8a Mon Sep 17 00:00:00 2001 From: Jacob Nevins Date: Tue, 29 May 2007 20:06:45 +0000 Subject: [PATCH 064/124] Index "MAC" a bit more thoroughly. [originally from svn r7610] --- doc/errors.but | 2 +- doc/index.but | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/errors.but b/doc/errors.but index 2369b4ce..cbca36b3 100644 --- a/doc/errors.but +++ b/doc/errors.but @@ -201,7 +201,7 @@ Upgrade your server, or use the workarounds described in \k{config-ssh-bug-ignore1} and possibly \k{config-ssh-bug-plainpw1}. \H{errors-crc} \q{Incorrect \i{CRC} received on packet} or \q{Incorrect -MAC received on packet} +\i{MAC} received on packet} This error occurs when PuTTY decrypts an SSH packet and its checksum is not correct. This probably means something has gone wrong in the diff --git a/doc/index.but b/doc/index.but index 8e63d82b..5a6d6e3a 100644 --- a/doc/index.but +++ b/doc/index.but @@ -672,8 +672,8 @@ saved sessions from \IM{ignore message} SSH \q{ignore} messages \IM{ignore message} \q{ignore} messages, in SSH -\IM{message authentication code} message authentication code -\IM{message authentication code} MAC (message authentication code) +\IM{message authentication code}{MAC} message authentication code (MAC) +\IM{message authentication code}{MAC} MAC (message authentication code) \IM{signatures} signature \IM{signatures} digital signature From fd26b64c74af3dec75fd0a15059afd2e2ed9a275 Mon Sep 17 00:00:00 2001 From: Simon Tatham Date: Sat, 30 Jun 2007 18:17:12 +0000 Subject: [PATCH 065/124] Tong Ho points out a missing ssh_pkt_ensure(). [originally from svn r7626] --- ssh.c | 1 + 1 file changed, 1 insertion(+) diff --git a/ssh.c b/ssh.c index 21fcd2d9..809f4af4 100644 --- a/ssh.c +++ b/ssh.c @@ -1424,6 +1424,7 @@ static int s_wrpkt_prepare(Ssh ssh, struct Packet *pkt, int *offset_p) zlib_compress_block(ssh->cs_comp_ctx, pkt->data + 12, pkt->length - 12, &compblk, &complen); + ssh_pkt_ensure(pkt, complen + 2); /* just in case it's got bigger */ memcpy(pkt->data + 12, compblk, complen); sfree(compblk); pkt->length = complen + 12; From 90e7bf4228fa74fda1c65cb2597c9d964329f702 Mon Sep 17 00:00:00 2001 From: Simon Tatham Date: Sat, 30 Jun 2007 18:18:20 +0000 Subject: [PATCH 066/124] Fix a couple of signedness compiler warnings, presumably due to me using a different version of gcc from before. [originally from svn r7627] --- ssh.c | 2 +- sshrsa.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ssh.c b/ssh.c index 809f4af4..5196b1b3 100644 --- a/ssh.c +++ b/ssh.c @@ -5756,7 +5756,7 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen, */ s->pktout = ssh2_pkt_init(SSH2_MSG_KEXRSA_SECRET); ssh2_pkt_addstring_start(s->pktout); - ssh2_pkt_addstring_data(s->pktout, outstr, outstrlen); + ssh2_pkt_addstring_data(s->pktout, (char *)outstr, outstrlen); ssh2_pkt_send_noqueue(ssh, s->pktout); hash_string(ssh->kex->hash, ssh->exhash, outstr, outstrlen); diff --git a/sshrsa.c b/sshrsa.c index 2dc09d1c..12229e63 100644 --- a/sshrsa.c +++ b/sshrsa.c @@ -961,7 +961,7 @@ void ssh_rsakex_encrypt(const struct ssh_hash *h, unsigned char *in, int inlen, */ b1 = bignum_from_bytes(out, outlen); b2 = modpow(b1, rsa->exponent, rsa->modulus); - p = out; + p = (char *)out; for (i = outlen; i--;) { *p++ = bignum_byte(b2, i); } From 46c00b0f381f48a9992e43e014e104015952e9da Mon Sep 17 00:00:00 2001 From: Jacob Nevins Date: Sat, 30 Jun 2007 21:56:44 +0000 Subject: [PATCH 067/124] Rationalise access to, and content of, backends[] array. Should be no significant change in behaviour. (Well, entering usernames containing commas on Plink's command line will be a little harder now.) [originally from svn r7628] --- be_all.c | 12 ++++++------ be_all_s.c | 14 ++++++------- be_none.c | 13 ++++-------- be_nos_s.c | 12 ++++++------ be_nossh.c | 10 +++++----- config.c | 22 ++++----------------- mac/mac.c | 9 +++------ mac/macterm.c | 7 +------ macosx/osxwin.m | 10 ++-------- putty.h | 10 +++++----- raw.c | 2 ++ rlogin.c | 2 ++ settings.c | 42 ++++++++++++++++++++++++++++++--------- ssh.c | 2 ++ telnet.c | 2 ++ testback.c | 4 ++-- unix/uxplink.c | 49 +++++++++++++++------------------------------- unix/uxpty.c | 2 ++ unix/uxputty.c | 17 ++++------------ unix/uxser.c | 2 ++ windows/window.c | 16 ++++----------- windows/winplink.c | 48 +++++++++++++++------------------------------ windows/winser.c | 2 ++ 23 files changed, 132 insertions(+), 177 deletions(-) diff --git a/be_all.c b/be_all.c index 024a39e9..c58903cc 100644 --- a/be_all.c +++ b/be_all.c @@ -22,10 +22,10 @@ const int be_default_protocol = PROT_TELNET; const int be_default_protocol = PROT_SSH; #endif -struct backend_list backends[] = { - {PROT_SSH, "ssh", &ssh_backend}, - {PROT_TELNET, "telnet", &telnet_backend}, - {PROT_RLOGIN, "rlogin", &rlogin_backend}, - {PROT_RAW, "raw", &raw_backend}, - {0, NULL} +Backend *backends[] = { + &ssh_backend, + &telnet_backend, + &rlogin_backend, + &raw_backend, + NULL }; diff --git a/be_all_s.c b/be_all_s.c index 4b3c1983..0ffd0737 100644 --- a/be_all_s.c +++ b/be_all_s.c @@ -22,11 +22,11 @@ const int be_default_protocol = PROT_TELNET; const int be_default_protocol = PROT_SSH; #endif -struct backend_list backends[] = { - {PROT_SSH, "ssh", &ssh_backend}, - {PROT_TELNET, "telnet", &telnet_backend}, - {PROT_RLOGIN, "rlogin", &rlogin_backend}, - {PROT_RAW, "raw", &raw_backend}, - {PROT_SERIAL, "serial", &serial_backend}, - {0, NULL} +Backend *backends[] = { + &ssh_backend, + &telnet_backend, + &rlogin_backend, + &raw_backend, + &serial_backend, + NULL }; diff --git a/be_none.c b/be_none.c index 879a20c5..6ec037ac 100644 --- a/be_none.c +++ b/be_none.c @@ -1,16 +1,11 @@ /* - * Linking module for PSCP: list the available backends, but - * without accompanying function suites. Used only for name - * lookups. + * Linking module for programs that do not support selection of backend + * (such as pscp or pterm). */ #include #include "putty.h" -struct backend_list backends[] = { - {PROT_SSH, "ssh", NULL}, - {PROT_TELNET, "telnet", NULL}, - {PROT_RLOGIN, "rlogin", NULL}, - {PROT_RAW, "raw", NULL}, - {0, NULL} +Backend *backends[] = { + NULL }; diff --git a/be_nos_s.c b/be_nos_s.c index ea8cf144..a574ead9 100644 --- a/be_nos_s.c +++ b/be_nos_s.c @@ -10,12 +10,12 @@ const int be_default_protocol = PROT_TELNET; const char *const appname = "PuTTYtel"; -struct backend_list backends[] = { - {PROT_TELNET, "telnet", &telnet_backend}, - {PROT_RLOGIN, "rlogin", &rlogin_backend}, - {PROT_RAW, "raw", &raw_backend}, - {PROT_SERIAL, "serial", &serial_backend}, - {0, NULL} +Backend *backends[] = { + &telnet_backend, + &rlogin_backend, + &raw_backend, + &serial_backend, + NULL }; /* diff --git a/be_nossh.c b/be_nossh.c index 18ba32a7..33d783a8 100644 --- a/be_nossh.c +++ b/be_nossh.c @@ -10,11 +10,11 @@ const int be_default_protocol = PROT_TELNET; const char *const appname = "PuTTYtel"; -struct backend_list backends[] = { - {PROT_TELNET, "telnet", &telnet_backend}, - {PROT_RLOGIN, "rlogin", &rlogin_backend}, - {PROT_RAW, "raw", &raw_backend}, - {0, NULL} +Backend *backends[] = { + &telnet_backend, + &rlogin_backend, + &raw_backend, + NULL }; /* diff --git a/config.c b/config.c index 41bb4943..39740b8d 100644 --- a/config.c +++ b/config.c @@ -15,20 +15,6 @@ #define HOST_BOX_TITLE "Host Name (or IP address)" #define PORT_BOX_TITLE "Port" -/* - * Convenience function: determine whether this binary supports a - * given backend. - */ -static int have_backend(int protocol) -{ - struct backend_list *p = backends; - for (p = backends; p->name; p++) { - if (p->protocol == protocol) - return 1; - } - return 0; -} - static void config_host_handler(union control *ctrl, void *dlg, void *data, int event) { @@ -1166,7 +1152,7 @@ void setup_config_box(struct controlbox *b, int midsession, hp->port = c; ctrl_columns(s, 1, 100); - if (!have_backend(PROT_SSH)) { + if (!backend_from_proto(PROT_SSH)) { ctrl_radiobuttons(s, "Connection type:", NO_SHORTCUT, 3, HELPCTX(session_hostname), config_protocolbuttons_handler, P(hp), @@ -1257,7 +1243,7 @@ void setup_config_box(struct controlbox *b, int midsession, { char *sshlogname, *sshrawlogname; if ((midsession && protocol == PROT_SSH) || - (!midsession && have_backend(PROT_SSH))) { + (!midsession && backend_from_proto(PROT_SSH))) { sshlogname = "SSH packets"; sshrawlogname = "SSH packets and raw data"; } else { @@ -1293,7 +1279,7 @@ void setup_config_box(struct controlbox *b, int midsession, dlg_stdcheckbox_handler, I(offsetof(Config,logflush))); if ((midsession && protocol == PROT_SSH) || - (!midsession && have_backend(PROT_SSH))) { + (!midsession && backend_from_proto(PROT_SSH))) { s = ctrl_getset(b, "Session/Logging", "ssh", "Options specific to SSH packet logging"); ctrl_checkbox(s, "Omit known password fields", 'k', @@ -1912,7 +1898,7 @@ void setup_config_box(struct controlbox *b, int midsession, * when we're not doing SSH. */ - if (have_backend(PROT_SSH) && (!midsession || protocol == PROT_SSH)) { + if (backend_from_proto(PROT_SSH) && (!midsession || protocol == PROT_SSH)) { /* * The Connection/SSH panel. diff --git a/mac/mac.c b/mac/mac.c index 9e1d1f30..25362061 100644 --- a/mac/mac.c +++ b/mac/mac.c @@ -210,13 +210,10 @@ static void mac_startup(void) { default_protocol = be_default_protocol; /* Find the appropriate default port. */ { - int i; + Backend *b = backend_from_proto(default_protocol); default_port = 0; /* illegal */ - for (i = 0; backends[i].backend != NULL; i++) - if (backends[i].protocol == default_protocol) { - default_port = backends[i].backend->default_port; - break; - } + if (b) + default_port = b->default_port; } flags = FLAG_INTERACTIVE; diff --git a/mac/macterm.c b/mac/macterm.c index dad26883..4a75a0c6 100644 --- a/mac/macterm.c +++ b/mac/macterm.c @@ -115,12 +115,7 @@ void mac_startsession(Session *s) * Select protocol. This is farmed out into a table in a * separate file to enable an ssh-free variant. */ - s->back = NULL; - for (i = 0; backends[i].backend != NULL; i++) - if (backends[i].protocol == s->cfg.protocol) { - s->back = backends[i].backend; - break; - } + s->back = backend_from_proto(s->cfg.protocol); if (s->back == NULL) fatalbox("Unsupported protocol number found"); diff --git a/macosx/osxwin.m b/macosx/osxwin.m index 361f548f..f61e6bff 100644 --- a/macosx/osxwin.m +++ b/macosx/osxwin.m @@ -232,15 +232,9 @@ /* * Set up a backend. */ - { - int i; + back = backend_from_proto(cfg.protocol); + if (!back) back = &pty_backend; - for (i = 0; backends[i].backend != NULL; i++) - if (backends[i].protocol == cfg.protocol) { - back = backends[i].backend; - break; - } - } { const char *error; diff --git a/putty.h b/putty.h index 3583ecf0..30b23bf5 100644 --- a/putty.h +++ b/putty.h @@ -389,14 +389,12 @@ struct backend_tag { */ void (*unthrottle) (void *handle, int); int (*cfg_info) (void *handle); + char *name; + int protocol; int default_port; }; -extern struct backend_list { - int protocol; - char *name; - Backend *backend; -} backends[]; +extern Backend *backends[]; /* * Suggested default protocol provided by the backend link module. @@ -778,6 +776,8 @@ void random_destroy_seed(void); /* * Exports from settings.c. */ +Backend *backend_from_name(const char *name); +Backend *backend_from_proto(int proto); char *save_settings(char *section, Config * cfg); void save_open_settings(void *sesskey, Config *cfg); void load_settings(char *section, Config * cfg); diff --git a/raw.c b/raw.c index d64b6d7f..49ca1ce2 100644 --- a/raw.c +++ b/raw.c @@ -278,5 +278,7 @@ Backend raw_backend = { raw_provide_logctx, raw_unthrottle, raw_cfg_info, + "raw", + PROT_RAW, 1 }; diff --git a/rlogin.c b/rlogin.c index 833d4ea4..f9a546e9 100644 --- a/rlogin.c +++ b/rlogin.c @@ -349,5 +349,7 @@ Backend rlogin_backend = { rlogin_provide_logctx, rlogin_unthrottle, rlogin_cfg_info, + "rlogin", + PROT_RLOGIN, 1 }; diff --git a/settings.c b/settings.c index c937d3c9..44b02cba 100644 --- a/settings.c +++ b/settings.c @@ -52,6 +52,29 @@ const char *const ttymodes[] = { "CS8", "PARENB", "PARODD", NULL }; +/* + * Convenience functions to access the backends[] array + * (which is only present in tools that manage settings). + */ + +Backend *backend_from_name(const char *name) +{ + Backend **p; + for (p = backends; *p != NULL; p++) + if (!strcmp((*p)->name, name)) + return *p; + return NULL; +} + +Backend *backend_from_proto(int proto) +{ + Backend **p; + for (p = backends; *p != NULL; p++) + if ((*p)->protocol == proto) + return *p; + return NULL; +} + static void gpps(void *handle, const char *name, const char *def, char *val, int len) { @@ -259,11 +282,11 @@ void save_open_settings(void *sesskey, Config *cfg) write_setting_i(sesskey, "SSHLogOmitPasswords", cfg->logomitpass); write_setting_i(sesskey, "SSHLogOmitData", cfg->logomitdata); p = "raw"; - for (i = 0; backends[i].name != NULL; i++) - if (backends[i].protocol == cfg->protocol) { - p = backends[i].name; - break; - } + { + const Backend *b = backend_from_proto(cfg->protocol); + if (b) + p = b->name; + } write_setting_s(sesskey, "Protocol", p); write_setting_i(sesskey, "PortNumber", cfg->port); /* The CloseOnExit numbers are arranged in a different order from @@ -476,12 +499,13 @@ void load_open_settings(void *sesskey, Config *cfg) gpps(sesskey, "Protocol", "default", prot, 10); cfg->protocol = default_protocol; cfg->port = default_port; - for (i = 0; backends[i].name != NULL; i++) - if (!strcmp(prot, backends[i].name)) { - cfg->protocol = backends[i].protocol; + { + const Backend *b = backend_from_name(prot); + if (b) { + cfg->protocol = b->protocol; gppi(sesskey, "PortNumber", default_port, &cfg->port); - break; } + } /* Address family selection */ gppi(sesskey, "AddressFamily", ADDRTYPE_UNSPEC, &cfg->addressfamily); diff --git a/ssh.c b/ssh.c index 5196b1b3..e5d2364d 100644 --- a/ssh.c +++ b/ssh.c @@ -9146,5 +9146,7 @@ Backend ssh_backend = { ssh_provide_logctx, ssh_unthrottle, ssh_cfg_info, + "ssh", + PROT_SSH, 22 }; diff --git a/telnet.c b/telnet.c index e6ad83e0..eeaa76f3 100644 --- a/telnet.c +++ b/telnet.c @@ -1106,5 +1106,7 @@ Backend telnet_backend = { telnet_provide_logctx, telnet_unthrottle, telnet_cfg_info, + "telnet", + PROT_TELNET, 23 }; diff --git a/testback.c b/testback.c index 159cdc54..0175e8e7 100644 --- a/testback.c +++ b/testback.c @@ -59,14 +59,14 @@ Backend null_backend = { null_init, null_free, null_reconfig, null_send, null_sendbuffer, null_size, null_special, null_get_specials, null_connected, null_exitcode, null_sendok, null_ldisc, null_provide_ldisc, null_provide_logctx, null_unthrottle, - null_cfg_info, 0 + null_cfg_info, "null", -1, 0 }; Backend loop_backend = { loop_init, loop_free, null_reconfig, loop_send, null_sendbuffer, null_size, null_special, null_get_specials, null_connected, null_exitcode, null_sendok, null_ldisc, null_provide_ldisc, null_provide_logctx, null_unthrottle, - null_cfg_info, 0 + null_cfg_info, "loop", -1, 0 }; struct loop_state { diff --git a/unix/uxplink.c b/unix/uxplink.c index e9c6c9e6..6b56d2af 100644 --- a/unix/uxplink.c +++ b/unix/uxplink.c @@ -596,15 +596,11 @@ int main(int argc, char **argv) * Override the default protocol if PLINK_PROTOCOL is set. */ char *p = getenv("PLINK_PROTOCOL"); - int i; if (p) { - for (i = 0; backends[i].backend != NULL; i++) { - if (!strcmp(backends[i].name, p)) { - default_protocol = cfg.protocol = backends[i].protocol; - default_port = cfg.port = - backends[i].backend->default_port; - break; - } + const Backend *b = backend_from_name(p); + if (b) { + default_protocol = cfg.protocol = b->protocol; + default_port = cfg.port = b->default_port; } } } @@ -681,19 +677,14 @@ int main(int argc, char **argv) */ r = strchr(p, ','); if (r) { - int i, j; - for (i = 0; backends[i].backend != NULL; i++) { - j = strlen(backends[i].name); - if (j == r - p && - !memcmp(backends[i].name, p, j)) { - default_protocol = cfg.protocol = - backends[i].protocol; - portnumber = - backends[i].backend->default_port; - p = r + 1; - break; - } + const Backend *b; + *r = '\0'; + b = backend_from_name(p); + if (b) { + default_protocol = cfg.protocol = b->protocol; + portnumber = b->default_port; } + p = r + 1; } /* @@ -836,19 +827,11 @@ int main(int argc, char **argv) * Select protocol. This is farmed out into a table in a * separate file to enable an ssh-free variant. */ - { - int i; - back = NULL; - for (i = 0; backends[i].backend != NULL; i++) - if (backends[i].protocol == cfg.protocol) { - back = backends[i].backend; - break; - } - if (back == NULL) { - fprintf(stderr, - "Internal fault: Unsupported protocol found\n"); - return 1; - } + back = backend_from_proto(cfg.protocol); + if (back == NULL) { + fprintf(stderr, + "Internal fault: Unsupported protocol found\n"); + return 1; } /* diff --git a/unix/uxpty.c b/unix/uxpty.c index 708e82d6..2e165cf2 100644 --- a/unix/uxpty.c +++ b/unix/uxpty.c @@ -1085,5 +1085,7 @@ Backend pty_backend = { pty_provide_logctx, pty_unthrottle, pty_cfg_info, + "pty", + -1, 1 }; diff --git a/unix/uxputty.c b/unix/uxputty.c index 54e0d71b..f42c428d 100644 --- a/unix/uxputty.c +++ b/unix/uxputty.c @@ -33,13 +33,7 @@ void cleanup_exit(int code) Backend *select_backend(Config *cfg) { - int i; - Backend *back = NULL; - for (i = 0; backends[i].backend != NULL; i++) - if (backends[i].protocol == cfg->protocol) { - back = backends[i].backend; - break; - } + Backend *back = backend_from_proto(cfg->protocol); assert(back != NULL); return back; } @@ -137,13 +131,10 @@ int main(int argc, char **argv) default_protocol = be_default_protocol; /* Find the appropriate default port. */ { - int i; + Backend *b = backend_from_proto(default_protocol); default_port = 0; /* illegal */ - for (i = 0; backends[i].backend != NULL; i++) - if (backends[i].protocol == default_protocol) { - default_port = backends[i].backend->default_port; - break; - } + if (b) + default_port = b->default_port; } return pt_main(argc, argv); } diff --git a/unix/uxser.c b/unix/uxser.c index 24b0124c..a12fdec7 100644 --- a/unix/uxser.c +++ b/unix/uxser.c @@ -536,5 +536,7 @@ Backend serial_backend = { serial_provide_logctx, serial_unthrottle, serial_cfg_info, + "serial", + PROT_SERIAL, 1 }; diff --git a/windows/window.c b/windows/window.c index 1df5feeb..1d4b7152 100644 --- a/windows/window.c +++ b/windows/window.c @@ -219,12 +219,7 @@ static void start_backend(void) * Select protocol. This is farmed out into a table in a * separate file to enable an ssh-free variant. */ - back = NULL; - for (i = 0; backends[i].backend != NULL; i++) - if (backends[i].protocol == cfg.protocol) { - back = backends[i].backend; - break; - } + back = backend_from_proto(cfg.protocol); if (back == NULL) { char *str = dupprintf("%s Internal Error", appname); MessageBox(NULL, "Unsupported protocol number found", @@ -369,13 +364,10 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) default_protocol = be_default_protocol; /* Find the appropriate default port. */ { - int i; + Backend *b = backend_from_proto(default_protocol); default_port = 0; /* illegal */ - for (i = 0; backends[i].backend != NULL; i++) - if (backends[i].protocol == default_protocol) { - default_port = backends[i].backend->default_port; - break; - } + if (b) + default_port = b->default_port; } cfg.logtype = LGTYP_NONE; diff --git a/windows/winplink.c b/windows/winplink.c index 42426000..60232ffe 100644 --- a/windows/winplink.c +++ b/windows/winplink.c @@ -307,13 +307,10 @@ int main(int argc, char **argv) char *p = getenv("PLINK_PROTOCOL"); int i; if (p) { - for (i = 0; backends[i].backend != NULL; i++) { - if (!strcmp(backends[i].name, p)) { - default_protocol = cfg.protocol = backends[i].protocol; - default_port = cfg.port = - backends[i].backend->default_port; - break; - } + const Backend *b = backend_from_name(p); + if (b) { + default_protocol = cfg.protocol = b->protocol; + default_port = cfg.port = b->default_port; } } } @@ -380,19 +377,14 @@ int main(int argc, char **argv) */ r = strchr(p, ','); if (r) { - int i, j; - for (i = 0; backends[i].backend != NULL; i++) { - j = strlen(backends[i].name); - if (j == r - p && - !memcmp(backends[i].name, p, j)) { - default_protocol = cfg.protocol = - backends[i].protocol; - portnumber = - backends[i].backend->default_port; - p = r + 1; - break; - } + const Backend *b; + *r = '\0'; + b = backend_from_name(p); + if (b) { + default_protocol = cfg.protocol = b->protocol; + portnumber = b->default_port; } + p = r + 1; } /* @@ -535,19 +527,11 @@ int main(int argc, char **argv) * Select protocol. This is farmed out into a table in a * separate file to enable an ssh-free variant. */ - { - int i; - back = NULL; - for (i = 0; backends[i].backend != NULL; i++) - if (backends[i].protocol == cfg.protocol) { - back = backends[i].backend; - break; - } - if (back == NULL) { - fprintf(stderr, - "Internal fault: Unsupported protocol found\n"); - return 1; - } + back = backend_from_proto(cfg.protocol); + if (back == NULL) { + fprintf(stderr, + "Internal fault: Unsupported protocol found\n"); + return 1; } /* diff --git a/windows/winser.c b/windows/winser.c index 9e6415e3..1188c46a 100644 --- a/windows/winser.c +++ b/windows/winser.c @@ -454,5 +454,7 @@ Backend serial_backend = { serial_provide_logctx, serial_unthrottle, serial_cfg_info, + "serial", + PROT_SERIAL, 1 }; From 6c14388c1d76c996318438d270afc91c76a076ce Mon Sep 17 00:00:00 2001 From: Jacob Nevins Date: Sun, 1 Jul 2007 15:41:09 +0000 Subject: [PATCH 068/124] Remove port number validation from Windows PuTTY -- it could cause unnecessary trouble with serial connections, and a port number of zero gets caught later anyway. [originally from svn r7634] --- windows/window.c | 9 --------- 1 file changed, 9 deletions(-) diff --git a/windows/window.c b/windows/window.c index 1d4b7152..033cfa5d 100644 --- a/windows/window.c +++ b/windows/window.c @@ -597,15 +597,6 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) } } - /* Check for invalid Port number (i.e. zero) */ - if (cfg.port == 0) { - char *str = dupprintf("%s Internal Error", appname); - MessageBox(NULL, "Invalid Port Number", - str, MB_OK | MB_ICONEXCLAMATION); - sfree(str); - cleanup_exit(1); - } - if (!prev) { wndclass.style = 0; wndclass.lpfnWndProc = WndProc; From db7cc1cba6dbd229db63d8ab1b2b86125c5d5933 Mon Sep 17 00:00:00 2001 From: Jacob Nevins Date: Sun, 1 Jul 2007 15:47:31 +0000 Subject: [PATCH 069/124] Implement Marcin Bulandra's suggestion of only automatically updating the port number in the GUI when the connection type is changed if the current port number is the standard one for the current protocol. It's not perfect, but it should make the common case of tabbing through the Session panel easier when starting non-SSH connections on odd ports. [originally from svn r7635] --- config.c | 25 +++++++++++++++---------- raw.c | 2 +- rlogin.c | 2 +- unix/uxpty.c | 2 +- unix/uxser.c | 2 +- windows/winser.c | 2 +- 6 files changed, 20 insertions(+), 15 deletions(-) diff --git a/config.c b/config.c index 39740b8d..1fedd284 100644 --- a/config.c +++ b/config.c @@ -90,7 +90,7 @@ struct hostport { void config_protocolbuttons_handler(union control *ctrl, void *dlg, void *data, int event) { - int button, defport; + int button; Config *cfg = (Config *)data; struct hostport *hp = (struct hostport *)ctrl->radio.context.p; @@ -114,15 +114,20 @@ void config_protocolbuttons_handler(union control *ctrl, void *dlg, assert(button >= 0 && button < ctrl->radio.nbuttons); cfg->protocol = ctrl->radio.buttondata[button].i; if (oldproto != cfg->protocol) { - defport = -1; - switch (cfg->protocol) { - case PROT_SSH: defport = 22; break; - case PROT_TELNET: defport = 23; break; - case PROT_RLOGIN: defport = 513; break; - } - if (defport > 0 && cfg->port != defport) { - cfg->port = defport; - } + Backend *ob = backend_from_proto(oldproto); + Backend *nb = backend_from_proto(cfg->protocol); + assert(ob); + assert(nb); + /* Iff the user hasn't changed the port from the protocol + * default (if any), update it with the new protocol's + * default. + * (XXX: this isn't perfect; a default can become permanent + * by going via the serial backend. However, it helps with + * the common case of tabbing through the controls in order + * and setting a non-default port.) */ + if (cfg->port == ob->default_port && + cfg->port > 0 && nb->default_port > 0) + cfg->port = nb->default_port; } dlg_refresh(hp->host, dlg); dlg_refresh(hp->port, dlg); diff --git a/raw.c b/raw.c index 49ca1ce2..b4b1108f 100644 --- a/raw.c +++ b/raw.c @@ -280,5 +280,5 @@ Backend raw_backend = { raw_cfg_info, "raw", PROT_RAW, - 1 + 0 }; diff --git a/rlogin.c b/rlogin.c index f9a546e9..e40f1597 100644 --- a/rlogin.c +++ b/rlogin.c @@ -351,5 +351,5 @@ Backend rlogin_backend = { rlogin_cfg_info, "rlogin", PROT_RLOGIN, - 1 + 513 }; diff --git a/unix/uxpty.c b/unix/uxpty.c index 2e165cf2..60dc7f7d 100644 --- a/unix/uxpty.c +++ b/unix/uxpty.c @@ -1087,5 +1087,5 @@ Backend pty_backend = { pty_cfg_info, "pty", -1, - 1 + 0 }; diff --git a/unix/uxser.c b/unix/uxser.c index a12fdec7..92961a7d 100644 --- a/unix/uxser.c +++ b/unix/uxser.c @@ -538,5 +538,5 @@ Backend serial_backend = { serial_cfg_info, "serial", PROT_SERIAL, - 1 + 0 }; diff --git a/windows/winser.c b/windows/winser.c index 1188c46a..ab88406d 100644 --- a/windows/winser.c +++ b/windows/winser.c @@ -456,5 +456,5 @@ Backend serial_backend = { serial_cfg_info, "serial", PROT_SERIAL, - 1 + 0 }; From 3ac841ff6fc3f2964c7c63d0be740019d9cb22a2 Mon Sep 17 00:00:00 2001 From: Ben Harris Date: Wed, 18 Jul 2007 22:54:31 +0000 Subject: [PATCH 070/124] Update an outdated comment. [originally from svn r7642] --- ssh.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ssh.c b/ssh.c index e5d2364d..794ef532 100644 --- a/ssh.c +++ b/ssh.c @@ -2480,7 +2480,7 @@ static int do_ssh_init(Ssh ssh, unsigned char c) if (ssh->version == 2) { size_t len; /* - * Hash our version string and their version string. + * Record our version string and their version string. */ len = strcspn(verstring, "\015\012"); ssh->v_c = snewn(len + 1, char); From 22cde3ee5b1938cf368e24955395c955bdd9daea Mon Sep 17 00:00:00 2001 From: Ben Harris Date: Thu, 19 Jul 2007 23:53:02 +0000 Subject: [PATCH 071/124] Separate out the code for creating and sending SSH version strings so that in the SSH-2-only case, we can send it as soon as we connect rather than waiting for the server's one. Unfortunately, actually doing so will take a little more effort -- there are subtleties to do with having a working log context at the right moment that need to be sorted out. [originally from svn r7645] --- ssh.c | 147 +++++++++++++++++++++++++++++++++++----------------------- 1 file changed, 90 insertions(+), 57 deletions(-) diff --git a/ssh.c b/ssh.c index 794ef532..16be7608 100644 --- a/ssh.c +++ b/ssh.c @@ -2377,6 +2377,47 @@ static void ssh_fix_verstring(char *str) } } +/* + * Send an appropriate SSH version string. + */ +static void ssh_send_verstring(Ssh ssh, char *svers) +{ + char *verstring; + + if (ssh->version == 2) { + /* + * Construct a v2 version string. + */ + verstring = dupprintf("SSH-2.0-%s\015\012", sshver); + } else { + /* + * Construct a v1 version string. + */ + verstring = dupprintf("SSH-%s-%s\012", + (ssh_versioncmp(svers, "1.5") <= 0 ? + svers : "1.5"), + sshver); + } + + ssh_fix_verstring(verstring); + + if (ssh->version == 2) { + size_t len; + /* + * Record our version string. + */ + len = strcspn(verstring, "\015\012"); + ssh->v_c = snewn(len + 1, char); + memcpy(ssh->v_c, verstring, len); + ssh->v_c[len] = 0; + } + + logeventf(ssh, "We claim version: %.*s", + strcspn(verstring, "\015\012"), verstring); + s_write(ssh, verstring, strlen(verstring)); + sfree(verstring); +} + static int do_ssh_init(Ssh ssh, unsigned char c) { struct do_ssh_init_state { @@ -2391,6 +2432,20 @@ static int do_ssh_init(Ssh ssh, unsigned char c) crBegin(ssh->do_ssh_init_crstate); + /* + * If the SSH version number's fixed, set it now, and if it's SSH-2, + * send the version string too. + * + * XXX This isn't actually early enough to be useful, since we only + * get here when the first incoming byte turns up. + */ + if (ssh->cfg.sshprot == 0) + ssh->version = 1; + if (ssh->cfg.sshprot == 3) { + ssh->version = 2; + ssh_send_verstring(ssh, NULL); + } + /* Search for a line beginning with the string "SSH-" in the input. */ for (;;) { if (c != 'S') goto no; @@ -2455,66 +2510,44 @@ static int do_ssh_init(Ssh ssh, unsigned char c) crStop(0); } - { - char *verstring; - - if (s->proto2 && (ssh->cfg.sshprot >= 2 || !s->proto1)) { - /* - * Construct a v2 version string. - */ - verstring = dupprintf("SSH-2.0-%s\015\012", sshver); - ssh->version = 2; - } else { - /* - * Construct a v1 version string. - */ - verstring = dupprintf("SSH-%s-%s\012", - (ssh_versioncmp(s->version, "1.5") <= 0 ? - s->version : "1.5"), - sshver); - ssh->version = 1; - } - - ssh_fix_verstring(verstring); - - if (ssh->version == 2) { - size_t len; - /* - * Record our version string and their version string. - */ - len = strcspn(verstring, "\015\012"); - ssh->v_c = snewn(len + 1, char); - memcpy(ssh->v_c, verstring, len); - ssh->v_c[len] = 0; - len = strcspn(s->vstring, "\015\012"); - ssh->v_s = snewn(len + 1, char); - memcpy(ssh->v_s, s->vstring, len); - ssh->v_s[len] = 0; - - /* - * Initialise SSH-2 protocol. - */ - ssh->protocol = ssh2_protocol; - ssh2_protocol_setup(ssh); - ssh->s_rdpkt = ssh2_rdpkt; - } else { - /* - * Initialise SSH-1 protocol. - */ - ssh->protocol = ssh1_protocol; - ssh1_protocol_setup(ssh); - ssh->s_rdpkt = ssh1_rdpkt; - } - logeventf(ssh, "We claim version: %.*s", - strcspn(verstring, "\015\012"), verstring); - s_write(ssh, verstring, strlen(verstring)); - sfree(verstring); - if (ssh->version == 2) - do_ssh2_transport(ssh, NULL, -1, NULL); - } + if (s->proto2 && (ssh->cfg.sshprot >= 2 || !s->proto1)) + ssh->version = 2; + else + ssh->version = 1; logeventf(ssh, "Using SSH protocol version %d", ssh->version); + /* Send the version string, if we haven't already */ + if (ssh->cfg.sshprot != 3) + ssh_send_verstring(ssh, s->version); + + if (ssh->version == 2) { + size_t len; + /* + * Record their version string. + */ + len = strcspn(s->vstring, "\015\012"); + ssh->v_s = snewn(len + 1, char); + memcpy(ssh->v_s, s->vstring, len); + ssh->v_s[len] = 0; + + /* + * Initialise SSH-2 protocol. + */ + ssh->protocol = ssh2_protocol; + ssh2_protocol_setup(ssh); + ssh->s_rdpkt = ssh2_rdpkt; + } else { + /* + * Initialise SSH-1 protocol. + */ + ssh->protocol = ssh1_protocol; + ssh1_protocol_setup(ssh); + ssh->s_rdpkt = ssh1_rdpkt; + } + if (ssh->version == 2) + do_ssh2_transport(ssh, NULL, -1, NULL); + update_specials_menu(ssh->frontend); ssh->state = SSH_STATE_BEFORE_SIZE; ssh->pinger = pinger_new(&ssh->cfg, &ssh_backend, ssh); From c1459927df4cb40a2d2a45d63afdddc27a0f6edc Mon Sep 17 00:00:00 2001 From: Ben Harris Date: Sat, 21 Jul 2007 13:43:57 +0000 Subject: [PATCH 072/124] Arrange that log_packet() isn't called for raw data logging if logctx is null. This allows us to send data in ssh_init(), albeit at the expense of its not being properly logged, so arrange to send the version string then if that's sensible, which should reduce the number of round-trips required to bring up an SSH-2 connection. [originally from svn r7646] --- ssh.c | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/ssh.c b/ssh.c index 16be7608..020021de 100644 --- a/ssh.c +++ b/ssh.c @@ -1453,7 +1453,8 @@ static int s_wrpkt_prepare(Ssh ssh, struct Packet *pkt, int *offset_p) static int s_write(Ssh ssh, void *data, int len) { - log_packet(ssh->logctx, PKT_OUTGOING, -1, NULL, data, len, 0, NULL); + if (ssh->logctx) + log_packet(ssh->logctx, PKT_OUTGOING, -1, NULL, data, len, 0, NULL); return sk_write(ssh->s, (char *)data, len); } @@ -2432,20 +2433,6 @@ static int do_ssh_init(Ssh ssh, unsigned char c) crBegin(ssh->do_ssh_init_crstate); - /* - * If the SSH version number's fixed, set it now, and if it's SSH-2, - * send the version string too. - * - * XXX This isn't actually early enough to be useful, since we only - * get here when the first incoming byte turns up. - */ - if (ssh->cfg.sshprot == 0) - ssh->version = 1; - if (ssh->cfg.sshprot == 3) { - ssh->version = 2; - ssh_send_verstring(ssh, NULL); - } - /* Search for a line beginning with the string "SSH-" in the input. */ for (;;) { if (c != 'S') goto no; @@ -2606,7 +2593,9 @@ static void ssh_set_frozen(Ssh ssh, int frozen) static void ssh_gotdata(Ssh ssh, unsigned char *data, int datalen) { /* Log raw data, if we're in that mode. */ - log_packet(ssh->logctx, PKT_INCOMING, -1, NULL, data, datalen, 0, NULL); + if (ssh->logctx) + log_packet(ssh->logctx, PKT_INCOMING, -1, NULL, data, datalen, + 0, NULL); crBegin(ssh->ssh_gotdata_crstate); @@ -2831,6 +2820,17 @@ static const char *connect_to_host(Ssh ssh, char *host, int port, return err; } + /* + * If the SSH version number's fixed, set it now, and if it's SSH-2, + * send the version string too. + */ + if (ssh->cfg.sshprot == 0) + ssh->version = 1; + if (ssh->cfg.sshprot == 3) { + ssh->version = 2; + ssh_send_verstring(ssh, NULL); + } + return NULL; } From 77da96c62f67871bfde01f48558b0e22882056f4 Mon Sep 17 00:00:00 2001 From: Ben Harris Date: Sat, 21 Jul 2007 21:39:36 +0000 Subject: [PATCH 073/124] Split pkt_ctx into a separate enumeration for each of kex and userauth instead of a bitfield for both. This doesn't gain much here, but it should make it easier to make things other than logging use the context. [originally from svn r7647] --- ssh.c | 86 +++++++++++++++++++++++++++++++---------------------------- 1 file changed, 45 insertions(+), 41 deletions(-) diff --git a/ssh.c b/ssh.c index 020021de..3476d3d8 100644 --- a/ssh.c +++ b/ssh.c @@ -113,14 +113,18 @@ * Packet type contexts, so that ssh2_pkt_type can correctly decode * the ambiguous type numbers back into the correct type strings. */ -#define SSH2_PKTCTX_DHGROUP 0x0001 -#define SSH2_PKTCTX_DHGEX 0x0002 -#define SSH2_PKTCTX_RSAKEX 0x0004 -#define SSH2_PKTCTX_KEX_MASK 0x000F -#define SSH2_PKTCTX_PUBLICKEY 0x0010 -#define SSH2_PKTCTX_PASSWORD 0x0020 -#define SSH2_PKTCTX_KBDINTER 0x0040 -#define SSH2_PKTCTX_AUTH_MASK 0x00F0 +typedef enum { + SSH2_PKTCTX_NOKEX, + SSH2_PKTCTX_DHGROUP, + SSH2_PKTCTX_DHGEX, + SSH2_PKTCTX_RSAKEX +} Pkt_KCtx; +typedef enum { + SSH2_PKTCTX_NOAUTH, + SSH2_PKTCTX_PUBLICKEY, + SSH2_PKTCTX_PASSWORD, + SSH2_PKTCTX_KBDINTER +} Pkt_ACtx; #define SSH2_DISCONNECT_HOST_NOT_ALLOWED_TO_CONNECT 1 /* 0x1 */ #define SSH2_DISCONNECT_PROTOCOL_ERROR 2 /* 0x2 */ @@ -281,7 +285,8 @@ static unsigned int ssh_tty_parse_boolean(char *s) } #define translate(x) if (type == x) return #x -#define translatec(x,ctx) if (type == x && (pkt_ctx & ctx)) return #x +#define translatek(x,ctx) if (type == x && (pkt_kctx == ctx)) return #x +#define translatea(x,ctx) if (type == x && (pkt_actx == ctx)) return #x static char *ssh1_pkt_type(int type) { translate(SSH1_MSG_DISCONNECT); @@ -327,7 +332,7 @@ static char *ssh1_pkt_type(int type) translate(SSH1_CMSG_AUTH_CCARD_RESPONSE); return "unknown"; } -static char *ssh2_pkt_type(int pkt_ctx, int type) +static char *ssh2_pkt_type(Pkt_KCtx pkt_kctx, Pkt_ACtx pkt_actx, int type) { translate(SSH2_MSG_DISCONNECT); translate(SSH2_MSG_IGNORE); @@ -337,23 +342,23 @@ static char *ssh2_pkt_type(int pkt_ctx, int type) translate(SSH2_MSG_SERVICE_ACCEPT); translate(SSH2_MSG_KEXINIT); translate(SSH2_MSG_NEWKEYS); - translatec(SSH2_MSG_KEXDH_INIT, SSH2_PKTCTX_DHGROUP); - translatec(SSH2_MSG_KEXDH_REPLY, SSH2_PKTCTX_DHGROUP); - translatec(SSH2_MSG_KEX_DH_GEX_REQUEST, SSH2_PKTCTX_DHGEX); - translatec(SSH2_MSG_KEX_DH_GEX_GROUP, SSH2_PKTCTX_DHGEX); - translatec(SSH2_MSG_KEX_DH_GEX_INIT, SSH2_PKTCTX_DHGEX); - translatec(SSH2_MSG_KEX_DH_GEX_REPLY, SSH2_PKTCTX_DHGEX); - translatec(SSH2_MSG_KEXRSA_PUBKEY, SSH2_PKTCTX_RSAKEX); - translatec(SSH2_MSG_KEXRSA_SECRET, SSH2_PKTCTX_RSAKEX); - translatec(SSH2_MSG_KEXRSA_DONE, SSH2_PKTCTX_RSAKEX); + translatek(SSH2_MSG_KEXDH_INIT, SSH2_PKTCTX_DHGROUP); + translatek(SSH2_MSG_KEXDH_REPLY, SSH2_PKTCTX_DHGROUP); + translatek(SSH2_MSG_KEX_DH_GEX_REQUEST, SSH2_PKTCTX_DHGEX); + translatek(SSH2_MSG_KEX_DH_GEX_GROUP, SSH2_PKTCTX_DHGEX); + translatek(SSH2_MSG_KEX_DH_GEX_INIT, SSH2_PKTCTX_DHGEX); + translatek(SSH2_MSG_KEX_DH_GEX_REPLY, SSH2_PKTCTX_DHGEX); + translatek(SSH2_MSG_KEXRSA_PUBKEY, SSH2_PKTCTX_RSAKEX); + translatek(SSH2_MSG_KEXRSA_SECRET, SSH2_PKTCTX_RSAKEX); + translatek(SSH2_MSG_KEXRSA_DONE, SSH2_PKTCTX_RSAKEX); translate(SSH2_MSG_USERAUTH_REQUEST); translate(SSH2_MSG_USERAUTH_FAILURE); translate(SSH2_MSG_USERAUTH_SUCCESS); translate(SSH2_MSG_USERAUTH_BANNER); - translatec(SSH2_MSG_USERAUTH_PK_OK, SSH2_PKTCTX_PUBLICKEY); - translatec(SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ, SSH2_PKTCTX_PASSWORD); - translatec(SSH2_MSG_USERAUTH_INFO_REQUEST, SSH2_PKTCTX_KBDINTER); - translatec(SSH2_MSG_USERAUTH_INFO_RESPONSE, SSH2_PKTCTX_KBDINTER); + translatea(SSH2_MSG_USERAUTH_PK_OK, SSH2_PKTCTX_PUBLICKEY); + translatea(SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ, SSH2_PKTCTX_PASSWORD); + translatea(SSH2_MSG_USERAUTH_INFO_REQUEST, SSH2_PKTCTX_KBDINTER); + translatea(SSH2_MSG_USERAUTH_INFO_RESPONSE, SSH2_PKTCTX_KBDINTER); translate(SSH2_MSG_GLOBAL_REQUEST); translate(SSH2_MSG_REQUEST_SUCCESS); translate(SSH2_MSG_REQUEST_FAILURE); @@ -770,7 +775,8 @@ struct ssh_tag { bufchain banner; /* accumulates banners during do_ssh2_authconn */ - int pkt_ctx; + Pkt_KCtx pkt_kctx; + Pkt_ACtx pkt_actx; void *x11auth; @@ -1387,7 +1393,8 @@ static struct Packet *ssh2_rdpkt(Ssh ssh, unsigned char **data, int *datalen) } } log_packet(ssh->logctx, PKT_INCOMING, st->pktin->type, - ssh2_pkt_type(ssh->pkt_ctx, st->pktin->type), + ssh2_pkt_type(ssh->pkt_kctx, ssh->pkt_actx, + st->pktin->type), st->pktin->data+6, st->pktin->length-6, nblanks, &blank); } @@ -1735,7 +1742,7 @@ static int ssh2_pkt_construct(Ssh ssh, struct Packet *pkt) if (ssh->logctx) log_packet(ssh->logctx, PKT_OUTGOING, pkt->data[5], - ssh2_pkt_type(ssh->pkt_ctx, pkt->data[5]), + ssh2_pkt_type(ssh->pkt_kctx, ssh->pkt_actx, pkt->data[5]), pkt->body, pkt->length - (pkt->body - pkt->data), pkt->nblanks, pkt->blanks); sfree(pkt->blanks); pkt->blanks = NULL; @@ -5181,7 +5188,7 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen, s->maclist = macs, s->nmacs = lenof(macs); begin_key_exchange: - ssh->pkt_ctx &= ~SSH2_PKTCTX_KEX_MASK; + ssh->pkt_kctx = SSH2_PKTCTX_NOKEX; { int i, j, commalist_started; @@ -5630,7 +5637,7 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen, */ if (!ssh->kex->pdata) { logevent("Doing Diffie-Hellman group exchange"); - ssh->pkt_ctx |= SSH2_PKTCTX_DHGEX; + ssh->pkt_kctx = SSH2_PKTCTX_DHGEX; /* * Work out how big a DH group we will need to allow that * much data. @@ -5655,7 +5662,7 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen, s->kex_init_value = SSH2_MSG_KEX_DH_GEX_INIT; s->kex_reply_value = SSH2_MSG_KEX_DH_GEX_REPLY; } else { - ssh->pkt_ctx |= SSH2_PKTCTX_DHGROUP; + ssh->pkt_kctx = SSH2_PKTCTX_DHGROUP; ssh->kex_ctx = dh_setup_group(ssh->kex); s->kex_init_value = SSH2_MSG_KEXDH_INIT; s->kex_reply_value = SSH2_MSG_KEXDH_REPLY; @@ -5714,7 +5721,7 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen, } else { logeventf(ssh, "Doing RSA key exchange with hash %s", ssh->kex->hash->text_name); - ssh->pkt_ctx |= SSH2_PKTCTX_RSAKEX; + ssh->pkt_kctx = SSH2_PKTCTX_RSAKEX; /* * RSA key exchange. First expect a KEXRSA_PUBKEY packet * from the server. @@ -7070,7 +7077,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, * just in case it succeeds, and (b) so that we know what * authentication methods we can usefully try next. */ - ssh->pkt_ctx &= ~SSH2_PKTCTX_AUTH_MASK; + ssh->pkt_actx = SSH2_PKTCTX_NOAUTH; s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST); ssh2_pkt_addstring(s->pktout, s->username); @@ -7204,7 +7211,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, in_commasep_string("keyboard-interactive", methods, methlen); } - ssh->pkt_ctx &= ~SSH2_PKTCTX_AUTH_MASK; + ssh->pkt_actx = SSH2_PKTCTX_NOAUTH; if (s->can_pubkey && !s->done_agent && s->nkeys) { @@ -7212,8 +7219,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, * Attempt public-key authentication using a key from Pageant. */ - ssh->pkt_ctx &= ~SSH2_PKTCTX_AUTH_MASK; - ssh->pkt_ctx |= SSH2_PKTCTX_PUBLICKEY; + ssh->pkt_actx = SSH2_PKTCTX_PUBLICKEY; logeventf(ssh, "Trying Pageant key #%d", s->keyi); @@ -7360,8 +7366,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, struct ssh2_userkey *key; /* not live over crReturn */ char *passphrase; /* not live over crReturn */ - ssh->pkt_ctx &= ~SSH2_PKTCTX_AUTH_MASK; - ssh->pkt_ctx |= SSH2_PKTCTX_PUBLICKEY; + ssh->pkt_actx = SSH2_PKTCTX_PUBLICKEY; s->tried_pubkey_config = TRUE; @@ -7540,8 +7545,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, s->type = AUTH_TYPE_KEYBOARD_INTERACTIVE; - ssh->pkt_ctx &= ~SSH2_PKTCTX_AUTH_MASK; - ssh->pkt_ctx |= SSH2_PKTCTX_KBDINTER; + ssh->pkt_actx = SSH2_PKTCTX_KBDINTER; s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST); ssh2_pkt_addstring(s->pktout, s->username); @@ -7683,8 +7687,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, int ret; /* not live over crReturn */ int changereq_first_time; /* not live over crReturn */ - ssh->pkt_ctx &= ~SSH2_PKTCTX_AUTH_MASK; - ssh->pkt_ctx |= SSH2_PKTCTX_PASSWORD; + ssh->pkt_actx = SSH2_PKTCTX_PASSWORD; s->cur_prompt = new_prompts(ssh->frontend); s->cur_prompt->to_server = TRUE; @@ -8555,7 +8558,8 @@ static const char *ssh_init(void *frontend_handle, void **backend_handle, ssh->deferred_len = 0; ssh->deferred_size = 0; ssh->fallback_cmd = 0; - ssh->pkt_ctx = 0; + ssh->pkt_kctx = SSH2_PKTCTX_NOKEX; + ssh->pkt_actx = SSH2_PKTCTX_NOAUTH; ssh->x11auth = NULL; ssh->v1_compressing = FALSE; ssh->v2_outgoing_sequence = 0; From ac6b5c8bd5e0e422d73b9994debadd7c9a7160dc Mon Sep 17 00:00:00 2001 From: Jacob Nevins Date: Sun, 22 Jul 2007 14:34:27 +0000 Subject: [PATCH 074/124] Note lack of proxy auto-detection. [originally from svn r7648] --- doc/config.but | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/doc/config.but b/doc/config.but index 5f74a2cf..c573bc0a 100644 --- a/doc/config.but +++ b/doc/config.but @@ -1801,6 +1801,11 @@ this panel affect the primary network connection forming your PuTTY session, and also any extra connections made as a result of SSH \i{port forwarding} (see \k{using-port-forwarding}). +Note that unlike some software (such as web browsers), PuTTY does not +attempt to automatically determine whether to use a proxy and (if so) +which one to use for a given destination. If you need to use a proxy, +it must always be explicitly configured. + \S{config-proxy-type} Setting the proxy type \cfg{winhelp-topic}{proxy.type} From 6d2c196708355f65d1764adc4fa1c04b0422356c Mon Sep 17 00:00:00 2001 From: Ben Harris Date: Sun, 29 Jul 2007 14:02:00 +0000 Subject: [PATCH 075/124] Don't throw away data that we receive before we're ready for it. Just save it up for later. This should prevent hangs when talking to particularly enthusiastic servers. Thanks to JCA for tracking this bug down. [originally from svn r7651] --- pscp.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/pscp.c b/pscp.c index 8e082e73..e208157b 100644 --- a/pscp.c +++ b/pscp.c @@ -180,12 +180,6 @@ int from_backend(void *frontend, int is_stderr, const char *data, int datalen) return 0; } - /* - * If this is before the real session begins, just return. - */ - if (!outptr) - return 0; - if ((outlen > 0) && (len > 0)) { unsigned used = outlen; if (used > len) From f48e3eb16b8abb9cf2f0f51e8fdbf0207f7ca35d Mon Sep 17 00:00:00 2001 From: Ben Harris Date: Sat, 4 Aug 2007 14:32:06 +0000 Subject: [PATCH 076/124] Tweak to window handling: Keep the local window in a signed integer, and arrange to handle usefully the case where the server sends us more data than it's allowed to. There's no danger of overflow, since the maximum is OUR_V2_WINSIZE and the minimum is -OUR_V2_MAXPKT (at least if the server is nice). [originally from svn r7661] --- ssh.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/ssh.c b/ssh.c index 3476d3d8..4f69201b 100644 --- a/ssh.c +++ b/ssh.c @@ -551,7 +551,8 @@ struct ssh_channel { struct ssh2_data_channel { bufchain outbuffer; unsigned remwindow, remmaxpkt; - unsigned locwindow; + /* locwindow is signed so we can cope with excess data. */ + int locwindow; } v2; } v; union { @@ -6284,9 +6285,12 @@ static void ssh2_msg_channel_data(Ssh ssh, struct Packet *pktin) /* * If we are not buffering too much data, * enlarge the window again at the remote side. + * If we are buffering too much, we may still + * need to adjust the window if the server's + * sent excess data. */ - if (bufsize < OUR_V2_WINSIZE) - ssh2_set_window(c, OUR_V2_WINSIZE - bufsize); + ssh2_set_window(c, bufsize < OUR_V2_WINSIZE ? + OUR_V2_WINSIZE - bufsize : 0); } } From 7cda30ac1a7f9e2a6770f6c76824b806e4b6c5bd Mon Sep 17 00:00:00 2001 From: Ben Harris Date: Sat, 4 Aug 2007 15:48:52 +0000 Subject: [PATCH 077/124] When omitting session data from logs, don't omit the length of the session data string. This isn't strictly necessary, but it makes the logs easier to use. [originally from svn r7666] --- ssh.c | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/ssh.c b/ssh.c index 4f69201b..0a823556 100644 --- a/ssh.c +++ b/ssh.c @@ -1222,9 +1222,9 @@ static struct Packet *ssh1_rdpkt(Ssh ssh, unsigned char **data, int *datalen) /* "Session data" packets - omit the data field */ if ((st->pktin->type == SSH1_SMSG_STDOUT_DATA) || (st->pktin->type == SSH1_SMSG_STDERR_DATA)) { - do_blank = TRUE; blank_prefix = 0; - } else if (st->pktin->type == SSH1_MSG_CHANNEL_DATA) { do_blank = TRUE; blank_prefix = 4; + } else if (st->pktin->type == SSH1_MSG_CHANNEL_DATA) { + do_blank = TRUE; blank_prefix = 8; } if (do_blank) { blank.offset = blank_prefix; @@ -1382,9 +1382,9 @@ static struct Packet *ssh2_rdpkt(Ssh ssh, unsigned char **data, int *datalen) int do_blank = FALSE, blank_prefix = 0; /* "Session data" packets - omit the data field */ if (st->pktin->type == SSH2_MSG_CHANNEL_DATA) { - do_blank = TRUE; blank_prefix = 4; - } else if (st->pktin->type == SSH2_MSG_CHANNEL_EXTENDED_DATA) { do_blank = TRUE; blank_prefix = 8; + } else if (st->pktin->type == SSH2_MSG_CHANNEL_EXTENDED_DATA) { + do_blank = TRUE; blank_prefix = 12; } if (do_blank) { blank.offset = blank_prefix; @@ -2940,8 +2940,8 @@ static void ssh_agentf_callback(void *cv, void *reply, int replylen) } else { send_packet(ssh, SSH1_MSG_CHANNEL_DATA, PKT_INT, c->remoteid, - PKTT_DATA, PKT_INT, replylen, + PKTT_DATA, PKT_DATA, sentreply, replylen, PKTT_OTHER, PKT_END); @@ -4001,8 +4001,7 @@ int sshfwd_write(struct ssh_channel *c, char *buf, int len) if (ssh->version == 1) { send_packet(ssh, SSH1_MSG_CHANNEL_DATA, PKT_INT, c->remoteid, - PKTT_DATA, - PKT_INT, len, PKT_DATA, buf, len, + PKT_INT, len, PKTT_DATA, PKT_DATA, buf, len, PKTT_OTHER, PKT_END); /* * In SSH-1 we can return 0 here - implying that forwarded @@ -4973,8 +4972,8 @@ static void do_ssh1_connection(Ssh ssh, unsigned char *in, int inlen, } else { while (inlen > 0) { int len = min(inlen, 512); - send_packet(ssh, SSH1_CMSG_STDIN_DATA, PKTT_DATA, - PKT_INT, len, PKT_DATA, in, len, + send_packet(ssh, SSH1_CMSG_STDIN_DATA, + PKT_INT, len, PKTT_DATA, PKT_DATA, in, len, PKTT_OTHER, PKT_END); in += len; inlen -= len; @@ -6124,8 +6123,8 @@ static int ssh2_try_send(struct ssh_channel *c) len = c->v.v2.remmaxpkt; pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_DATA); ssh2_pkt_adduint32(pktout, c->remoteid); - dont_log_data(ssh, pktout, PKTLOG_OMIT); ssh2_pkt_addstring_start(pktout); + dont_log_data(ssh, pktout, PKTLOG_OMIT); ssh2_pkt_addstring_data(pktout, data, len); end_log_omission(ssh, pktout); ssh2_pkt_send(ssh, pktout); From 3dc0f8507d6752702e118822d5b34a5d922b2362 Mon Sep 17 00:00:00 2001 From: Ben Harris Date: Sat, 4 Aug 2007 16:04:08 +0000 Subject: [PATCH 078/124] Tweak window handling so that we send a window adjust if the window is half used up, rather than over half. That this increases the throughput of PSCP by 50% indicates just how broken our window handling is. [originally from svn r7667] --- ssh.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ssh.c b/ssh.c index 0a823556..08741abc 100644 --- a/ssh.c +++ b/ssh.c @@ -6187,7 +6187,7 @@ static void ssh2_set_window(struct ssh_channel *c, unsigned newwin) * * "Significant" is arbitrarily defined as half the window size. */ - if (newwin > c->v.v2.locwindow * 2) { + if (newwin >= c->v.v2.locwindow * 2) { struct Packet *pktout; pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_WINDOW_ADJUST); From 8659f5145ff8e72091943739a681b5031283551b Mon Sep 17 00:00:00 2001 From: Ben Harris Date: Sat, 4 Aug 2007 19:16:46 +0000 Subject: [PATCH 079/124] "CR implies LF" patch, based on one from Paul Coldrey. [originally from svn r7669] --- config.c | 3 +++ doc/config.but | 13 +++++++++++++ putty.h | 1 + settings.c | 2 ++ terminal.c | 7 +++++++ windows/winhelp.h | 1 + 6 files changed, 27 insertions(+) diff --git a/config.c b/config.c index 1fedd284..d5b50dd1 100644 --- a/config.c +++ b/config.c @@ -1310,6 +1310,9 @@ void setup_config_box(struct controlbox *b, int midsession, ctrl_checkbox(s, "Implicit CR in every LF", 'r', HELPCTX(terminal_lfhascr), dlg_stdcheckbox_handler, I(offsetof(Config,lfhascr))); + ctrl_checkbox(s, "Implicit LF in every CR", 'f', + HELPCTX(terminal_crhaslf), + dlg_stdcheckbox_handler, I(offsetof(Config,crhaslf))); ctrl_checkbox(s, "Use background colour to erase screen", 'e', HELPCTX(terminal_bce), dlg_stdcheckbox_handler, I(offsetof(Config,bce))); diff --git a/doc/config.but b/doc/config.but index c573bc0a..ac76be8b 100644 --- a/doc/config.but +++ b/doc/config.but @@ -368,6 +368,19 @@ option, and things might go back to normal: \c Second line \c Third line +\S{config-lfcr} \q{Implicit LF in every CR} + +\cfg{winhelp-topic}{terminal.crhaslf} + +Most servers send two control characters, \i{CR} and \i{LF}, to start a +\i{new line} of the screen. The CR character makes the cursor return to the +left-hand side of the screen. The LF character makes the cursor move +one line down (and might make the screen scroll). + +Some servers only send CR, and so the newly +written line is overwritten by the following line. This option causes +a line feed so that all lines are displayed. + \S{config-erase} \q{Use \i{background colour} to erase screen} \cfg{winhelp-topic}{terminal.bce} diff --git a/putty.h b/putty.h index 30b23bf5..26f4b40e 100644 --- a/putty.h +++ b/putty.h @@ -597,6 +597,7 @@ struct config_tag { FontSpec widefont; FontSpec wideboldfont; int shadowboldoffset; + int crhaslf; }; /* diff --git a/settings.c b/settings.c index 44b02cba..09666f4e 100644 --- a/settings.c +++ b/settings.c @@ -388,6 +388,7 @@ void save_open_settings(void *sesskey, Config *cfg) write_setting_i(sesskey, "DECOriginMode", cfg->dec_om); write_setting_i(sesskey, "AutoWrapMode", cfg->wrap_mode); write_setting_i(sesskey, "LFImpliesCR", cfg->lfhascr); + write_setting_i(sesskey, "CRImpliesLF", cfg->crhaslf); write_setting_i(sesskey, "DisableArabicShaping", cfg->arabicshaping); write_setting_i(sesskey, "DisableBidi", cfg->bidi); write_setting_i(sesskey, "WinNameAlways", cfg->win_name_always); @@ -681,6 +682,7 @@ void load_open_settings(void *sesskey, Config *cfg) gppi(sesskey, "DECOriginMode", 0, &cfg->dec_om); gppi(sesskey, "AutoWrapMode", 1, &cfg->wrap_mode); gppi(sesskey, "LFImpliesCR", 0, &cfg->lfhascr); + gppi(sesskey, "CRImpliesLF", 0, &cfg->crhaslf); gppi(sesskey, "DisableArabicShaping", 0, &cfg->arabicshaping); gppi(sesskey, "DisableBidi", 0, &cfg->bidi); gppi(sesskey, "WinNameAlways", 1, &cfg->win_name_always); diff --git a/terminal.c b/terminal.c index f58941fa..def573c8 100644 --- a/terminal.c +++ b/terminal.c @@ -2850,6 +2850,13 @@ static void term_out(Terminal *term) term->wrapnext = FALSE; seen_disp_event(term); term->paste_hold = 0; + + if (term->cfg.crhaslf) { + if (term->curs.y == term->marg_b) + scroll(term, term->marg_t, term->marg_b, 1, TRUE); + else if (term->curs.y < term->rows - 1) + term->curs.y++; + } if (term->logctx) logtraffic(term->logctx, (unsigned char) c, LGTYP_ASCII); break; diff --git a/windows/winhelp.h b/windows/winhelp.h index 5d63e5e5..ce7881c7 100644 --- a/windows/winhelp.h +++ b/windows/winhelp.h @@ -42,6 +42,7 @@ #define WINHELP_CTX_terminal_autowrap "terminal.autowrap:config-autowrap" #define WINHELP_CTX_terminal_decom "terminal.decom:config-decom" #define WINHELP_CTX_terminal_lfhascr "terminal.lfhascr:config-crlf" +#define WINHELP_CTX_terminal_crhaslf "terminal.crhaslf:config-lfcr" #define WINHELP_CTX_terminal_bce "terminal.bce:config-erase" #define WINHELP_CTX_terminal_blink "terminal.blink:config-blink" #define WINHELP_CTX_terminal_answerback "terminal.answerback:config-answerback" From a3ea90c0e8a113ec8ec28942a650d3aae99f44ff Mon Sep 17 00:00:00 2001 From: Ben Harris Date: Sat, 4 Aug 2007 22:14:19 +0000 Subject: [PATCH 080/124] In the file-transfer applications, which only ever use the main channel, arrange to set the SSH-2 window size to something very large. This prevents the connection stalling when the window fills up, and means that PSCP receives data _much_ faster. [originally from svn r7672] --- pscp.c | 1 + psftp.c | 1 + putty.h | 6 ++++++ ssh.c | 25 ++++++++++++++++--------- 4 files changed, 24 insertions(+), 9 deletions(-) diff --git a/pscp.c b/pscp.c index e208157b..1809c6f7 100644 --- a/pscp.c +++ b/pscp.c @@ -418,6 +418,7 @@ static void do_cmd(char *host, char *user, char *cmd) cfg.x11_forward = 0; cfg.agentfwd = 0; cfg.portfwd[0] = cfg.portfwd[1] = '\0'; + cfg.ssh_simple = TRUE; /* * Set up main and possibly fallback command depending on diff --git a/psftp.c b/psftp.c index e41112aa..990bc074 100644 --- a/psftp.c +++ b/psftp.c @@ -2752,6 +2752,7 @@ static int psftp_connect(char *userhost, char *user, int portnumber) cfg.x11_forward = 0; cfg.agentfwd = 0; cfg.portfwd[0] = cfg.portfwd[1] = '\0'; + cfg.ssh_simple = TRUE; /* Set up subsystem name. */ strcpy(cfg.remote_cmd, "sftp"); diff --git a/putty.h b/putty.h index 26f4b40e..c935efa0 100644 --- a/putty.h +++ b/putty.h @@ -588,6 +588,12 @@ struct config_tag { int sshbug_ignore1, sshbug_plainpw1, sshbug_rsa1, sshbug_hmac2, sshbug_derivekey2, sshbug_rsapad2, sshbug_pksessid2, sshbug_rekey2; + /* + * ssh_simple means that we promise never to open any channel other + * than the main one, which means it can safely use a very large + * window in SSH-2. + */ + int ssh_simple; /* Options for pterm. Should split out into platform-dependent part. */ int stamp_utmp; int login_shell; diff --git a/ssh.c b/ssh.c index 08741abc..51b53201 100644 --- a/ssh.c +++ b/ssh.c @@ -470,11 +470,15 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, * * - OUR_V2_WINSIZE is the maximum window size we present on SSH-2 * channels. + * + * - OUR_V2_BIGWIN is the window size we advertise for the only + * channel in a simple connection. */ #define SSH1_BUFFER_LIMIT 32768 #define SSH_MAX_BACKLOG 32768 #define OUR_V2_WINSIZE 16384 +#define OUR_V2_BIGWIN 0x10000000 #define OUR_V2_MAXPKT 0x4000UL /* Maximum length of passwords/passphrases (arbitrary) */ @@ -552,7 +556,7 @@ struct ssh_channel { bufchain outbuffer; unsigned remwindow, remmaxpkt; /* locwindow is signed so we can cope with excess data. */ - int locwindow; + int locwindow, locmaxwin; } v2; } v; union { @@ -4030,7 +4034,7 @@ void sshfwd_unthrottle(struct ssh_channel *c, int bufsize) ssh1_throttle(ssh, -1); } } else { - ssh2_set_window(c, OUR_V2_WINSIZE - bufsize); + ssh2_set_window(c, c->v.v2.locmaxwin - bufsize); } } @@ -6288,8 +6292,8 @@ static void ssh2_msg_channel_data(Ssh ssh, struct Packet *pktin) * need to adjust the window if the server's * sent excess data. */ - ssh2_set_window(c, bufsize < OUR_V2_WINSIZE ? - OUR_V2_WINSIZE - bufsize : 0); + ssh2_set_window(c, bufsize < c->v.v2.locmaxwin ? + c->v.v2.locmaxwin - bufsize : 0); } } @@ -6756,7 +6760,7 @@ static void ssh2_msg_channel_open(Ssh ssh, struct Packet *pktin) } else { c->localid = alloc_channel_id(ssh); c->closes = 0; - c->v.v2.locwindow = OUR_V2_WINSIZE; + c->v.v2.locwindow = c->v.v2.locmaxwin = OUR_V2_WINSIZE; c->v.v2.remwindow = winsize; c->v.v2.remmaxpkt = pktsize; bufchain_init(&c->v.v2.outbuffer); @@ -7967,7 +7971,8 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, s->pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_OPEN); ssh2_pkt_addstring(s->pktout, "direct-tcpip"); ssh2_pkt_adduint32(s->pktout, ssh->mainchan->localid); - ssh->mainchan->v.v2.locwindow = OUR_V2_WINSIZE; + ssh->mainchan->v.v2.locwindow = ssh->mainchan->v.v2.locmaxwin = + ssh->cfg.ssh_simple ? OUR_V2_BIGWIN : OUR_V2_WINSIZE; ssh2_pkt_adduint32(s->pktout, ssh->mainchan->v.v2.locwindow);/* our window size */ ssh2_pkt_adduint32(s->pktout, OUR_V2_MAXPKT); /* our max pkt size */ ssh2_pkt_addstring(s->pktout, ssh->cfg.ssh_nc_host); @@ -8009,7 +8014,8 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, s->pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_OPEN); ssh2_pkt_addstring(s->pktout, "session"); ssh2_pkt_adduint32(s->pktout, ssh->mainchan->localid); - ssh->mainchan->v.v2.locwindow = OUR_V2_WINSIZE; + ssh->mainchan->v.v2.locwindow = ssh->mainchan->v.v2.locmaxwin = + ssh->cfg.ssh_simple ? OUR_V2_BIGWIN : OUR_V2_WINSIZE; ssh2_pkt_adduint32(s->pktout, ssh->mainchan->v.v2.locwindow);/* our window size */ ssh2_pkt_adduint32(s->pktout, OUR_V2_MAXPKT); /* our max pkt size */ ssh2_pkt_send(ssh, s->pktout); @@ -9062,7 +9068,8 @@ static void ssh_unthrottle(void *handle, int bufsize) ssh1_throttle(ssh, -1); } } else { - ssh2_set_window(ssh->mainchan, OUR_V2_WINSIZE - bufsize); + ssh2_set_window(ssh->mainchan, + ssh->mainchan->v.v2.locmaxwin - bufsize); } } @@ -9085,7 +9092,7 @@ void ssh_send_port_open(void *channel, char *hostname, int port, char *org) pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_OPEN); ssh2_pkt_addstring(pktout, "direct-tcpip"); ssh2_pkt_adduint32(pktout, c->localid); - c->v.v2.locwindow = OUR_V2_WINSIZE; + c->v.v2.locwindow = c->v.v2.locmaxwin = OUR_V2_WINSIZE; ssh2_pkt_adduint32(pktout, c->v.v2.locwindow);/* our window size */ ssh2_pkt_adduint32(pktout, OUR_V2_MAXPKT); /* our max pkt size */ ssh2_pkt_addstring(pktout, hostname); From 486771ec4a2e40278fb210cc36e133e976ef4609 Mon Sep 17 00:00:00 2001 From: Ben Harris Date: Sat, 4 Aug 2007 22:19:12 +0000 Subject: [PATCH 081/124] Remember to clear ssh_simple when initialising config. [originally from svn r7674] --- settings.c | 1 + 1 file changed, 1 insertion(+) diff --git a/settings.c b/settings.c index 09666f4e..3846715e 100644 --- a/settings.c +++ b/settings.c @@ -788,6 +788,7 @@ void load_open_settings(void *sesskey, Config *cfg) gppi(sesskey, "BugRSAPad2", 0, &i); cfg->sshbug_rsapad2 = 2-i; gppi(sesskey, "BugPKSessID2", 0, &i); cfg->sshbug_pksessid2 = 2-i; gppi(sesskey, "BugRekey2", 0, &i); cfg->sshbug_rekey2 = 2-i; + cfg->ssh_simple = FALSE; gppi(sesskey, "StampUtmp", 1, &cfg->stamp_utmp); gppi(sesskey, "LoginShell", 1, &cfg->login_shell); gppi(sesskey, "ScrollbarOnLeft", 0, &cfg->scrollbar_on_left); From 16cbd4f26095627796e2774e0ca43648f9c4c692 Mon Sep 17 00:00:00 2001 From: Ben Harris Date: Sun, 5 Aug 2007 14:18:43 +0000 Subject: [PATCH 082/124] Small window-handling tweaks. Set the default big window to 0x7fffffff bytes, and tweak ssh2_set_window() so it can cope with that. Also arrange to send a private channel message in simple mode to tell the server that it can safely use a large window too. [originally from svn r7679] --- ssh.c | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/ssh.c b/ssh.c index 51b53201..1127c5c6 100644 --- a/ssh.c +++ b/ssh.c @@ -472,13 +472,13 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, * channels. * * - OUR_V2_BIGWIN is the window size we advertise for the only - * channel in a simple connection. + * channel in a simple connection. It must be <= INT_MAX. */ #define SSH1_BUFFER_LIMIT 32768 #define SSH_MAX_BACKLOG 32768 #define OUR_V2_WINSIZE 16384 -#define OUR_V2_BIGWIN 0x10000000 +#define OUR_V2_BIGWIN 0x7fffffff #define OUR_V2_MAXPKT 0x4000UL /* Maximum length of passwords/passphrases (arbitrary) */ @@ -6191,7 +6191,7 @@ static void ssh2_set_window(struct ssh_channel *c, unsigned newwin) * * "Significant" is arbitrarily defined as half the window size. */ - if (newwin >= c->v.v2.locwindow * 2) { + if (newwin / 2 >= c->v.v2.locwindow) { struct Packet *pktout; pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_WINDOW_ADJUST); @@ -8060,6 +8060,20 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, ssh->packet_dispatch[SSH2_MSG_CHANNEL_OPEN] = ssh2_msg_channel_open; + if (ssh->cfg.ssh_simple) { + /* + * This message indicates to the server that we promise + * not to try to run any other channel in parallel with + * this one, so it's safe for it to advertise a very large + * window and leave the flow control to TCP. + */ + s->pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_REQUEST); + ssh2_pkt_adduint32(s->pktout, ssh->mainchan->remoteid); + ssh2_pkt_addstring(s->pktout, "simple@putty.projects.tartarus.org"); + ssh2_pkt_addbool(s->pktout, 0); /* no reply */ + ssh2_pkt_send(ssh, s->pktout); + } + /* * Potentially enable X11 forwarding. */ From dd10ff58648cb323d4449077b7b5e8c44fb652dd Mon Sep 17 00:00:00 2001 From: Ben Harris Date: Sun, 5 Aug 2007 22:18:59 +0000 Subject: [PATCH 083/124] May as well reference an RFC rather than an Internet Draft where we can. [originally from svn r7682] --- ssh.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ssh.c b/ssh.c index 1127c5c6..7a697a16 100644 --- a/ssh.c +++ b/ssh.c @@ -183,7 +183,7 @@ static const char *const ssh2_disconnect_reasons[] = { /* * Codes for terminal modes. * Most of these are the same in SSH-1 and SSH-2. - * This list is derived from draft-ietf-secsh-connect-25 and + * This list is derived from RFC 4254 and * SSH-1 RFC-1.2.31. */ static const struct { From ac041a3d66c8105645267af4f017c55a8a4ef48f Mon Sep 17 00:00:00 2001 From: Ben Harris Date: Mon, 6 Aug 2007 20:56:52 +0000 Subject: [PATCH 084/124] Use "int" rather than "unsigned" as the argument to ssh2_set_window, not because it can ever be negative, but because we'll be comparing it with another int. This way, C's promotion rules don't bite us and we should stand slightly more chance of coping with broken servers that overrun our window. [originally from svn r7683] --- ssh.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ssh.c b/ssh.c index 7a697a16..f78ffbef 100644 --- a/ssh.c +++ b/ssh.c @@ -663,7 +663,7 @@ static void ssh_special(void *handle, Telnet_Special); static int ssh2_try_send(struct ssh_channel *c); static void ssh2_add_channel_data(struct ssh_channel *c, char *buf, int len); 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, int newwin); static int ssh_sendbuffer(void *handle); static int ssh_do_close(Ssh ssh, int notify_exit); static unsigned long ssh_pkt_getuint32(struct Packet *pkt); @@ -6172,7 +6172,7 @@ static void ssh2_try_send_and_unthrottle(struct ssh_channel *c) /* * Potentially enlarge the window on an SSH-2 channel. */ -static void ssh2_set_window(struct ssh_channel *c, unsigned newwin) +static void ssh2_set_window(struct ssh_channel *c, int newwin) { Ssh ssh = c->ssh; From be8818b4bcf3a802850c99ee68731a9a8663c94b Mon Sep 17 00:00:00 2001 From: Ben Harris Date: Tue, 7 Aug 2007 22:02:03 +0000 Subject: [PATCH 085/124] Correctly terminate nc target hostname when copying it. While we're here, use memcpy rather than strncpy when we've already worked out how much we're going to copy. [originally from svn r7685] --- cmdline.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmdline.c b/cmdline.c index 5b294578..183797df 100644 --- a/cmdline.c +++ b/cmdline.c @@ -263,8 +263,8 @@ int cmdline_process_param(char *p, char *value, int need_save, Config *cfg) unsigned len = portp - host; if (len >= sizeof(cfg->ssh_nc_host)) len = sizeof(cfg->ssh_nc_host) - 1; - strncpy(cfg->ssh_nc_host, value, len); - cfg->ssh_nc_host[sizeof(cfg->ssh_nc_host) - 1] = '\0'; + memcpy(cfg->ssh_nc_host, value, len); + cfg->ssh_nc_host[len] = '\0'; cfg->ssh_nc_port = atoi(portp+1); } else { cmdline_error("-nc expects argument of form 'host:port'"); From 4b178be3e9608120fe68a62999b8c678ef860e97 Mon Sep 17 00:00:00 2001 From: Simon Tatham Date: Mon, 3 Sep 2007 19:09:56 +0000 Subject: [PATCH 086/124] ssh->mainchan can be NULL; try not to segfault in that situation. [originally from svn r7705] --- ssh.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/ssh.c b/ssh.c index f78ffbef..a2aeb541 100644 --- a/ssh.c +++ b/ssh.c @@ -9082,8 +9082,9 @@ static void ssh_unthrottle(void *handle, int bufsize) ssh1_throttle(ssh, -1); } } else { - ssh2_set_window(ssh->mainchan, - ssh->mainchan->v.v2.locmaxwin - bufsize); + if (ssh->mainchan) + ssh2_set_window(ssh->mainchan, + ssh->mainchan->v.v2.locmaxwin - bufsize); } } From 54321544d465196d52f9a3c3b4f6aae15ff61127 Mon Sep 17 00:00:00 2001 From: Ben Harris Date: Mon, 3 Sep 2007 20:33:40 +0000 Subject: [PATCH 087/124] Suggest another ttymode we could usefully set automatically. [originally from svn r7709] --- terminal.c | 1 + 1 file changed, 1 insertion(+) diff --git a/terminal.c b/terminal.c index def573c8..65efb8eb 100644 --- a/terminal.c +++ b/terminal.c @@ -6405,6 +6405,7 @@ char *term_get_ttymode(Terminal *term, const char *mode) val = term->cfg.bksp_is_delete ? "^?" : "^H"; } /* FIXME: perhaps we should set ONLCR based on cfg.lfhascr as well? */ + /* FIXME: or ECHO and friends based on local echo state? */ return dupstr(val); } From 2323cb947e2923f2d15137b17697b36985f849c9 Mon Sep 17 00:00:00 2001 From: Ben Harris Date: Mon, 3 Sep 2007 20:52:56 +0000 Subject: [PATCH 088/124] Avoid "unused variable" warning when NO_IPV6 defined. [originally from svn r7710] --- config.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/config.c b/config.c index d5b50dd1..fd1379ab 100644 --- a/config.c +++ b/config.c @@ -1049,7 +1049,9 @@ static void portfwd_handler(union control *ctrl, void *dlg, { static const char *const afs = "A46"; char *afp = strchr(afs, *p); +#ifndef NO_IPV6 int idx = afp ? afp-afs : 0; +#endif if (afp) p++; #ifndef NO_IPV6 From 1e8a5e47960cdb77a1627493d519a1cdf8c5fb3e Mon Sep 17 00:00:00 2001 From: Ben Harris Date: Thu, 20 Sep 2007 21:07:24 +0000 Subject: [PATCH 089/124] In SSH-1, don't attempt password authentication unless the server has announced support for it. Instead exit with a fatal error (since password auth is our last resort). [originally from svn r7724] --- ssh.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/ssh.c b/ssh.c index a2aeb541..a1f43edc 100644 --- a/ssh.c +++ b/ssh.c @@ -62,6 +62,10 @@ #define SSH1_SMSG_AUTH_CCARD_CHALLENGE 71 /* 0x47 */ #define SSH1_CMSG_AUTH_CCARD_RESPONSE 72 /* 0x48 */ +#define SSH1_AUTH_RHOSTS 1 /* 0x1 */ +#define SSH1_AUTH_RSA 2 /* 0x2 */ +#define SSH1_AUTH_PASSWORD 3 /* 0x3 */ +#define SSH1_AUTH_RHOSTS_RSA 4 /* 0x4 */ #define SSH1_AUTH_TIS 5 /* 0x5 */ #define SSH1_AUTH_CCARD 16 /* 0x10 */ @@ -3776,6 +3780,10 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen, } } if (s->pwpkt_type == SSH1_CMSG_AUTH_PASSWORD) { + if ((s->supported_auths_mask & (1 << SSH1_AUTH_PASSWORD)) == 0) { + bombout(("No supported authentication methods available")); + crStop(0); + } s->cur_prompt->to_server = TRUE; s->cur_prompt->name = dupstr("SSH password"); add_prompt(s->cur_prompt, dupprintf("%.90s@%.90s's password: ", From 1854dcd3881a23d85f8e39eaff53821823f2b942 Mon Sep 17 00:00:00 2001 From: Ben Harris Date: Thu, 20 Sep 2007 21:33:21 +0000 Subject: [PATCH 090/124] Don't try SSH-1 RSA authentication unless the server has advertised support for it. It's possible that this obsoletes BUG_CHOKES_ON_RSA. Certainly the one SSH-1.5-Cisco-1.25 server I found was correctly not advertising RSA auth. For now, leave it in, because I'm not feeling entirely confident. [originally from svn r7726] --- ssh.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ssh.c b/ssh.c index a1f43edc..38f14958 100644 --- a/ssh.c +++ b/ssh.c @@ -3070,6 +3070,8 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen, ssh->v1_remote_protoflags = ssh_pkt_getuint32(pktin); s->supported_ciphers_mask = ssh_pkt_getuint32(pktin); s->supported_auths_mask = ssh_pkt_getuint32(pktin); + if ((ssh->remote_bugs & BUG_CHOKES_ON_RSA)) + s->supported_auths_mask &= ~(1 << SSH1_AUTH_RSA); ssh->v1_local_protoflags = ssh->v1_remote_protoflags & SSH1_PROTOFLAGS_SUPPORTED; @@ -3323,7 +3325,7 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen, crWaitUntil(pktin); - if ((ssh->remote_bugs & BUG_CHOKES_ON_RSA)) { + if ((s->supported_auths_mask & (1 << SSH1_AUTH_RSA)) == 0) { /* We must not attempt PK auth. Pretend we've already tried it. */ s->tried_publickey = s->tried_agent = 1; } else { From 9acc508c4ee32577c2626ec9088d551472e9456c Mon Sep 17 00:00:00 2001 From: Simon Tatham Date: Fri, 21 Sep 2007 18:04:08 +0000 Subject: [PATCH 091/124] Harvey Kwok observes that EnumPrinters() can sometimes fail to fill in its output parameters. Hence, we initialise them before calling it. [originally from svn r7729] --- windows/winprint.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/windows/winprint.c b/windows/winprint.c index b8696520..1548c3c9 100644 --- a/windows/winprint.c +++ b/windows/winprint.c @@ -21,7 +21,7 @@ struct printer_job_tag { static char *printer_add_enum(int param, DWORD level, char *buffer, int offset, int *nprinters_ptr) { - DWORD needed, nprinters; + DWORD needed = 0, nprinters = 0; buffer = sresize(buffer, offset+512, char); From 187d481d735e13f2a45e3ff8d7e3c49df3257116 Mon Sep 17 00:00:00 2001 From: Ben Harris Date: Sat, 22 Sep 2007 13:55:25 +0000 Subject: [PATCH 092/124] Document "No supported authentication methods available", and make it clear that TIS/CryptoCard auth can be used for simple passwords too. [originally from svn r7730] --- doc/config.but | 5 +++-- doc/errors.but | 7 +++++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/doc/config.but b/doc/config.but index ac76be8b..98b4a7e2 100644 --- a/doc/config.but +++ b/doc/config.but @@ -2419,11 +2419,12 @@ forms of simple \I{challenge/response authentication}challenge/response authentication available in SSH protocol version 1 only. You might use them if you were using \i{S/Key} \i{one-time passwords}, for example, or if you had a physical \i{security token} that generated responses -to authentication challenges. +to authentication challenges. They can even be used to prompt for +simple passwords. With this switch enabled, PuTTY will attempt these forms of authentication if the server is willing to try them. You will be -presented with a challenge string (which will be different every +presented with a challenge string (which may be different every time) and must supply the correct response in order to log in. If your server supports this, you should talk to your system administrator about precisely what form these challenges and diff --git a/doc/errors.but b/doc/errors.but index cbca36b3..5a0d83dc 100644 --- a/doc/errors.but +++ b/doc/errors.but @@ -200,6 +200,13 @@ the various strategies we use for camouflaging passwords in transit. Upgrade your server, or use the workarounds described in \k{config-ssh-bug-ignore1} and possibly \k{config-ssh-bug-plainpw1}. +\H{errors-no-auth} \q{No supported authentication methods available} + +This error indicates that PuTTY has run out of ways to authenticate +you to an SSH server. This may be because PuTTY has TIS or +keyboard-interactive authentication disabled, in which case +\k{config-ssh-tis} and \k{config-ssh-ki}. + \H{errors-crc} \q{Incorrect \i{CRC} received on packet} or \q{Incorrect \i{MAC} received on packet} From 22f9618deb4f7ad93e1204f669900d84b45f30c8 Mon Sep 17 00:00:00 2001 From: Ben Harris Date: Mon, 24 Sep 2007 15:18:11 +0000 Subject: [PATCH 093/124] Add support for automatically tuning the SSH-2 window size for decent performance. The theory behind this is fairly simple, though the implementation turns out to be a little trickier than it looks. The basic idea is that when the connection isn't being limited by our ability to process data, we want to ensure that the window size _as seen by the server_ never drops to zero. Measuring the server's view of the window size is done by arranging for it to acknowledge every SSH_MSG_CHANNEL_WINDOW_ADJUST, or rather an SSH_MSG_CHANNEL_REQUEST sent just before it. That way we can tell when it its outgoing data stream it received the window adjustment, and thus how small the server's view of the window got. At present, we only ever increase the window size. In theory, we could arrange to reduce it again if the server's view of it seemed to be persistently too large, but my experiments suggest that getting this right will be tricky. [originally from svn r7735] --- ssh.c | 125 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 125 insertions(+) diff --git a/ssh.c b/ssh.c index 38f14958..d1a2c80f 100644 --- a/ssh.c +++ b/ssh.c @@ -532,6 +532,14 @@ enum { /* channel types */ CHAN_SOCKDATA_DORMANT /* one the remote hasn't confirmed */ }; +/* + * little structure to keep track of outstanding WINDOW_ADJUSTs + */ +struct winadj { + struct winadj *next; + unsigned size; +}; + /* * 2-3-4 tree storing channels. */ @@ -561,6 +569,18 @@ struct ssh_channel { unsigned remwindow, remmaxpkt; /* locwindow is signed so we can cope with excess data. */ int locwindow, locmaxwin; + /* + * remlocwin is the amount of local window that we think + * the remote end had available to it after it sent the + * last data packet or window adjust ack. + */ + int remlocwin; + /* + * These store the list of window adjusts that haven't + * been acked. + */ + struct winadj *winadj_head, *winadj_tail; + enum { THROTTLED, UNTHROTTLING, UNTHROTTLED } throttle_state; } v2; } v; union { @@ -6203,7 +6223,51 @@ static void ssh2_set_window(struct ssh_channel *c, int newwin) */ if (newwin / 2 >= c->v.v2.locwindow) { struct Packet *pktout; + struct winadj *wa; + /* + * In order to keep track of how much window the client + * actually has available, we'd like it to acknowledge each + * WINDOW_ADJUST. We can't do that directly, so we accompany + * it with a CHANNEL_REQUEST that has to be acknowledged. + * + * This is only necessary if we're opening the window wide. + * If we're not, then throughput is being constrained by + * something other than the maximum window size anyway. + * + * We also only send this if the main channel has finished its + * initial CHANNEL_REQUESTs and installed the default + * CHANNEL_FAILURE handler, so as not to risk giving it + * unexpected CHANNEL_FAILUREs. + */ + if (newwin == c->v.v2.locmaxwin && + ssh->packet_dispatch[SSH2_MSG_CHANNEL_FAILURE]) { + pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_REQUEST); + ssh2_pkt_adduint32(pktout, c->remoteid); + ssh2_pkt_addstring(pktout, "winadj@putty.projects.tartarus.org"); + ssh2_pkt_addbool(pktout, TRUE); + ssh2_pkt_send(ssh, pktout); + + /* + * CHANNEL_FAILURE doesn't come with any indication of + * what message caused it, so we have to keep track of the + * outstanding CHANNEL_REQUESTs ourselves. + */ + wa = snew(struct winadj); + wa->size = newwin - c->v.v2.locwindow; + wa->next = NULL; + if (!c->v.v2.winadj_head) + c->v.v2.winadj_head = wa; + else + c->v.v2.winadj_tail->next = wa; + c->v.v2.winadj_tail = wa; + if (c->v.v2.throttle_state != UNTHROTTLED) + c->v.v2.throttle_state = UNTHROTTLING; + } else { + /* Pretend the WINDOW_ADJUST was acked immediately. */ + c->v.v2.remlocwin = newwin; + c->v.v2.throttle_state = THROTTLED; + } pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_WINDOW_ADJUST); ssh2_pkt_adduint32(pktout, c->remoteid); ssh2_pkt_adduint32(pktout, newwin - c->v.v2.locwindow); @@ -6212,6 +6276,38 @@ static void ssh2_set_window(struct ssh_channel *c, int newwin) } } +static void ssh2_msg_channel_failure(Ssh ssh, struct Packet *pktin) +{ + /* + * The only time this should get called is for "winadj@putty" + * messages sent above. All other channel requests are either + * sent with want_reply false or are sent before this handler gets + * installed. + */ + unsigned i = ssh_pkt_getuint32(pktin); + struct ssh_channel *c; + struct winadj *wa; + + c = find234(ssh->channels, &i, ssh_channelfind); + if (!c) + return; /* nonexistent channel */ + wa = c->v.v2.winadj_head; + if (!wa) + logevent("excess SSH_MSG_CHANNEL_FAILURE"); + else { + c->v.v2.winadj_head = wa->next; + c->v.v2.remlocwin += wa->size; + sfree(wa); + /* + * winadj messages are only sent when the window is fully open, + * so if we get an ack of one, we know any pending unthrottle + * is complete. + */ + if (c->v.v2.throttle_state == UNTHROTTLING) + c->v.v2.throttle_state = UNTHROTTLED; + } +} + static void ssh2_msg_channel_window_adjust(Ssh ssh, struct Packet *pktin) { unsigned i = ssh_pkt_getuint32(pktin); @@ -6239,6 +6335,7 @@ static void ssh2_msg_channel_data(Ssh ssh, struct Packet *pktin) if (data) { int bufsize = 0; c->v.v2.locwindow -= length; + c->v.v2.remlocwin -= length; switch (c->type) { case CHAN_MAINSESSION: bufsize = @@ -6295,6 +6392,14 @@ static void ssh2_msg_channel_data(Ssh ssh, struct Packet *pktin) bufsize = 0; break; } + /* + * If it looks like the remote end hit the end of its window, + * and we didn't want it to do that, think about using a + * larger window. + */ + if (c->v.v2.remlocwin <= 0 && c->v.v2.throttle_state == UNTHROTTLED && + c->v.v2.locmaxwin < 0x40000000) + c->v.v2.locmaxwin += OUR_V2_WINSIZE; /* * If we are not buffering too much data, * enlarge the window again at the remote side. @@ -6773,6 +6878,9 @@ static void ssh2_msg_channel_open(Ssh ssh, struct Packet *pktin) c->v.v2.locwindow = c->v.v2.locmaxwin = OUR_V2_WINSIZE; c->v.v2.remwindow = winsize; c->v.v2.remmaxpkt = pktsize; + c->v.v2.remlocwin = OUR_V2_WINSIZE; + c->v.v2.winadj_head = c->v.v2.winadj_tail = NULL; + c->v.v2.throttle_state = UNTHROTTLED; bufchain_init(&c->v.v2.outbuffer); add234(ssh->channels, c); pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_OPEN_CONFIRMATION); @@ -7982,7 +8090,11 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, ssh2_pkt_addstring(s->pktout, "direct-tcpip"); ssh2_pkt_adduint32(s->pktout, ssh->mainchan->localid); ssh->mainchan->v.v2.locwindow = ssh->mainchan->v.v2.locmaxwin = + ssh->mainchan->v.v2.remlocwin = ssh->cfg.ssh_simple ? OUR_V2_BIGWIN : OUR_V2_WINSIZE; + ssh->mainchan->v.v2.winadj_head = NULL; + ssh->mainchan->v.v2.winadj_tail = NULL; + ssh->mainchan->v.v2.throttle_state = UNTHROTTLED; ssh2_pkt_adduint32(s->pktout, ssh->mainchan->v.v2.locwindow);/* our window size */ ssh2_pkt_adduint32(s->pktout, OUR_V2_MAXPKT); /* our max pkt size */ ssh2_pkt_addstring(s->pktout, ssh->cfg.ssh_nc_host); @@ -8025,7 +8137,11 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, ssh2_pkt_addstring(s->pktout, "session"); ssh2_pkt_adduint32(s->pktout, ssh->mainchan->localid); ssh->mainchan->v.v2.locwindow = ssh->mainchan->v.v2.locmaxwin = + ssh->mainchan->v.v2.remlocwin = ssh->cfg.ssh_simple ? OUR_V2_BIGWIN : OUR_V2_WINSIZE; + ssh->mainchan->v.v2.winadj_head = NULL; + ssh->mainchan->v.v2.winadj_tail = NULL; + ssh->mainchan->v.v2.throttle_state = UNTHROTTLED; ssh2_pkt_adduint32(s->pktout, ssh->mainchan->v.v2.locwindow);/* our window size */ ssh2_pkt_adduint32(s->pktout, OUR_V2_MAXPKT); /* our max pkt size */ ssh2_pkt_send(ssh, s->pktout); @@ -8338,6 +8454,12 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, if (ssh->eof_needed) ssh_special(ssh, TS_EOF); + /* + * All the initial channel requests are done, so install the default + * failure handler. + */ + ssh->packet_dispatch[SSH2_MSG_CHANNEL_FAILURE] = ssh2_msg_channel_failure; + /* * Transfer data! */ @@ -9118,6 +9240,9 @@ void ssh_send_port_open(void *channel, char *hostname, int port, char *org) ssh2_pkt_addstring(pktout, "direct-tcpip"); ssh2_pkt_adduint32(pktout, c->localid); c->v.v2.locwindow = c->v.v2.locmaxwin = OUR_V2_WINSIZE; + c->v.v2.remlocwin = OUR_V2_WINSIZE; + c->v.v2.winadj_head = c->v.v2.winadj_head = NULL; + c->v.v2.throttle_state = UNTHROTTLED; ssh2_pkt_adduint32(pktout, c->v.v2.locwindow);/* our window size */ ssh2_pkt_adduint32(pktout, OUR_V2_MAXPKT); /* our max pkt size */ ssh2_pkt_addstring(pktout, hostname); From d0db31a1ca9675ae0db3dd5123236a60f9a3e2cb Mon Sep 17 00:00:00 2001 From: Ben Harris Date: Mon, 24 Sep 2007 19:26:08 +0000 Subject: [PATCH 094/124] stdout and stderr should be made O_NONBLOCK so that we don't end up blocking the entire process because stdout is busy. Arguably, this shouldn't apply to stderr when we're printing our own error messages to it, but I'll leave that fix for another time. [originally from svn r7738] --- unix/uxplink.c | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/unix/uxplink.c b/unix/uxplink.c index 6b56d2af..c1117e43 100644 --- a/unix/uxplink.c +++ b/unix/uxplink.c @@ -385,7 +385,7 @@ void try_output(int is_stderr) ret = write(fd, senddata, sendlen); if (ret > 0) bufchain_consume(chain, ret); - else if (ret < 0) { + else if (ret < 0 && errno != EWOULDBLOCK) { perror(is_stderr ? "stderr: write" : "stdout: write"); exit(1); } @@ -883,6 +883,23 @@ int main(int argc, char **argv) local_tty = (tcgetattr(0, &orig_termios) == 0); atexit(cleanup_termios); ldisc_update(NULL, 1, 1); + + { + int fl; + /* + * Make sure that stdout/err are non-blocking. + */ + if ((fl = fcntl(1, F_GETFL)) == -1 || + fcntl(1, F_SETFL, fl | O_NONBLOCK) == -1) { + perror("stdout"); + exit(1); + } + if ((fl = fcntl(2, F_GETFL)) == -1 || + fcntl(2, F_SETFL, fl | O_NONBLOCK) == -1) { + perror("stderr"); + exit(1); + } + } sending = FALSE; now = GETTICKCOUNT(); From 57c3ac7f14291200621a13a94f694a1fff0123a0 Mon Sep 17 00:00:00 2001 From: Ben Harris Date: Mon, 24 Sep 2007 21:31:45 +0000 Subject: [PATCH 095/124] Manifest constants are good. Introduce plink to STD{IN,OUT,ERR}_FILENO, TRUE, and FALSE. [originally from svn r7741] --- unix/uxplink.c | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/unix/uxplink.c b/unix/uxplink.c index c1117e43..db881997 100644 --- a/unix/uxplink.c +++ b/unix/uxplink.c @@ -80,7 +80,7 @@ void cmdline_error(char *p, ...) exit(1); } -static int local_tty = 0; /* do we have a local tty? */ +static int local_tty = FALSE; /* do we have a local tty? */ static struct termios orig_termios; static Backend *back; @@ -106,7 +106,7 @@ int platform_default_i(const char *name, int def) if (!strcmp(name, "TermWidth") || !strcmp(name, "TermHeight")) { struct winsize size; - if (ioctl(0, TIOCGWINSZ, (void *)&size) >= 0) + if (ioctl(STDIN_FILENO, TIOCGWINSZ, (void *)&size) >= 0) return (!strcmp(name, "TermWidth") ? size.ws_col : size.ws_row); } return def; @@ -180,7 +180,7 @@ void ldisc_update(void *frontend, int echo, int edit) */ mode.c_iflag = (mode.c_iflag | INPCK | PARMRK) & ~IGNPAR; - tcsetattr(0, TCSANOW, &mode); + tcsetattr(STDIN_FILENO, TCSANOW, &mode); } /* Helper function to extract a special character from a termios. */ @@ -366,7 +366,7 @@ char *get_ttymode(void *frontend, const char *mode) void cleanup_termios(void) { if (local_tty) - tcsetattr(0, TCSANOW, &orig_termios); + tcsetattr(STDIN_FILENO, TCSANOW, &orig_termios); } bufchain stdout_data, stderr_data; @@ -374,7 +374,7 @@ bufchain stdout_data, stderr_data; void try_output(int is_stderr) { bufchain *chain = (is_stderr ? &stderr_data : &stdout_data); - int fd = (is_stderr ? 2 : 1); + int fd = (is_stderr ? STDERR_FILENO : STDOUT_FILENO); void *senddata; int sendlen, ret; @@ -398,10 +398,10 @@ int from_backend(void *frontend_handle, int is_stderr, if (is_stderr) { bufchain_add(&stderr_data, data, len); - try_output(1); + try_output(TRUE); } else { bufchain_add(&stdout_data, data, len); - try_output(0); + try_output(FALSE); } osize = bufchain_size(&stdout_data); @@ -880,7 +880,7 @@ int main(int argc, char **argv) * fails, because we know we aren't necessarily running in a * console. */ - local_tty = (tcgetattr(0, &orig_termios) == 0); + local_tty = (tcgetattr(STDIN_FILENO, &orig_termios) == 0); atexit(cleanup_termios); ldisc_update(NULL, 1, 1); @@ -889,13 +889,13 @@ int main(int argc, char **argv) /* * Make sure that stdout/err are non-blocking. */ - if ((fl = fcntl(1, F_GETFL)) == -1 || - fcntl(1, F_SETFL, fl | O_NONBLOCK) == -1) { + if ((fl = fcntl(STDOUT_FILENO, F_GETFL)) == -1 || + fcntl(STDOUT_FILENO, F_SETFL, fl | O_NONBLOCK) == -1) { perror("stdout"); exit(1); } - if ((fl = fcntl(2, F_GETFL)) == -1 || - fcntl(2, F_SETFL, fl | O_NONBLOCK) == -1) { + if ((fl = fcntl(STDERR_FILENO, F_GETFL)) == -1 || + fcntl(STDERR_FILENO, F_SETFL, fl | O_NONBLOCK) == -1) { perror("stderr"); exit(1); } @@ -921,17 +921,17 @@ int main(int argc, char **argv) back->sendok(backhandle) && back->sendbuffer(backhandle) < MAX_STDIN_BACKLOG) { /* If we're OK to send, then try to read from stdin. */ - FD_SET_MAX(0, maxfd, rset); + FD_SET_MAX(STDIN_FILENO, maxfd, rset); } if (bufchain_size(&stdout_data) > 0) { /* If we have data for stdout, try to write to stdout. */ - FD_SET_MAX(1, maxfd, wset); + FD_SET_MAX(STDOUT_FILENO, maxfd, wset); } if (bufchain_size(&stderr_data) > 0) { /* If we have data for stderr, try to write to stderr. */ - FD_SET_MAX(2, maxfd, wset); + FD_SET_MAX(STDERR_FILENO, maxfd, wset); } /* Count the currently active fds. */ @@ -1028,12 +1028,12 @@ int main(int argc, char **argv) back->size(backhandle, size.ws_col, size.ws_row); } - if (FD_ISSET(0, &rset)) { + if (FD_ISSET(STDIN_FILENO, &rset)) { char buf[4096]; int ret; if (connopen && back->connected(backhandle)) { - ret = read(0, buf, sizeof(buf)); + ret = read(STDIN_FILENO, buf, sizeof(buf)); if (ret < 0) { perror("stdin: read"); exit(1); @@ -1049,12 +1049,12 @@ int main(int argc, char **argv) } } - if (FD_ISSET(1, &wset)) { - try_output(0); + if (FD_ISSET(STDOUT_FILENO, &wset)) { + try_output(FALSE); } - if (FD_ISSET(2, &wset)) { - try_output(1); + if (FD_ISSET(STDERR_FILENO, &wset)) { + try_output(TRUE); } if ((!connopen || !back->connected(backhandle)) && From 38ee5fc58dbe2454842760954a31b90c84879e5a Mon Sep 17 00:00:00 2001 From: Ben Harris Date: Mon, 24 Sep 2007 21:43:48 +0000 Subject: [PATCH 096/124] My changes in r7738 (O_NONBLOCK for Unix Plink) were half-arsed, and completely broke interactive logins. The problem, or at least one of the problems, was that in interactive use stdin, stdout, and stderr tend to be the same file, so setting O_NONBLOCK on the latter two also sets it on the former. Thus, we need to cope with all of them being non-blocking. [originally from svn r7742] [r7738 == d0db31a1ca9675ae0db3dd5123236a60f9a3e2cb] --- unix/uxplink.c | 19 +------------------ 1 file changed, 1 insertion(+), 18 deletions(-) diff --git a/unix/uxplink.c b/unix/uxplink.c index db881997..13369b10 100644 --- a/unix/uxplink.c +++ b/unix/uxplink.c @@ -385,7 +385,7 @@ void try_output(int is_stderr) ret = write(fd, senddata, sendlen); if (ret > 0) bufchain_consume(chain, ret); - else if (ret < 0 && errno != EWOULDBLOCK) { + else if (ret < 0) { perror(is_stderr ? "stderr: write" : "stdout: write"); exit(1); } @@ -883,23 +883,6 @@ int main(int argc, char **argv) local_tty = (tcgetattr(STDIN_FILENO, &orig_termios) == 0); atexit(cleanup_termios); ldisc_update(NULL, 1, 1); - - { - int fl; - /* - * Make sure that stdout/err are non-blocking. - */ - if ((fl = fcntl(STDOUT_FILENO, F_GETFL)) == -1 || - fcntl(STDOUT_FILENO, F_SETFL, fl | O_NONBLOCK) == -1) { - perror("stdout"); - exit(1); - } - if ((fl = fcntl(STDERR_FILENO, F_GETFL)) == -1 || - fcntl(STDERR_FILENO, F_SETFL, fl | O_NONBLOCK) == -1) { - perror("stderr"); - exit(1); - } - } sending = FALSE; now = GETTICKCOUNT(); From faa6e26d38317d1495e47b27012746331942bfaa Mon Sep 17 00:00:00 2001 From: Ben Harris Date: Sat, 29 Sep 2007 12:27:45 +0000 Subject: [PATCH 097/124] Add support for resetting the terminal modes on stderr to something sensible before printing error messages to it. This should fix the stair-stepping in Plink's progress messages. [originally from svn r7745] --- unix/unix.h | 18 ++++++++++++++++++ unix/uxcons.c | 42 ++++++++++++++++++++++++++++++++++++++++++ unix/uxplink.c | 19 +++++++++++++++++-- 3 files changed, 77 insertions(+), 2 deletions(-) diff --git a/unix/unix.h b/unix/unix.h index 90faf5a6..44749b8d 100644 --- a/unix/unix.h +++ b/unix/unix.h @@ -60,6 +60,18 @@ extern long tickcount_offset; #define WCHAR wchar_t #define BYTE unsigned char +/* + * Unix-specific global flag + * + * FLAG_STDERR_TTY indicates that standard error might be a terminal and + * might get its configuration munged, so anything trying to output plain + * text (i.e. with newlines in it) will need to put it back into cooked + * mode first. Applications setting this flag should also call + * stderr_tty_init() before messing with any terminal modes, and can call + * premsg() before outputting text to stderr and postmsg() afterwards. + */ +#define FLAG_STDERR_TTY 0x1000 + /* Things pty.c needs from pterm.c */ char *get_x_display(void *frontend); int font_dimension(void *frontend, int which);/* 0 for width, 1 for height */ @@ -91,6 +103,12 @@ char *x_get_default(const char *key); /* Things uxstore.c provides to pterm.c */ void provide_xrm_string(char *string); +/* Things provided by uxcons.c */ +struct termios; +void stderr_tty_init(void); +void premsg(struct termios *); +void postmsg(struct termios *); + /* The interface used by uxsel.c */ void uxsel_init(void); typedef int (*uxsel_callback_fn)(int fd, int event); diff --git a/unix/uxcons.c b/unix/uxcons.c index d4848a6a..16e69fbb 100644 --- a/unix/uxcons.c +++ b/unix/uxcons.c @@ -18,6 +18,31 @@ int console_batch_mode = FALSE; static void *console_logctx = NULL; +static struct termios orig_termios_stderr; +static int stderr_is_a_tty; + +void stderr_tty_init() +{ + /* Ensure that if stderr is a tty, we can get it back to a sane state. */ + if ((flags & FLAG_STDERR_TTY) && isatty(STDERR_FILENO)) { + stderr_is_a_tty = TRUE; + tcgetattr(STDERR_FILENO, &orig_termios_stderr); + } +} + +void premsg(struct termios *cf) +{ + if (stderr_is_a_tty) { + tcgetattr(STDERR_FILENO, cf); + tcsetattr(STDERR_FILENO, TCSADRAIN, &orig_termios_stderr); + } +} +void postmsg(struct termios *cf) +{ + if (stderr_is_a_tty) + tcsetattr(STDERR_FILENO, TCSADRAIN, cf); +} + /* * Clean up and exit. */ @@ -101,6 +126,7 @@ int verify_ssh_host_key(void *frontend, char *host, int port, char *keytype, static const char abandoned[] = "Connection abandoned.\n"; char line[32]; + struct termios cf; /* * Verify the key. @@ -110,6 +136,7 @@ int verify_ssh_host_key(void *frontend, char *host, int port, char *keytype, if (ret == 0) /* success - key matched OK */ return 1; + premsg(&cf); if (ret == 2) { /* key was different */ if (console_batch_mode) { fprintf(stderr, wrongmsg_batch, keytype, fingerprint); @@ -141,9 +168,11 @@ int verify_ssh_host_key(void *frontend, char *host, int port, char *keytype, if (line[0] != '\0' && line[0] != '\r' && line[0] != '\n') { if (line[0] == 'y' || line[0] == 'Y') store_host_key(host, port, keytype, keystr); + postmsg(&cf); return 1; } else { fprintf(stderr, abandoned); + postmsg(&cf); return 0; } } @@ -166,7 +195,9 @@ int askalg(void *frontend, const char *algtype, const char *algname, static const char abandoned[] = "Connection abandoned.\n"; char line[32]; + struct termios cf; + premsg(&cf); if (console_batch_mode) { fprintf(stderr, msg_batch, algtype, algname); return 0; @@ -187,9 +218,11 @@ int askalg(void *frontend, const char *algtype, const char *algname, } if (line[0] == 'y' || line[0] == 'Y') { + postmsg(&cf); return 1; } else { fprintf(stderr, abandoned); + postmsg(&cf); return 0; } } @@ -215,7 +248,9 @@ int askappend(void *frontend, Filename filename, "Logging will not be enabled.\n"; char line[32]; + struct termios cf; + premsg(&cf); if (console_batch_mode) { fprintf(stderr, msgtemplate_batch, FILENAME_MAX, filename.path); fflush(stderr); @@ -235,6 +270,7 @@ int askappend(void *frontend, Filename filename, tcsetattr(0, TCSANOW, &oldmode); } + postmsg(&cf); if (line[0] == 'y' || line[0] == 'Y') return 2; else if (line[0] == 'n' || line[0] == 'N') @@ -266,7 +302,10 @@ void old_keyfile_warning(void) "Once the key is loaded into PuTTYgen, you can perform\n" "this conversion simply by saving it again.\n"; + struct termios cf; + premsg(&cf); fputs(message, stderr); + postmsg(&cf); } void console_provide_logctx(void *logctx) @@ -276,8 +315,11 @@ void console_provide_logctx(void *logctx) void logevent(void *frontend, const char *string) { + struct termios cf; + premsg(&cf); if (console_logctx) log_eventlog(console_logctx, string); + postmsg(&cf); } static void console_data_untrusted(const char *data, int len) diff --git a/unix/uxplink.c b/unix/uxplink.c index 13369b10..a1dac9ad 100644 --- a/unix/uxplink.c +++ b/unix/uxplink.c @@ -27,14 +27,19 @@ void *logctx; +static struct termios orig_termios; + void fatalbox(char *p, ...) { + struct termios cf; va_list ap; + premsg(&cf); fprintf(stderr, "FATAL ERROR: "); va_start(ap, p); vfprintf(stderr, p, ap); va_end(ap); fputc('\n', stderr); + postmsg(&cf); if (logctx) { log_free(logctx); logctx = NULL; @@ -43,12 +48,15 @@ void fatalbox(char *p, ...) } void modalfatalbox(char *p, ...) { + struct termios cf; va_list ap; + premsg(&cf); fprintf(stderr, "FATAL ERROR: "); va_start(ap, p); vfprintf(stderr, p, ap); va_end(ap); fputc('\n', stderr); + postmsg(&cf); if (logctx) { log_free(logctx); logctx = NULL; @@ -57,12 +65,15 @@ void modalfatalbox(char *p, ...) } void connection_fatal(void *frontend, char *p, ...) { + struct termios cf; va_list ap; + premsg(&cf); fprintf(stderr, "FATAL ERROR: "); va_start(ap, p); vfprintf(stderr, p, ap); va_end(ap); fputc('\n', stderr); + postmsg(&cf); if (logctx) { log_free(logctx); logctx = NULL; @@ -71,17 +82,19 @@ void connection_fatal(void *frontend, char *p, ...) } void cmdline_error(char *p, ...) { + struct termios cf; va_list ap; + premsg(&cf); fprintf(stderr, "plink: "); va_start(ap, p); vfprintf(stderr, p, ap); va_end(ap); fputc('\n', stderr); + postmsg(&cf); exit(1); } static int local_tty = FALSE; /* do we have a local tty? */ -static struct termios orig_termios; static Backend *back; static void *backhandle; @@ -582,7 +595,9 @@ int main(int argc, char **argv) default_protocol = PROT_SSH; default_port = 22; - flags = FLAG_STDERR; + flags = FLAG_STDERR | FLAG_STDERR_TTY; + + stderr_tty_init(); /* * Process the command line. */ From 17bc691cc277be4c5a89c1c7ec7bbe57add4ab95 Mon Sep 17 00:00:00 2001 From: Ben Harris Date: Sat, 29 Sep 2007 14:20:55 +0000 Subject: [PATCH 098/124] Now that PuTTY is actually using names "@putty.projects.tartarus.org", it seems like a good idea to document them. [originally from svn r7747] --- doc/Makefile | 2 +- doc/sshnames.but | 77 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 78 insertions(+), 1 deletion(-) create mode 100644 doc/sshnames.but diff --git a/doc/Makefile b/doc/Makefile index 36e3672b..89b4be92 100644 --- a/doc/Makefile +++ b/doc/Makefile @@ -35,7 +35,7 @@ VERSIONIDS=vids endif CHAPTERS := $(SITE) blurb intro gs using config pscp psftp plink pubkey -CHAPTERS += pageant errors faq feedback licence udp pgpkeys +CHAPTERS += pageant errors faq feedback licence udp pgpkeys sshnames CHAPTERS += index $(VERSIONIDS) INPUTS = $(patsubst %,%.but,$(CHAPTERS)) diff --git a/doc/sshnames.but b/doc/sshnames.but new file mode 100644 index 00000000..97acc7b0 --- /dev/null +++ b/doc/sshnames.but @@ -0,0 +1,77 @@ +\define{versionidsshnames} \versionid $Id$ + +\A{sshnames} SSH-2 names specified for PuTTY + +There are various parts of the SSH-2 protocol where things are specified +using a textual name. Names ending in \cw{@putty.projects.tartarus.org} +are reserved for allocation by the PuTTY team. Allocated names are +documented here. + +\H{sshnames-global} Connection protocol global request name + +This name can be sent in a \cw{SSH_MSG_GLOBAL_REQUEST} message. + +\dt \cw{simple@putty.projects.tartarus.org} + +\dd This is sent by a client to announce that it will not have more that +one channel open at a time in the current connection. The intention +is that the server, knowing this, can set the window on that one +channel to something very large, and leave flow control to TCP. The +format of the request is: + +\lcont{ + +\c byte SSH_MSG_GLOBAL_REQUEST +\c uint32 recipient channel +\c string "simple@putty.projects.tartarus.org" +\c boolean want reply + +} + +\H{sshnames-channel} Connection protocol channel request name + +This name can be sent in a \cw{SSH_MSG_CHANNEL_REQUEST} message. + +\dt \cw{winadj@putty.project.tartarus.org} + +\dd PuTTY sends this request along with some +\cw{SSH_MSG_CHANNEL_WINDOW_ADJUST} messages as part of its window-size +tuning. It can be sent on any type of channel. Servers MUST treat it +as an unrecognised request and respond with +\cw{SSH_MSG_CHANNEL_FAILURE}. + +\H{sshnames-kex} Key exchange method names + +\dt \cw{rsa-sha1-draft-00@putty.projects.tartarus.org} + +\dt \cw{rsa-sha256-draft-00@putty.projects.tartarus.org} + +\dt \cw{rsa1024-sha1-draft-01@putty.projects.tartarus.org} + +\dt \cw{rsa1024-sha256-draft-01@putty.projects.tartarus.org} + +\dt \cw{rsa2048-sha256-draft-01@putty.projects.tartarus.org} + +\dt \cw{rsa1024-sha1-draft-02@putty.projects.tartarus.org} + +\dt \cw{rsa2048-sha512-draft-02@putty.projects.tartarus.org} + +\dt \cw{rsa1024-sha1-draft-03@putty.projects.tartarus.org} + +\dt \cw{rsa2048-sha256-draft-03@putty.projects.tartarus.org} + +\dt \cw{rsa1024-sha1-draft-04@putty.projects.tartarus.org} + +\dt \cw{rsa2048-sha256-draft-04@putty.projects.tartarus.org} + +\dd These appeared in various drafts of what eventually became RFC\_4432. +They have been superseded by \cw{rsa1024-sha1} and \cw{rsa2048-sha256}. + +\H{sshnames-encrypt} Encryption algorithm names + +\dt \cw{arcfour128-draft-00@putty.projects.tartarus.org} + +\dt \cw{arcfour256-draft-00@putty.projects.tartarus.org} + +\dd These were used in drafts of what eventually became RFC\_4345. +They have been superseded by \cw{arcfour128} and \cw{arcfour256}. From c5996bcde5d83bb7cd7a80bcb8b7479faf1655e0 Mon Sep 17 00:00:00 2001 From: Ben Harris Date: Sun, 30 Sep 2007 12:45:49 +0000 Subject: [PATCH 099/124] When writing session data to stdout or stderr, switch the relevant file descriptor into non-blocking mode temporarily, and correctly handle returns of EAGAIN from write(). This should fix unix-plink-stdout-nonblock, while avoiding EAGAIN turning up where we aren't expecting it. [originally from svn r7748] --- unix/uxplink.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/unix/uxplink.c b/unix/uxplink.c index a1dac9ad..9d5f0016 100644 --- a/unix/uxplink.c +++ b/unix/uxplink.c @@ -389,16 +389,21 @@ void try_output(int is_stderr) bufchain *chain = (is_stderr ? &stderr_data : &stdout_data); int fd = (is_stderr ? STDERR_FILENO : STDOUT_FILENO); void *senddata; - int sendlen, ret; + int sendlen, ret, fl; if (bufchain_size(chain) == 0) return; bufchain_prefix(chain, &senddata, &sendlen); + fl = fcntl(fd, F_GETFL); + if (fl != -1 && !(fl & O_NONBLOCK)) + fcntl(fd, F_SETFL, fl | O_NONBLOCK); ret = write(fd, senddata, sendlen); + if (fl != -1 && !(fl & O_NONBLOCK)) + fcntl(fd, F_SETFL, fl); if (ret > 0) bufchain_consume(chain, ret); - else if (ret < 0) { + else if (ret < 0 && errno != EAGAIN) { perror(is_stderr ? "stderr: write" : "stdout: write"); exit(1); } From ef370ee6fa3ff670a3146f3ee08f312b12ce414e Mon Sep 17 00:00:00 2001 From: Ben Harris Date: Sun, 30 Sep 2007 14:14:29 +0000 Subject: [PATCH 100/124] Set cfg.ssh_simple if there are no forwardings. [originally from svn r7750] --- unix/uxplink.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/unix/uxplink.c b/unix/uxplink.c index 9d5f0016..9867df2e 100644 --- a/unix/uxplink.c +++ b/unix/uxplink.c @@ -872,6 +872,14 @@ int main(int argc, char **argv) sk_init(); uxsel_init(); + /* + * Unix Plink doesn't provide any way to add forwardings after the + * connection is set up, so if there are none now, we can safely set + * the "simple" flag. + */ + if (cfg.protocol == PROT_SSH && !cfg.x11_forward && !cfg.agentfwd && + cfg.portfwd[0] == '\0' && cfg.portfwd[1] == '\0') + cfg.ssh_simple = TRUE; /* * Start up the connection. */ From da5d553afc99b849d1baaa1c0724116cf73ebb2e Mon Sep 17 00:00:00 2001 From: Ben Harris Date: Sun, 30 Sep 2007 19:42:31 +0000 Subject: [PATCH 101/124] Merge the looking up of channel numbers for SSH-2 channel messages into a single function which also handles checking that channels exist and are properly open. This should make PuTTY a little less tolerant of servers that send bogus messages. [originally from svn r7751] --- ssh.c | 82 +++++++++++++++++++++++++++++++---------------------------- 1 file changed, 43 insertions(+), 39 deletions(-) diff --git a/ssh.c b/ssh.c index d1a2c80f..0c878c91 100644 --- a/ssh.c +++ b/ssh.c @@ -6276,6 +6276,30 @@ static void ssh2_set_window(struct ssh_channel *c, int newwin) } } +/* + * Find the channel associated with a message. If there's no channel, + * or it's not properly open, make a noise about it and return NULL. + */ +static struct ssh_channel *ssh2_channel_msg(Ssh ssh, struct Packet *pktin) +{ + unsigned localid = ssh_pkt_getuint32(pktin); + struct ssh_channel *c; + + c = find234(ssh->channels, &localid, ssh_channelfind); + if (!c || + (c->halfopen && pktin->type != SSH2_MSG_CHANNEL_OPEN_CONFIRMATION && + pktin->type != SSH2_MSG_CHANNEL_OPEN_FAILURE)) { + char *buf = dupprintf("Received %s for %s channel %u", + ssh2_pkt_type(ssh->pkt_kctx, ssh->pkt_actx, + pktin->type), + c ? "half-open" : "nonexistent", localid); + ssh_disconnect(ssh, NULL, buf, SSH2_DISCONNECT_PROTOCOL_ERROR, FALSE); + sfree(buf); + return NULL; + } + return c; +} + static void ssh2_msg_channel_failure(Ssh ssh, struct Packet *pktin) { /* @@ -6284,13 +6308,12 @@ static void ssh2_msg_channel_failure(Ssh ssh, struct Packet *pktin) * sent with want_reply false or are sent before this handler gets * installed. */ - unsigned i = ssh_pkt_getuint32(pktin); struct ssh_channel *c; struct winadj *wa; - c = find234(ssh->channels, &i, ssh_channelfind); + c = ssh2_channel_msg(ssh, pktin); if (!c) - return; /* nonexistent channel */ + return; wa = c->v.v2.winadj_head; if (!wa) logevent("excess SSH_MSG_CHANNEL_FAILURE"); @@ -6310,10 +6333,11 @@ static void ssh2_msg_channel_failure(Ssh ssh, struct Packet *pktin) static void ssh2_msg_channel_window_adjust(Ssh ssh, struct Packet *pktin) { - unsigned i = ssh_pkt_getuint32(pktin); struct ssh_channel *c; - c = find234(ssh->channels, &i, ssh_channelfind); - if (c && !c->closes) { + c = ssh2_channel_msg(ssh, pktin); + if (!c) + return; + if (!c->closes) { c->v.v2.remwindow += ssh_pkt_getuint32(pktin); ssh2_try_send_and_unthrottle(c); } @@ -6323,11 +6347,10 @@ static void ssh2_msg_channel_data(Ssh ssh, struct Packet *pktin) { char *data; int length; - unsigned i = ssh_pkt_getuint32(pktin); struct ssh_channel *c; - c = find234(ssh->channels, &i, ssh_channelfind); + c = ssh2_channel_msg(ssh, pktin); if (!c) - return; /* nonexistent channel */ + return; if (pktin->type == SSH2_MSG_CHANNEL_EXTENDED_DATA && ssh_pkt_getuint32(pktin) != SSH2_EXTENDED_DATA_STDERR) return; /* extended but not stderr */ @@ -6414,12 +6437,11 @@ static void ssh2_msg_channel_data(Ssh ssh, struct Packet *pktin) static void ssh2_msg_channel_eof(Ssh ssh, struct Packet *pktin) { - unsigned i = ssh_pkt_getuint32(pktin); struct ssh_channel *c; - c = find234(ssh->channels, &i, ssh_channelfind); + c = ssh2_channel_msg(ssh, pktin); if (!c) - return; /* nonexistent channel */ + return; if (c->type == CHAN_X11) { /* @@ -6438,16 +6460,12 @@ static void ssh2_msg_channel_eof(Ssh ssh, struct Packet *pktin) static void ssh2_msg_channel_close(Ssh ssh, struct Packet *pktin) { - unsigned i = ssh_pkt_getuint32(pktin); struct ssh_channel *c; struct Packet *pktout; - c = find234(ssh->channels, &i, ssh_channelfind); - if (!c || c->halfopen) { - bombout(("Received CHANNEL_CLOSE for %s channel %d\n", - c ? "half-open" : "nonexistent", i)); + c = ssh2_channel_msg(ssh, pktin); + if (!c) return; - } /* Do pre-close processing on the channel. */ switch (c->type) { case CHAN_MAINSESSION: @@ -6500,13 +6518,12 @@ static void ssh2_msg_channel_close(Ssh ssh, struct Packet *pktin) static void ssh2_msg_channel_open_confirmation(Ssh ssh, struct Packet *pktin) { - unsigned i = ssh_pkt_getuint32(pktin); struct ssh_channel *c; struct Packet *pktout; - c = find234(ssh->channels, &i, ssh_channelfind); + c = ssh2_channel_msg(ssh, pktin); if (!c) - return; /* nonexistent channel */ + return; if (c->type != CHAN_SOCKDATA_DORMANT) return; /* dunno why they're confirming this */ c->remoteid = ssh_pkt_getuint32(pktin); @@ -6538,14 +6555,13 @@ static void ssh2_msg_channel_open_failure(Ssh ssh, struct Packet *pktin) "Unknown channel type", "Resource shortage", }; - unsigned i = ssh_pkt_getuint32(pktin); unsigned reason_code; char *reason_string; int reason_length; struct ssh_channel *c; - c = find234(ssh->channels, &i, ssh_channelfind); + c = ssh2_channel_msg(ssh, pktin); if (!c) - return; /* nonexistent channel */ + return; if (c->type != CHAN_SOCKDATA_DORMANT) return; /* dunno why they're failing this */ @@ -6564,30 +6580,18 @@ static void ssh2_msg_channel_open_failure(Ssh ssh, struct Packet *pktin) static void ssh2_msg_channel_request(Ssh ssh, struct Packet *pktin) { - unsigned localid; char *type; int typelen, want_reply; int reply = SSH2_MSG_CHANNEL_FAILURE; /* default */ struct ssh_channel *c; struct Packet *pktout; - localid = ssh_pkt_getuint32(pktin); + c = ssh2_channel_msg(ssh, pktin); + if (!c) + return; ssh_pkt_getstring(pktin, &type, &typelen); want_reply = ssh2_pkt_getbool(pktin); - /* - * First, check that the channel exists. Otherwise, - * we can instantly disconnect with a rude message. - */ - c = find234(ssh->channels, &localid, ssh_channelfind); - if (!c) { - char *buf = dupprintf("Received channel request for nonexistent" - " channel %d", localid); - ssh_disconnect(ssh, NULL, buf, SSH2_DISCONNECT_PROTOCOL_ERROR, FALSE); - sfree(buf); - return; - } - /* * Having got the channel number, we now look at * the request type string to see if it's something From 2db59b7443a193b200ee46a00f72139c0bac7b33 Mon Sep 17 00:00:00 2001 From: Ben Harris Date: Mon, 1 Oct 2007 21:11:11 +0000 Subject: [PATCH 102/124] Rather than rejecting spurious SSH_MSG_CHANNEL_SUCCESSes, and ignoring spurious SSH_MSG_CHANNEL_FAILUREs, treat them as the protocol errors they are and forcibly disconnect. Inspired by recent traffic on comp.security.ssh. [originally from svn r7752] --- ssh.c | 53 ++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 40 insertions(+), 13 deletions(-) diff --git a/ssh.c b/ssh.c index 0c878c91..fb24f435 100644 --- a/ssh.c +++ b/ssh.c @@ -6300,6 +6300,30 @@ static struct ssh_channel *ssh2_channel_msg(Ssh ssh, struct Packet *pktin) return c; } +static void ssh2_msg_channel_success(Ssh ssh, struct Packet *pktin) +{ + /* + * This should never get called. All channel requests are either + * sent with want_reply false or are sent before this handler gets + * installed. + */ + struct ssh_channel *c; + struct winadj *wa; + + c = ssh2_channel_msg(ssh, pktin); + if (!c) + return; + wa = c->v.v2.winadj_head; + if (wa) + ssh_disconnect(ssh, NULL, "Received SSH_MSG_CHANNEL_SUCCESS for " + "\"winadj@putty.projects.tartarus.org\"", + SSH2_DISCONNECT_PROTOCOL_ERROR, FALSE); + else + ssh_disconnect(ssh, NULL, + "Received unsolicited SSH_MSG_CHANNEL_SUCCESS", + SSH2_DISCONNECT_PROTOCOL_ERROR, FALSE); +} + static void ssh2_msg_channel_failure(Ssh ssh, struct Packet *pktin) { /* @@ -6315,20 +6339,22 @@ static void ssh2_msg_channel_failure(Ssh ssh, struct Packet *pktin) if (!c) return; wa = c->v.v2.winadj_head; - if (!wa) - logevent("excess SSH_MSG_CHANNEL_FAILURE"); - else { - c->v.v2.winadj_head = wa->next; - c->v.v2.remlocwin += wa->size; - sfree(wa); - /* - * winadj messages are only sent when the window is fully open, - * so if we get an ack of one, we know any pending unthrottle - * is complete. - */ - if (c->v.v2.throttle_state == UNTHROTTLING) - c->v.v2.throttle_state = UNTHROTTLED; + if (!wa) { + ssh_disconnect(ssh, NULL, + "Received unsolicited SSH_MSG_CHANNEL_FAILURE", + SSH2_DISCONNECT_PROTOCOL_ERROR, FALSE); + return; } + c->v.v2.winadj_head = wa->next; + c->v.v2.remlocwin += wa->size; + sfree(wa); + /* + * winadj messages are only sent when the window is fully open, so + * if we get an ack of one, we know any pending unthrottle is + * complete. + */ + if (c->v.v2.throttle_state == UNTHROTTLING) + c->v.v2.throttle_state = UNTHROTTLED; } static void ssh2_msg_channel_window_adjust(Ssh ssh, struct Packet *pktin) @@ -8462,6 +8488,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, * All the initial channel requests are done, so install the default * failure handler. */ + ssh->packet_dispatch[SSH2_MSG_CHANNEL_SUCCESS] = ssh2_msg_channel_success; ssh->packet_dispatch[SSH2_MSG_CHANNEL_FAILURE] = ssh2_msg_channel_failure; /* From 241c53aceab81fe84edd4beb46a0685a857c9a7c Mon Sep 17 00:00:00 2001 From: Ben Harris Date: Tue, 2 Oct 2007 21:07:52 +0000 Subject: [PATCH 103/124] As far as I can see (at least in NetBSD) O_NONBLOCK and FIONBIO are equivalent, except that O_NONBLOCK is standardised and FIONBIO isn't. In consequence, replace our only use of FIONBIO with O_NONBLOCK. Inspired by Jonathan H N Chin, who had problems with this on Solaris. [originally from svn r7753] --- unix/uxpty.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/unix/uxpty.c b/unix/uxpty.c index 60dc7f7d..ca7e98ad 100644 --- a/unix/uxpty.c +++ b/unix/uxpty.c @@ -360,8 +360,10 @@ static void pty_open_master(Pty pty) /* * Set the pty master into non-blocking mode. */ - int i = 1; - ioctl(pty->master_fd, FIONBIO, &i); + int fl; + fl = fcntl(pty->master_fd, F_GETFL); + if (fl != -1 && !(fl & O_NONBLOCK)) + fcntl(pty->master_fd, F_SETFL, fl | O_NONBLOCK); } if (!ptys_by_fd) From ea9a3bdb7d76554acb1524167118c570a306465c Mon Sep 17 00:00:00 2001 From: Ben Harris Date: Tue, 2 Oct 2007 21:43:53 +0000 Subject: [PATCH 104/124] More fixes to stdout and stderr. When the backlog on either clears, call the backend's unthrottle function. If we don't, we'll deadlock. While we're here, also pump as much data as possible out during each call to try_output(), rather than restricting ourselves to a single call to write(). [originally from svn r7755] --- unix/uxplink.c | 32 ++++++++++++++------------------ 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/unix/uxplink.c b/unix/uxplink.c index 9867df2e..0c34a296 100644 --- a/unix/uxplink.c +++ b/unix/uxplink.c @@ -384,7 +384,7 @@ void cleanup_termios(void) bufchain stdout_data, stderr_data; -void try_output(int is_stderr) +int try_output(int is_stderr) { bufchain *chain = (is_stderr ? &stderr_data : &stdout_data); int fd = (is_stderr ? STDERR_FILENO : STDOUT_FILENO); @@ -392,40 +392,36 @@ void try_output(int is_stderr) int sendlen, ret, fl; if (bufchain_size(chain) == 0) - return; + return bufchain_size(&stdout_data) + bufchain_size(&stderr_data); - bufchain_prefix(chain, &senddata, &sendlen); fl = fcntl(fd, F_GETFL); if (fl != -1 && !(fl & O_NONBLOCK)) fcntl(fd, F_SETFL, fl | O_NONBLOCK); - ret = write(fd, senddata, sendlen); + do { + bufchain_prefix(chain, &senddata, &sendlen); + ret = write(fd, senddata, sendlen); + if (ret > 0) + bufchain_consume(chain, ret); + } while (ret == sendlen && bufchain_size(chain) != 0); if (fl != -1 && !(fl & O_NONBLOCK)) fcntl(fd, F_SETFL, fl); - if (ret > 0) - bufchain_consume(chain, ret); - else if (ret < 0 && errno != EAGAIN) { + if (ret < 0 && errno != EAGAIN) { perror(is_stderr ? "stderr: write" : "stdout: write"); exit(1); } + return bufchain_size(&stdout_data) + bufchain_size(&stderr_data); } int from_backend(void *frontend_handle, int is_stderr, const char *data, int len) { - int osize, esize; - if (is_stderr) { bufchain_add(&stderr_data, data, len); - try_output(TRUE); + return try_output(TRUE); } else { bufchain_add(&stdout_data, data, len); - try_output(FALSE); + return try_output(FALSE); } - - osize = bufchain_size(&stdout_data); - esize = bufchain_size(&stderr_data); - - return osize + esize; } int from_backend_untrusted(void *frontend_handle, const char *data, int len) @@ -1061,11 +1057,11 @@ int main(int argc, char **argv) } if (FD_ISSET(STDOUT_FILENO, &wset)) { - try_output(FALSE); + back->unthrottle(backhandle, try_output(FALSE)); } if (FD_ISSET(STDERR_FILENO, &wset)) { - try_output(TRUE); + back->unthrottle(backhandle, try_output(TRUE)); } if ((!connopen || !back->connected(backhandle)) && From 3c149087e4d33cce70e3e856b9f43a58d242e5c4 Mon Sep 17 00:00:00 2001 From: Ben Harris Date: Wed, 3 Oct 2007 20:29:27 +0000 Subject: [PATCH 105/124] Take the code that does flow control in SSH-1, and make it work in SSH-2 as well. This won't be triggered in the usual case, but it's useful if the remote end ignores our window, or if we're in "simple" mode and setting the window far larger than is necessary. [originally from svn r7756] --- ssh.c | 86 ++++++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 59 insertions(+), 27 deletions(-) diff --git a/ssh.c b/ssh.c index fb24f435..87fabfbd 100644 --- a/ssh.c +++ b/ssh.c @@ -560,10 +560,12 @@ struct ssh_channel { * A channel is completely finished with when all four bits are set. */ int closes; + /* + * True if this channel is causing the underlying connection to be + * throttled. + */ + int throttling_conn; union { - struct ssh1_data_channel { - int throttling; - } v1; struct ssh2_data_channel { bufchain outbuffer; unsigned remwindow, remmaxpkt; @@ -810,7 +812,7 @@ struct ssh_tag { void *x11auth; int version; - int v1_throttle_count; + int conn_throttle_count; int overall_bufsize; int throttled_all; int v1_stdout_throttling; @@ -2873,14 +2875,14 @@ static const char *connect_to_host(Ssh ssh, char *host, int port, /* * Throttle or unthrottle the SSH connection. */ -static void ssh1_throttle(Ssh ssh, int adjust) +static void ssh_throttle_conn(Ssh ssh, int adjust) { - int old_count = ssh->v1_throttle_count; - ssh->v1_throttle_count += adjust; - assert(ssh->v1_throttle_count >= 0); - if (ssh->v1_throttle_count && !old_count) { + int old_count = ssh->conn_throttle_count; + ssh->conn_throttle_count += adjust; + assert(ssh->conn_throttle_count >= 0); + if (ssh->conn_throttle_count && !old_count) { ssh_set_frozen(ssh, 1); - } else if (!ssh->v1_throttle_count && old_count) { + } else if (!ssh->conn_throttle_count && old_count) { ssh_set_frozen(ssh, 0); } } @@ -4054,17 +4056,20 @@ int sshfwd_write(struct ssh_channel *c, char *buf, int len) void sshfwd_unthrottle(struct ssh_channel *c, int bufsize) { Ssh ssh = c->ssh; + int buflimit; if (ssh->state == SSH_STATE_CLOSED) return; if (ssh->version == 1) { - if (c->v.v1.throttling && bufsize < SSH1_BUFFER_LIMIT) { - c->v.v1.throttling = 0; - ssh1_throttle(ssh, -1); - } + buflimit = SSH1_BUFFER_LIMIT; } else { - ssh2_set_window(c, c->v.v2.locmaxwin - bufsize); + buflimit = c->v.v2.locmaxwin; + ssh2_set_window(c, bufsize < buflimit ? buflimit - bufsize : 0); + } + if (c->throttling_conn && bufsize <= buflimit) { + c->throttling_conn = 0; + ssh_throttle_conn(ssh, -1); } } @@ -4493,7 +4498,7 @@ static void ssh1_smsg_stdout_stderr_data(Ssh ssh, struct Packet *pktin) string, stringlen); if (!ssh->v1_stdout_throttling && bufsize > SSH1_BUFFER_LIMIT) { ssh->v1_stdout_throttling = 1; - ssh1_throttle(ssh, +1); + ssh_throttle_conn(ssh, +1); } } @@ -4527,7 +4532,7 @@ static void ssh1_smsg_x11_open(Ssh ssh, struct Packet *pktin) c->halfopen = FALSE; c->localid = alloc_channel_id(ssh); c->closes = 0; - c->v.v1.throttling = 0; + c->throttling_conn = 0; c->type = CHAN_X11; /* identify channel type */ add234(ssh->channels, c); send_packet(ssh, SSH1_MSG_CHANNEL_OPEN_CONFIRMATION, @@ -4556,7 +4561,7 @@ static void ssh1_smsg_agent_open(Ssh ssh, struct Packet *pktin) c->halfopen = FALSE; c->localid = alloc_channel_id(ssh); c->closes = 0; - c->v.v1.throttling = 0; + c->throttling_conn = 0; c->type = CHAN_AGENT; /* identify channel type */ c->u.a.lensofar = 0; add234(ssh->channels, c); @@ -4610,7 +4615,7 @@ static void ssh1_msg_port_open(Ssh ssh, struct Packet *pktin) c->halfopen = FALSE; c->localid = alloc_channel_id(ssh); c->closes = 0; - c->v.v1.throttling = 0; + c->throttling_conn = 0; c->type = CHAN_SOCKDATA; /* identify channel type */ add234(ssh->channels, c); send_packet(ssh, SSH1_MSG_CHANNEL_OPEN_CONFIRMATION, @@ -4632,7 +4637,7 @@ static void ssh1_msg_channel_open_confirmation(Ssh ssh, struct Packet *pktin) c->remoteid = localid; c->halfopen = FALSE; c->type = CHAN_SOCKDATA; - c->v.v1.throttling = 0; + c->throttling_conn = 0; pfd_confirm(c->u.pfd.s); } @@ -4768,9 +4773,9 @@ static void ssh1_msg_channel_data(Ssh ssh, struct Packet *pktin) bufsize = 0; /* agent channels never back up */ break; } - if (!c->v.v1.throttling && bufsize > SSH1_BUFFER_LIMIT) { - c->v.v1.throttling = 1; - ssh1_throttle(ssh, +1); + if (!c->throttling_conn && bufsize > SSH1_BUFFER_LIMIT) { + c->throttling_conn = 1; + ssh_throttle_conn(ssh, +1); } } } @@ -6458,6 +6463,17 @@ static void ssh2_msg_channel_data(Ssh ssh, struct Packet *pktin) */ ssh2_set_window(c, bufsize < c->v.v2.locmaxwin ? c->v.v2.locmaxwin - bufsize : 0); + /* + * If we're either buffering way too much data, or if we're + * buffering anything at all and we're in "simple" mode, + * throttle the whole channel. + */ + if ((bufsize > c->v.v2.locmaxwin || + (ssh->cfg.ssh_simple && bufsize > 0)) && + !c->throttling_conn) { + c->throttling_conn = 1; + ssh_throttle_conn(ssh, +1); + } } } @@ -6905,6 +6921,7 @@ static void ssh2_msg_channel_open(Ssh ssh, struct Packet *pktin) } else { c->localid = alloc_channel_id(ssh); c->closes = 0; + c->throttling_conn = FALSE; c->v.v2.locwindow = c->v.v2.locmaxwin = OUR_V2_WINSIZE; c->v.v2.remwindow = winsize; c->v.v2.remmaxpkt = pktsize; @@ -8119,6 +8136,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, s->pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_OPEN); ssh2_pkt_addstring(s->pktout, "direct-tcpip"); ssh2_pkt_adduint32(s->pktout, ssh->mainchan->localid); + ssh->mainchan->throttling_conn = FALSE; ssh->mainchan->v.v2.locwindow = ssh->mainchan->v.v2.locmaxwin = ssh->mainchan->v.v2.remlocwin = ssh->cfg.ssh_simple ? OUR_V2_BIGWIN : OUR_V2_WINSIZE; @@ -8166,6 +8184,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, s->pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_OPEN); ssh2_pkt_addstring(s->pktout, "session"); ssh2_pkt_adduint32(s->pktout, ssh->mainchan->localid); + ssh->mainchan->throttling_conn = FALSE; ssh->mainchan->v.v2.locwindow = ssh->mainchan->v.v2.locmaxwin = ssh->mainchan->v.v2.remlocwin = ssh->cfg.ssh_simple ? OUR_V2_BIGWIN : OUR_V2_WINSIZE; @@ -8792,7 +8811,7 @@ static const char *ssh_init(void *frontend_handle, void **backend_handle, ssh->send_ok = 0; ssh->editing = 0; ssh->echoing = 0; - ssh->v1_throttle_count = 0; + ssh->conn_throttle_count = 0; ssh->overall_bufsize = 0; ssh->fallback_cmd = 0; @@ -9239,15 +9258,27 @@ void *new_sock_channel(void *handle, Socket s) static void ssh_unthrottle(void *handle, int bufsize) { Ssh ssh = (Ssh) handle; + int buflimit; + if (ssh->version == 1) { if (ssh->v1_stdout_throttling && bufsize < SSH1_BUFFER_LIMIT) { ssh->v1_stdout_throttling = 0; - ssh1_throttle(ssh, -1); + ssh_throttle_conn(ssh, -1); } } else { - if (ssh->mainchan) + if (ssh->mainchan) { ssh2_set_window(ssh->mainchan, - ssh->mainchan->v.v2.locmaxwin - bufsize); + bufsize < ssh->mainchan->v.v2.locmaxwin ? + ssh->mainchan->v.v2.locmaxwin - bufsize : 0); + if (ssh->cfg.ssh_simple) + buflimit = 0; + else + buflimit = ssh->mainchan->v.v2.locmaxwin; + if (ssh->mainchan->throttling_conn && bufsize <= buflimit) { + ssh->mainchan->throttling_conn = 0; + ssh_throttle_conn(ssh, -1); + } + } } } @@ -9270,6 +9301,7 @@ void ssh_send_port_open(void *channel, char *hostname, int port, char *org) pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_OPEN); ssh2_pkt_addstring(pktout, "direct-tcpip"); ssh2_pkt_adduint32(pktout, c->localid); + c->throttling_conn = FALSE; c->v.v2.locwindow = c->v.v2.locmaxwin = OUR_V2_WINSIZE; c->v.v2.remlocwin = OUR_V2_WINSIZE; c->v.v2.winadj_head = c->v.v2.winadj_head = NULL; From 4a9feea43d4c3282a42b29a5c26596339a23fcfb Mon Sep 17 00:00:00 2001 From: Ben Harris Date: Wed, 3 Oct 2007 21:04:26 +0000 Subject: [PATCH 106/124] Factor out the increasingly complicated SSH-2 channel structure initialisation into its own function. Maintaining four copies was getting boring. [originally from svn r7757] --- ssh.c | 56 ++++++++++++++++++++------------------------------------ 1 file changed, 20 insertions(+), 36 deletions(-) diff --git a/ssh.c b/ssh.c index 87fabfbd..92d96f1b 100644 --- a/ssh.c +++ b/ssh.c @@ -6204,6 +6204,22 @@ static void ssh2_try_send_and_unthrottle(struct ssh_channel *c) } } +/* + * Set up most of a new ssh_channel for SSH-2. + */ +static void ssh2_channel_init(struct ssh_channel *c) +{ + Ssh ssh = c->ssh; + c->localid = alloc_channel_id(ssh); + c->closes = 0; + c->throttling_conn = FALSE; + c->v.v2.locwindow = c->v.v2.locmaxwin = c->v.v2.remlocwin = + ssh->cfg.ssh_simple ? OUR_V2_BIGWIN : OUR_V2_WINSIZE; + c->v.v2.winadj_head = c->v.v2.winadj_tail = NULL; + c->v.v2.throttle_state = UNTHROTTLED; + bufchain_init(&c->v.v2.outbuffer); +} + /* * Potentially enlarge the window on an SSH-2 channel. */ @@ -6919,16 +6935,9 @@ static void ssh2_msg_channel_open(Ssh ssh, struct Packet *pktin) logeventf(ssh, "Rejected channel open: %s", error); sfree(c); } else { - c->localid = alloc_channel_id(ssh); - c->closes = 0; - c->throttling_conn = FALSE; - c->v.v2.locwindow = c->v.v2.locmaxwin = OUR_V2_WINSIZE; + ssh2_channel_init(c); c->v.v2.remwindow = winsize; c->v.v2.remmaxpkt = pktsize; - c->v.v2.remlocwin = OUR_V2_WINSIZE; - c->v.v2.winadj_head = c->v.v2.winadj_tail = NULL; - c->v.v2.throttle_state = UNTHROTTLED; - bufchain_init(&c->v.v2.outbuffer); add234(ssh->channels, c); pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_OPEN_CONFIRMATION); ssh2_pkt_adduint32(pktout, c->remoteid); @@ -8129,20 +8138,13 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, */ ssh->mainchan = snew(struct ssh_channel); ssh->mainchan->ssh = ssh; - ssh->mainchan->localid = alloc_channel_id(ssh); + ssh2_channel_init(ssh->mainchan); logeventf(ssh, "Opening direct-tcpip channel to %s:%d in place of session", ssh->cfg.ssh_nc_host, ssh->cfg.ssh_nc_port); s->pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_OPEN); ssh2_pkt_addstring(s->pktout, "direct-tcpip"); ssh2_pkt_adduint32(s->pktout, ssh->mainchan->localid); - ssh->mainchan->throttling_conn = FALSE; - ssh->mainchan->v.v2.locwindow = ssh->mainchan->v.v2.locmaxwin = - ssh->mainchan->v.v2.remlocwin = - ssh->cfg.ssh_simple ? OUR_V2_BIGWIN : OUR_V2_WINSIZE; - ssh->mainchan->v.v2.winadj_head = NULL; - ssh->mainchan->v.v2.winadj_tail = NULL; - ssh->mainchan->v.v2.throttle_state = UNTHROTTLED; ssh2_pkt_adduint32(s->pktout, ssh->mainchan->v.v2.locwindow);/* our window size */ ssh2_pkt_adduint32(s->pktout, OUR_V2_MAXPKT); /* our max pkt size */ ssh2_pkt_addstring(s->pktout, ssh->cfg.ssh_nc_host); @@ -8169,10 +8171,8 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, ssh->mainchan->remoteid = ssh_pkt_getuint32(pktin); ssh->mainchan->halfopen = FALSE; ssh->mainchan->type = CHAN_MAINSESSION; - ssh->mainchan->closes = 0; ssh->mainchan->v.v2.remwindow = ssh_pkt_getuint32(pktin); ssh->mainchan->v.v2.remmaxpkt = ssh_pkt_getuint32(pktin); - bufchain_init(&ssh->mainchan->v.v2.outbuffer); add234(ssh->channels, ssh->mainchan); update_specials_menu(ssh->frontend); logevent("Opened direct-tcpip channel"); @@ -8180,17 +8180,10 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, } else { ssh->mainchan = snew(struct ssh_channel); ssh->mainchan->ssh = ssh; - ssh->mainchan->localid = alloc_channel_id(ssh); + ssh2_channel_init(ssh->mainchan); s->pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_OPEN); ssh2_pkt_addstring(s->pktout, "session"); ssh2_pkt_adduint32(s->pktout, ssh->mainchan->localid); - ssh->mainchan->throttling_conn = FALSE; - ssh->mainchan->v.v2.locwindow = ssh->mainchan->v.v2.locmaxwin = - ssh->mainchan->v.v2.remlocwin = - ssh->cfg.ssh_simple ? OUR_V2_BIGWIN : OUR_V2_WINSIZE; - ssh->mainchan->v.v2.winadj_head = NULL; - ssh->mainchan->v.v2.winadj_tail = NULL; - ssh->mainchan->v.v2.throttle_state = UNTHROTTLED; ssh2_pkt_adduint32(s->pktout, ssh->mainchan->v.v2.locwindow);/* our window size */ ssh2_pkt_adduint32(s->pktout, OUR_V2_MAXPKT); /* our max pkt size */ ssh2_pkt_send(ssh, s->pktout); @@ -8207,10 +8200,8 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, ssh->mainchan->remoteid = ssh_pkt_getuint32(pktin); ssh->mainchan->halfopen = FALSE; ssh->mainchan->type = CHAN_MAINSESSION; - ssh->mainchan->closes = 0; ssh->mainchan->v.v2.remwindow = ssh_pkt_getuint32(pktin); ssh->mainchan->v.v2.remmaxpkt = ssh_pkt_getuint32(pktin); - bufchain_init(&ssh->mainchan->v.v2.outbuffer); add234(ssh->channels, ssh->mainchan); update_specials_menu(ssh->frontend); logevent("Opened channel for session"); @@ -9240,12 +9231,10 @@ void *new_sock_channel(void *handle, Socket s) c->ssh = ssh; if (c) { + ssh2_channel_init(c); c->halfopen = TRUE; - c->localid = alloc_channel_id(ssh); - c->closes = 0; c->type = CHAN_SOCKDATA_DORMANT;/* identify channel type */ c->u.pfd.s = s; - bufchain_init(&c->v.v2.outbuffer); add234(ssh->channels, c); } return c; @@ -9301,11 +9290,6 @@ void ssh_send_port_open(void *channel, char *hostname, int port, char *org) pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_OPEN); ssh2_pkt_addstring(pktout, "direct-tcpip"); ssh2_pkt_adduint32(pktout, c->localid); - c->throttling_conn = FALSE; - c->v.v2.locwindow = c->v.v2.locmaxwin = OUR_V2_WINSIZE; - c->v.v2.remlocwin = OUR_V2_WINSIZE; - c->v.v2.winadj_head = c->v.v2.winadj_head = NULL; - c->v.v2.throttle_state = UNTHROTTLED; ssh2_pkt_adduint32(pktout, c->v.v2.locwindow);/* our window size */ ssh2_pkt_adduint32(pktout, OUR_V2_MAXPKT); /* our max pkt size */ ssh2_pkt_addstring(pktout, hostname); From f8e7894e15123334f676b584d38c96f8f437e414 Mon Sep 17 00:00:00 2001 From: Ben Harris Date: Wed, 3 Oct 2007 21:06:00 +0000 Subject: [PATCH 107/124] snew() always returns non-NULL, so checking if its return value is NULL is pointless. [originally from svn r7758] --- ssh.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/ssh.c b/ssh.c index 92d96f1b..63aecbc2 100644 --- a/ssh.c +++ b/ssh.c @@ -9228,15 +9228,13 @@ void *new_sock_channel(void *handle, Socket s) Ssh ssh = (Ssh) handle; struct ssh_channel *c; c = snew(struct ssh_channel); - c->ssh = ssh; - if (c) { - ssh2_channel_init(c); - c->halfopen = TRUE; - c->type = CHAN_SOCKDATA_DORMANT;/* identify channel type */ - c->u.pfd.s = s; - add234(ssh->channels, c); - } + c->ssh = ssh; + ssh2_channel_init(c); + c->halfopen = TRUE; + c->type = CHAN_SOCKDATA_DORMANT;/* identify channel type */ + c->u.pfd.s = s; + add234(ssh->channels, c); return c; } From ca2b97f1d0165005d747f31959fd0ed4e8177934 Mon Sep 17 00:00:00 2001 From: Ben Harris Date: Wed, 3 Oct 2007 21:21:18 +0000 Subject: [PATCH 108/124] Replace mentions of SSH-2 I-Ds with references to the corresponding RFCs. [originally from svn r7759] --- ssh.c | 6 +++--- sshdes.c | 2 +- sshdss.c | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/ssh.c b/ssh.c index 63aecbc2..a4b78c4d 100644 --- a/ssh.c +++ b/ssh.c @@ -6677,7 +6677,7 @@ static void ssh2_msg_channel_request(Ssh ssh, struct Packet *pktin) 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 + * having been a `string' in the drafts of RFC 4254 since at * least 2001. (Fixed in session.c 1.147.) Try to * infer which we can safely parse it as. */ { @@ -6720,7 +6720,7 @@ static void ssh2_msg_channel_request(Ssh ssh, struct Packet *pktin) fmt_sig = dupprintf(" %d", signum); ssh->exitcode = 128 + signum; } else { - /* As per the drafts. */ + /* As per RFC 4254. */ char *sig; int siglen; ssh_pkt_getstring(pktin, &sig, &siglen); @@ -9079,7 +9079,7 @@ static const struct telnet_special *ssh_get_specials(void *handle) static const struct telnet_special ssh2_session_specials[] = { {NULL, TS_SEP}, {"Break", TS_BRK}, - /* These are the signal names defined by draft-ietf-secsh-connect-23. + /* These are the signal names defined by RFC 4254. * They include all the ISO C signals, but are a subset of the POSIX * required signals. */ {"SIGINT (Interrupt)", TS_SIGINT}, diff --git a/sshdes.c b/sshdes.c index 8c3ab72e..b12a91d5 100644 --- a/sshdes.c +++ b/sshdes.c @@ -959,7 +959,7 @@ static const struct ssh2_cipher ssh_3des_ssh2_ctr = { /* * Single DES in SSH-2. "des-cbc" is marked as HISTORIC in - * draft-ietf-secsh-assignednumbers-04.txt, referring to + * RFC 4250, referring to * FIPS-46-3. ("Single DES (i.e., DES) will be permitted * for legacy systems only.") , but ssh.com support it and * apparently aren't the only people to do so, so we sigh diff --git a/sshdss.c b/sshdss.c index 22992fea..7c95d11b 100644 --- a/sshdss.c +++ b/sshdss.c @@ -231,14 +231,14 @@ static int dss_verifysig(void *key, char *sig, int siglen, #endif /* * Commercial SSH (2.0.13) and OpenSSH disagree over the format - * of a DSA signature. OpenSSH is in line with the IETF drafts: + * of a DSA signature. OpenSSH is in line with RFC 4253: * it uses a string "ssh-dss", followed by a 40-byte string * containing two 160-bit integers end-to-end. Commercial SSH * can't be bothered with the header bit, and considers a DSA * signature blob to be _just_ the 40-byte string containing * the two 160-bit integers. We tell them apart by measuring * the length: length 40 means the commercial-SSH bug, anything - * else is assumed to be IETF-compliant. + * else is assumed to be RFC-compliant. */ if (siglen != 40) { /* bug not present; read admin fields */ getstring(&sig, &siglen, &p, &slen); From 439b72d947fba5892dacce72f4496e368704a9a4 Mon Sep 17 00:00:00 2001 From: Jacob Nevins Date: Fri, 19 Oct 2007 21:47:47 +0000 Subject: [PATCH 109/124] Marc TERRIER pointed out a couple of places that claim there is an X11 forwarding checkbox on the Tunnels panel, which hasn't been the case for a while. [originally from svn r7771] --- doc/using.but | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/doc/using.but b/doc/using.but index adf5ee9a..74c3f793 100644 --- a/doc/using.but +++ b/doc/using.but @@ -338,7 +338,7 @@ doesn't, the manual for the \i{X server} should tell you what it does do. You should then tick the \q{Enable X11 forwarding} box in the -Tunnels panel (see \k{config-ssh-x11}) before starting your SSH +X11 panel (see \k{config-ssh-x11}) before starting your SSH session. The \i{\q{X display location}} box is blank by default, which means that PuTTY will try to use a sensible default such as \c{:0}, which is the usual display location where your X server will be @@ -765,8 +765,7 @@ it off. These options are only meaningful if you are using SSH. For information on X11 forwarding, see \k{using-x-forwarding}. These options are equivalent to the X11 forwarding checkbox in the -Tunnels panel of the PuTTY configuration box (see -\k{config-ssh-x11}). +X11 panel of the PuTTY configuration box (see \k{config-ssh-x11}). These options are not available in the file transfer tools PSCP and PSFTP. From 868208b8bd7f0740197a53ea0a2acb7b94534835 Mon Sep 17 00:00:00 2001 From: Jacob Nevins Date: Sat, 27 Oct 2007 16:05:02 +0000 Subject: [PATCH 110/124] Reset mouse reporting mode as part of resetting the terminal. [originally from svn r7773] --- terminal.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/terminal.c b/terminal.c index 65efb8eb..079a738a 100644 --- a/terminal.c +++ b/terminal.c @@ -1223,6 +1223,8 @@ static void power_on(Terminal *term, int clear) term->erase_char = term->basic_erase_char; term->alt_which = 0; term_print_finish(term); + term->xterm_mouse = FALSE; + set_raw_mouse_mode(term->frontend, FALSE); { int i; for (i = 0; i < 256; i++) @@ -1448,7 +1450,7 @@ Terminal *term_init(Config *mycfg, struct unicode_data *ucsdata, term->vt52_mode = FALSE; term->cr_lf_return = FALSE; term->seen_disp_event = FALSE; - term->xterm_mouse = term->mouse_is_down = FALSE; + term->mouse_is_down = FALSE; term->reset_132 = FALSE; term->cblinker = term->tblinker = 0; term->has_focus = 1; From 712b4689c8e69f936b0fa8706bee9449c2db1ca9 Mon Sep 17 00:00:00 2001 From: Simon Tatham Date: Mon, 26 Nov 2007 21:09:54 +0000 Subject: [PATCH 111/124] sktree is indexed on the numeric value of the socket structure's underlying WinSock SOCKET. Therefore, if we plan to modify the SOCKET in a socket, we must remove it from the tree before doing so, and put it back again afterwards. Otherwise it'll violate the tree's sorting order, and sooner or later someone will try to find it and get back NULL. [originally from svn r7795] --- windows/winnet.c | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/windows/winnet.c b/windows/winnet.c index a5fc3834..19babb0f 100644 --- a/windows/winnet.c +++ b/windows/winnet.c @@ -96,6 +96,10 @@ static int cmpfortree(void *av, void *bv) return -1; if (as > bs) return +1; + if (a < b) + return -1; + if (a > b) + return +1; return 0; } @@ -788,6 +792,14 @@ static DWORD try_connect(Actual_Socket sock) family = AF_INET; } + /* + * Remove the socket from the tree before we overwrite its + * internal socket id, because that forms part of the tree's + * sorting criterion. We'll add it back before exiting this + * function, whether we changed anything or not. + */ + del234(sktree, sock); + s = p_socket(family, SOCK_STREAM, 0); sock->s = s; @@ -932,11 +944,15 @@ static DWORD try_connect(Actual_Socket sock) sock->writable = 1; } - add234(sktree, sock); - err = 0; ret: + + /* + * No matter what happened, put the socket back in the tree. + */ + add234(sktree, sock); + if (err) plug_log(sock->plug, 1, sock->addr, sock->port, sock->error, err); return err; From 020c481dd4e588cc8ba36f1c3c8479f1e6e3e7fd Mon Sep 17 00:00:00 2001 From: Simon Tatham Date: Wed, 28 Nov 2007 20:45:50 +0000 Subject: [PATCH 112/124] Duplicate r7795 in uxnet.c. [originally from svn r7796] [r7795 == 712b4689c8e69f936b0fa8706bee9449c2db1ca9] --- unix/uxnet.c | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/unix/uxnet.c b/unix/uxnet.c index e1dfce32..bfc4a8ed 100644 --- a/unix/uxnet.c +++ b/unix/uxnet.c @@ -97,6 +97,10 @@ static int cmpfortree(void *av, void *bv) return -1; if (as > bs) return +1; + if (a < b) + return -1; + if (a > b) + return +1; return 0; } @@ -453,6 +457,14 @@ static int try_connect(Actual_Socket sock) short localport; int fl, salen; + /* + * Remove the socket from the tree before we overwrite its + * internal socket id, because that forms part of the tree's + * sorting criterion. We'll add it back before exiting this + * function, whether we changed anything or not. + */ + del234(sktree, sock); + if (sock->s >= 0) close(sock->s); @@ -605,9 +617,14 @@ static int try_connect(Actual_Socket sock) } uxsel_tell(sock); - add234(sktree, sock); ret: + + /* + * No matter what happened, put the socket back in the tree. + */ + add234(sktree, sock); + if (err) plug_log(sock->plug, 1, sock->addr, sock->port, strerror(err), err); return err; From 1940b37ff0d65468b531fd00f5576d9fa6d54e69 Mon Sep 17 00:00:00 2001 From: Ben Harris Date: Wed, 5 Dec 2007 00:02:06 +0000 Subject: [PATCH 113/124] Add a new bug-compatibility mode that limits the window size we'll advertise so that the server can't exceed our maximum packet size. Enable it for "1.36_sshlib GlobalSCAPE" which apparently sends oversize packets otherwise. [originally from svn r7804] --- config.c | 3 +++ doc/config.but | 16 ++++++++++++++++ putty.h | 2 +- settings.c | 2 ++ ssh.c | 20 ++++++++++++++++++++ 5 files changed, 42 insertions(+), 1 deletion(-) diff --git a/config.c b/config.c index fd1379ab..bd07d951 100644 --- a/config.c +++ b/config.c @@ -2245,6 +2245,9 @@ void setup_config_box(struct controlbox *b, int midsession, ctrl_droplist(s, "Handles SSH-2 key re-exchange badly", 'k', 20, HELPCTX(ssh_bugs_rekey2), sshbug_handler, I(offsetof(Config,sshbug_rekey2))); + ctrl_droplist(s, "Ignores SSH-2 maximum packet size", 'x', 20, + HELPCTX(ssh_bugs_maxpkt2), + sshbug_handler, I(offsetof(Config,sshbug_maxpkt2))); } } } diff --git a/doc/config.but b/doc/config.but index 98b4a7e2..02ada516 100644 --- a/doc/config.but +++ b/doc/config.but @@ -2978,6 +2978,22 @@ would expect. This is an SSH-2-specific bug. +\S{config-ssh-bug-maxpkt} \q{Ignores SSH-2 \i{maximum packet size}} + +\cfg{winhelp-topic}{ssh.bugs.maxpkt2} + +When an SSH-2 channel is set up, each end announces the maximum size +of data packet that it is willing to receive for that channel. Some +servers ignore PuTTY's announcement and send packets larger than PuTTY +is willing to accept, causing it to report \q{Incoming packet was +garbled on decryption}. + +If this bug is detected, PuTTY never allows the channel's +\i{flow-control window} to grow large enough to allow the server to +send an over-sized packet. If this bug is enabled when talking to a +correct server, the session will work correctly, but download +performance will be less than it could be. + \H{config-serial} The Serial panel The \i{Serial} panel allows you to configure options that only apply diff --git a/putty.h b/putty.h index c935efa0..8f6461e6 100644 --- a/putty.h +++ b/putty.h @@ -587,7 +587,7 @@ struct config_tag { /* SSH bug compatibility modes */ int sshbug_ignore1, sshbug_plainpw1, sshbug_rsa1, sshbug_hmac2, sshbug_derivekey2, sshbug_rsapad2, - sshbug_pksessid2, sshbug_rekey2; + sshbug_pksessid2, sshbug_rekey2, sshbug_maxpkt2; /* * ssh_simple means that we promise never to open any channel other * than the main one, which means it can safely use a very large diff --git a/settings.c b/settings.c index 3846715e..ab2dc925 100644 --- a/settings.c +++ b/settings.c @@ -454,6 +454,7 @@ void save_open_settings(void *sesskey, Config *cfg) write_setting_i(sesskey, "BugRSAPad2", 2-cfg->sshbug_rsapad2); write_setting_i(sesskey, "BugPKSessID2", 2-cfg->sshbug_pksessid2); write_setting_i(sesskey, "BugRekey2", 2-cfg->sshbug_rekey2); + write_setting_i(sesskey, "BugMaxPkt2", 2-cfg->sshbug_maxpkt2); write_setting_i(sesskey, "StampUtmp", cfg->stamp_utmp); write_setting_i(sesskey, "LoginShell", cfg->login_shell); write_setting_i(sesskey, "ScrollbarOnLeft", cfg->scrollbar_on_left); @@ -788,6 +789,7 @@ void load_open_settings(void *sesskey, Config *cfg) gppi(sesskey, "BugRSAPad2", 0, &i); cfg->sshbug_rsapad2 = 2-i; gppi(sesskey, "BugPKSessID2", 0, &i); cfg->sshbug_pksessid2 = 2-i; gppi(sesskey, "BugRekey2", 0, &i); cfg->sshbug_rekey2 = 2-i; + gppi(sesskey, "BugMaxPkt2", 0, &i); cfg->sshbug_maxpkt2 = 2-i; cfg->ssh_simple = FALSE; gppi(sesskey, "StampUtmp", 1, &cfg->stamp_utmp); gppi(sesskey, "LoginShell", 1, &cfg->login_shell); diff --git a/ssh.c b/ssh.c index a4b78c4d..eec05808 100644 --- a/ssh.c +++ b/ssh.c @@ -183,6 +183,7 @@ static const char *const ssh2_disconnect_reasons[] = { #define BUG_SSH2_DERIVEKEY 32 #define BUG_SSH2_REKEY 64 #define BUG_SSH2_PK_SESSIONID 128 +#define BUG_SSH2_MAXPKT 256 /* * Codes for terminal modes. @@ -2391,6 +2392,16 @@ static void ssh_detect_bugs(Ssh ssh, char *vstring) ssh->remote_bugs |= BUG_SSH2_REKEY; logevent("We believe remote version has SSH-2 rekey bug"); } + + if (ssh->cfg.sshbug_maxpkt2 == FORCE_ON || + (ssh->cfg.sshbug_maxpkt2 == AUTO && + (wc_match("1.36_sshlib GlobalSCAPE", imp)))) { + /* + * This version ignores our makpkt and needs to be throttled. + */ + ssh->remote_bugs |= BUG_SSH2_MAXPKT; + logevent("We believe remote version ignores SSH-2 maximum packet size"); + } } /* @@ -6235,6 +6246,15 @@ static void ssh2_set_window(struct ssh_channel *c, int newwin) if (c->closes != 0) return; + /* + * If the remote end has a habit of ignoring maxpkt, limit the + * window so that it has no choice (assuming it doesn't ignore the + * window as well). + */ + if ((ssh->remote_bugs & BUG_SSH2_MAXPKT) && newwin > OUR_V2_MAXPKT) + newwin = OUR_V2_MAXPKT; + + /* * Only send a WINDOW_ADJUST if there's significantly more window * available than the other end thinks there is. This saves us From b2b89061d3d6785da25e2b740427772a1d470c45 Mon Sep 17 00:00:00 2001 From: Ben Harris Date: Wed, 5 Dec 2007 00:28:22 +0000 Subject: [PATCH 114/124] Document maxpkt bug under garbled packet error message. [originally from svn r7806] --- doc/config.but | 2 +- doc/errors.but | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/doc/config.but b/doc/config.but index 02ada516..4ed912d4 100644 --- a/doc/config.but +++ b/doc/config.but @@ -2978,7 +2978,7 @@ would expect. This is an SSH-2-specific bug. -\S{config-ssh-bug-maxpkt} \q{Ignores SSH-2 \i{maximum packet size}} +\S{config-ssh-bug-maxpkt2} \q{Ignores SSH-2 \i{maximum packet size}} \cfg{winhelp-topic}{ssh.bugs.maxpkt2} diff --git a/doc/errors.but b/doc/errors.but index 5a0d83dc..ae00a410 100644 --- a/doc/errors.but +++ b/doc/errors.but @@ -235,9 +235,10 @@ gone wrong in the encryption or decryption process. It's difficult to tell from this error message whether the problem is in the client, in the server, or in between. -If you get this error, one thing you could try would be to fiddle -with the setting of \q{Miscomputes SSH-2 encryption keys} on the Bugs -panel (see \k{config-ssh-bug-derivekey2}). +If you get this error, one thing you could try would be to fiddle with +the setting of \q{Miscomputes SSH-2 encryption keys} (see +\k{config-ssh-bug-derivekey2}) or \q{Ignores SSH-2 maximum packet +size} (see \k{config-ssh-bug-maxpkt2}) on the Bugs panel . Another known server problem which can cause this error is described in \k{faq-openssh-bad-openssl} in the FAQ. From 9024621b919aaf8afe1564a60802d9504c36f232 Mon Sep 17 00:00:00 2001 From: Simon Tatham Date: Sat, 15 Dec 2007 10:41:40 +0000 Subject: [PATCH 115/124] r7804 neglected to add a help context #define to winhelp.h. [originally from svn r7815] [r7804 == 1940b37ff0d65468b531fd00f5576d9fa6d54e69] --- windows/winhelp.h | 1 + 1 file changed, 1 insertion(+) diff --git a/windows/winhelp.h b/windows/winhelp.h index ce7881c7..2dedfc8c 100644 --- a/windows/winhelp.h +++ b/windows/winhelp.h @@ -131,6 +131,7 @@ #define WINHELP_CTX_ssh_bugs_rsapad2 "ssh.bugs.rsapad2:config-ssh-bug-sig" #define WINHELP_CTX_ssh_bugs_pksessid2 "ssh.bugs.pksessid2:config-ssh-bug-pksessid2" #define WINHELP_CTX_ssh_bugs_rekey2 "ssh.bugs.rekey2:config-ssh-bug-rekey" +#define WINHELP_CTX_ssh_bugs_maxpkt2 "ssh.bugs.maxpkt2:config-ssh-bug-maxpkt2" #define WINHELP_CTX_serial_line "serial.line:config-serial-line" #define WINHELP_CTX_serial_speed "serial.speed:config-serial-speed" #define WINHELP_CTX_serial_databits "serial.databits:config-serial-databits" From c47a0a1f4e03500bc13d0a74d7aa17526bcd21a1 Mon Sep 17 00:00:00 2001 From: Simon Tatham Date: Thu, 20 Dec 2007 11:03:45 +0000 Subject: [PATCH 116/124] Make the text about our interest in new mirror sites significantly less fluffy and welcoming. [originally from svn r7824] --- doc/faq.but | 5 +++-- doc/feedback.but | 29 +++++++++++++++++++---------- 2 files changed, 22 insertions(+), 12 deletions(-) diff --git a/doc/faq.but b/doc/faq.but index bdc1a70f..2a7509d8 100644 --- a/doc/faq.but +++ b/doc/faq.but @@ -1133,8 +1133,9 @@ link to you at all. If you have software based on PuTTY, or specifically designed to interoperate with PuTTY, or in some other way of genuine interest to PuTTY users, then we will probably be happy to add a link to you on -our Links page. And if you're running a mirror of the PuTTY web -site, we're \e{definitely} interested. +our Links page. And if you're running a particularly valuable mirror +of the PuTTY web site, we might be interested in linking to you from +our Mirrors page. \S{faq-sourceforge}{Question} Why don't you move PuTTY to SourceForge? diff --git a/doc/feedback.but b/doc/feedback.but index fe649272..04bbab98 100644 --- a/doc/feedback.but +++ b/doc/feedback.but @@ -375,18 +375,27 @@ clear that we \e{could} stop you doing this, even if we wanted to!) \H{feedback-mirrors} Mirroring the PuTTY web site -\#{This paragraph also in putty-website/mirrors.html} -Mirrors of the PuTTY web site are welcome, especially in regions not -well covered by existing mirrors. (However, if you're in a region that is -already well served by mirrors, you should consider whether yet another one -will be worth the effort.) Please don't bother asking us for permission before +\# the next two paragraphs also on the Mirrors page itself, with +\# minor context changes + +If you want to set up a mirror of the PuTTY website, go ahead and +set one up. Please don't bother asking us for permission before setting up a mirror. You already have permission. -If you mail us \e{after} you have set up the mirror and checked that -it works, and remember to let us know which country your mirror is in, -then we'll add it to the -\W{http://www.chiark.greenend.org.uk/~sgtatham/putty/mirrors.html}{Mirrors -page} on the PuTTY website. +If the mirror is in a country where we don't already have plenty of +mirrors, we may be willing to add it to the list on our +\W{http://www.chiark.greenend.org.uk/~sgtatham/putty/mirrors.html}{mirrors +page}. Read the guidelines on that page, make sure your mirror +works, and email us the information listed at the bottom of the +page. + +Note that we do not \e{promise} to list your mirror: we get a lot of +mirror notifications and yours may not happen to find its way to the +top of the list. + +Also note that we link to all our mirror sites using the +\c{rel="nofollow"} attribute. Running a PuTTY mirror is not intended +to be a cheap way to gain search rankings. If you have technical questions about the process of mirroring, then you might want to mail us before setting up the mirror (see also the From d08f057f472850e399b71087cca4f9376a421bea Mon Sep 17 00:00:00 2001 From: Jacob Nevins Date: Wed, 9 Jan 2008 19:59:16 +0000 Subject: [PATCH 117/124] Typo in winadj@ name. [originally from svn r7834] --- doc/sshnames.but | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/sshnames.but b/doc/sshnames.but index 97acc7b0..57959fb4 100644 --- a/doc/sshnames.but +++ b/doc/sshnames.but @@ -32,7 +32,7 @@ format of the request is: This name can be sent in a \cw{SSH_MSG_CHANNEL_REQUEST} message. -\dt \cw{winadj@putty.project.tartarus.org} +\dt \cw{winadj@putty.projects.tartarus.org} \dd PuTTY sends this request along with some \cw{SSH_MSG_CHANNEL_WINDOW_ADJUST} messages as part of its window-size From c639a04a1fc08a3286188a3d4216a3d82a525eeb Mon Sep 17 00:00:00 2001 From: Jacob Nevins Date: Sun, 10 Feb 2008 14:00:51 +0000 Subject: [PATCH 118/124] Update web-SVN URL for kh2reg.py. [originally from svn r7843] --- doc/faq.but | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/faq.but b/doc/faq.but index 2a7509d8..82214761 100644 --- a/doc/faq.but +++ b/doc/faq.but @@ -161,7 +161,7 @@ completely is the wrong solution and we will not do it. If you have host keys available in the common \i\c{known_hosts} format, we have a script called -\W{http://www.tartarus.org/~simon-anonsvn/viewcvs.cgi/putty/contrib/kh2reg.py?view=markup}\c{kh2reg.py} +\W{http://svn.tartarus.org/putty/contrib/kh2reg.py?view=markup}\c{kh2reg.py} to convert them to a Windows .REG file, which can be installed ahead of time by double-clicking or using \c{REGEDIT}. From 5e42fe8fc9cd6eed77681116dffeb427f5a6aca8 Mon Sep 17 00:00:00 2001 From: Simon Tatham Date: Thu, 21 Feb 2008 09:18:24 +0000 Subject: [PATCH 119/124] Aha, _that's_ why I've been periodically getting blocking-write problems using Unix PuTTY port forwarding. Sockets we create by connect() are immediately set into nonblocking mode by fcntl, but sockets we create by accept() were not. This trivial fix should help. [originally from svn r7864] --- unix/uxnet.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/unix/uxnet.c b/unix/uxnet.c index bfc4a8ed..bd40937a 100644 --- a/unix/uxnet.c +++ b/unix/uxnet.c @@ -1077,6 +1077,7 @@ static int net_select_result(int fd, int event) #endif socklen_t addrlen = sizeof(ss); int t; /* socket of connection */ + int fl; memset(&ss, 0, addrlen); t = accept(s->s, (struct sockaddr *)&ss, &addrlen); @@ -1084,6 +1085,10 @@ static int net_select_result(int fd, int event) break; } + fl = fcntl(t, F_GETFL); + if (fl != -1) + fcntl(t, F_SETFL, fl | O_NONBLOCK); + if (s->localhost_only && !sockaddr_is_loopback((struct sockaddr *)&ss)) { close(t); /* someone let nonlocal through?! */ From d6fdbfbd2f1928fd536ec2545c94fd5669d42920 Mon Sep 17 00:00:00 2001 From: Jacob Nevins Date: Sat, 23 Feb 2008 22:00:48 +0000 Subject: [PATCH 120/124] Tunnels: more explicit link from introductory to reference section. [originally from svn r7880] --- doc/using.but | 3 +++ 1 file changed, 3 insertions(+) diff --git a/doc/using.but b/doc/using.but index 74c3f793..99905c38 100644 --- a/doc/using.but +++ b/doc/using.but @@ -467,6 +467,9 @@ theory but servers will not necessarily cooperate. to obtain a fix from Microsoft in order to use addresses like \cw{127.0.0.5} - see \k{faq-alternate-localhost}.) +For more options relating to port forwarding, see +\k{config-ssh-portfwd}. + \H{using-rawprot} Making \i{raw TCP connections} A lot of \I{debugging Internet protocols}Internet protocols are From 98c0039a8315c7e1392755ca915818e8d46064ce Mon Sep 17 00:00:00 2001 From: Jacob Nevins Date: Sat, 23 Feb 2008 23:56:22 +0000 Subject: [PATCH 121/124] Attempt to clarify what the various IP version selection options do. [originally from svn r7882] --- doc/config.but | 12 ++++++++++-- doc/using.but | 3 ++- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/doc/config.but b/doc/config.but index 4ed912d4..16d15899 100644 --- a/doc/config.but +++ b/doc/config.but @@ -1700,8 +1700,13 @@ TCP keepalives are disabled by default. \cfg{winhelp-topic}{connection.ipversion} This option allows the user to select between the old and new -Internet protocols and addressing schemes (\i{IPv4} and \i{IPv6}). The -default setting is \q{Auto}, which means PuTTY will do something +Internet protocols and addressing schemes (\i{IPv4} and \i{IPv6}). +The selected protocol will be used for most outgoing network +connections (including connections to \I{proxy}proxies); however, +tunnels have their own configuration, for which see +\k{config-ssh-portfwd-address-family}. + +The default setting is \q{Auto}, which means PuTTY will do something sensible and try to guess which protocol you wanted. (If you specify a literal \i{Internet address}, it will use whichever protocol that address implies. If you provide a \i{hostname}, it will see what kinds @@ -2788,6 +2793,9 @@ incoming connections in both IPv4 and (if available) IPv6 \b for a remote-to-local port forwarding, PuTTY will choose a sensible protocol for the outgoing connection. +This overrides the general Internet protocol version preference +on the Connection panel (see \k{config-address-family}). + Note that some operating systems may listen for incoming connections in IPv4 even if you specifically asked for IPv6, because their IPv4 and IPv6 protocol stacks are linked together. Apparently \i{Linux} does diff --git a/doc/using.but b/doc/using.but index 99905c38..0c7fcf88 100644 --- a/doc/using.but +++ b/doc/using.but @@ -870,7 +870,8 @@ PuTTY configuration box (see \k{config-ssh-prot}). \i{Internet protocol version} The \c{-4} and \c{-6} options force PuTTY to use the older Internet -protocol \i{IPv4} or the newer \i{IPv6}. +protocol \i{IPv4} or the newer \i{IPv6} for most outgoing +connections. These options are equivalent to selecting your preferred Internet protocol version as \q{IPv4} or \q{IPv6} in the Connection panel of From 9503c5e5c32902235d392d599fc9570e705c9144 Mon Sep 17 00:00:00 2001 From: Jacob Nevins Date: Sun, 24 Feb 2008 00:16:29 +0000 Subject: [PATCH 122/124] It's a new year (and we've even made a code checkin). [originally from svn r7883] [this svn revision also touched putty-website] --- LICENCE | 2 +- doc/blurb.but | 2 +- doc/licence.but | 2 +- mac/mac_res.r | 4 ++-- mac/macpgen.r | 4 ++-- unix/gtkdlg.c | 4 ++-- windows/pageant.rc | 4 ++-- windows/puttygen.rc | 4 ++-- windows/version.rc2 | 2 +- windows/win_res.rc2 | 4 ++-- 10 files changed, 16 insertions(+), 16 deletions(-) diff --git a/LICENCE b/LICENCE index 1960cc2b..6f8d4b27 100644 --- a/LICENCE +++ b/LICENCE @@ -1,4 +1,4 @@ -PuTTY is copyright 1997-2007 Simon Tatham. +PuTTY is copyright 1997-2008 Simon Tatham. Portions copyright Robert de Bath, Joris van Rantwijk, Delian Delchev, Andreas Schultz, Jeroen Massar, Wez Furlong, Nicolas Barry, diff --git a/doc/blurb.but b/doc/blurb.but index c470dd55..9a8a0bdb 100644 --- a/doc/blurb.but +++ b/doc/blurb.but @@ -31,6 +31,6 @@ features not described here; and the \i\cw{pterm} and command-line Unix-specific documentation that currently exists is the \I{man pages for PuTTY tools}man pages. -\copyright This manual is copyright 2001-2007 Simon Tatham. All +\copyright This manual is copyright 2001-2008 Simon Tatham. All rights reserved. You may distribute this documentation under the MIT licence. See \k{licence} for the licence text in full. diff --git a/doc/licence.but b/doc/licence.but index 1e3b2561..6e97198d 100644 --- a/doc/licence.but +++ b/doc/licence.but @@ -2,7 +2,7 @@ \A{licence} PuTTY \ii{Licence} -PuTTY is \i{copyright} 1997-2007 Simon Tatham. +PuTTY is \i{copyright} 1997-2008 Simon Tatham. Portions copyright Robert de Bath, Joris van Rantwijk, Delian Delchev, Andreas Schultz, Jeroen Massar, Wez Furlong, Nicolas Barry, diff --git a/mac/mac_res.r b/mac/mac_res.r index 32d3960f..d73621ec 100644 --- a/mac/mac_res.r +++ b/mac/mac_res.r @@ -1221,7 +1221,7 @@ resource 'DITL' (wAbout, "about", purgeable) { StaticText { disabled, "PuTTY"}, { 42, 13, 74, 227 }, StaticText { disabled, "Some version or other\n" - "Copyright © 1997-2007 Simon Tatham"}, + "Copyright © 1997-2008 Simon Tatham"}, } }; @@ -1242,7 +1242,7 @@ type 'TEXT' { }; resource 'TEXT' (wLicence, "licence", purgeable) { - "PuTTY is copyright 1997-2007 Simon Tatham.\n" + "PuTTY is copyright 1997-2008 Simon Tatham.\n" "\n" "Portions copyright Robert de Bath, Joris van Rantwijk, Delian " "Delchev, Andreas Schultz, Jeroen Massar, Wez Furlong, Nicolas Barry, " diff --git a/mac/macpgen.r b/mac/macpgen.r index 5dafc6de..9919c62a 100644 --- a/mac/macpgen.r +++ b/mac/macpgen.r @@ -422,7 +422,7 @@ resource 'DITL' (wAbout, "about", purgeable) { StaticText { disabled, "PuTTYgen"}, { 42, 13, 74, 227 }, StaticText { disabled, "Some version or other\n" - "Copyright © 1997-2007 Simon Tatham"}, + "Copyright © 1997-2008 Simon Tatham"}, } }; @@ -443,7 +443,7 @@ type 'TEXT' { }; resource 'TEXT' (wLicence, "licence", purgeable) { - "Copyright 1997-2007 Simon Tatham.\n" + "Copyright 1997-2008 Simon Tatham.\n" "\n" "Portions copyright Robert de Bath, Joris van Rantwijk, Delian " "Delchev, Andreas Schultz, Jeroen Massar, Wez Furlong, Nicolas Barry, " diff --git a/unix/gtkdlg.c b/unix/gtkdlg.c index 10138e27..bdfe3e55 100644 --- a/unix/gtkdlg.c +++ b/unix/gtkdlg.c @@ -2544,7 +2544,7 @@ static void licence_clicked(GtkButton *button, gpointer data) char *title; char *licence = - "Copyright 1997-2007 Simon Tatham.\n\n" + "Copyright 1997-2008 Simon Tatham.\n\n" "Portions copyright Robert de Bath, Joris van Rantwijk, Delian " "Delchev, Andreas Schultz, Jeroen Massar, Wez Furlong, Nicolas " @@ -2625,7 +2625,7 @@ void about_box(void *window) w, FALSE, FALSE, 5); gtk_widget_show(w); - w = gtk_label_new("Copyright 1997-2007 Simon Tatham. All rights reserved"); + w = gtk_label_new("Copyright 1997-2008 Simon Tatham. All rights reserved"); gtk_box_pack_start(GTK_BOX(GTK_DIALOG(aboutbox)->vbox), w, FALSE, FALSE, 5); gtk_widget_show(w); diff --git a/windows/pageant.rc b/windows/pageant.rc index c2b5b7cd..84452587 100644 --- a/windows/pageant.rc +++ b/windows/pageant.rc @@ -45,7 +45,7 @@ BEGIN PUSHBUTTON "View &Licence", 101, 6, 52, 70, 14 CTEXT "Pageant", 102, 10, 6, 120, 8 CTEXT "", 100, 10, 16, 120, 16 - CTEXT "\251 1997-2007 Simon Tatham. All rights reserved.", + CTEXT "\251 1997-2008 Simon Tatham. All rights reserved.", 103, 10, 34, 120, 16 END @@ -57,7 +57,7 @@ FONT 8, "MS Shell Dlg" BEGIN DEFPUSHBUTTON "OK", IDOK, 98, 243, 44, 14 - LTEXT "Copyright \251 1997-2007 Simon Tatham", 1000, 10, 10, 206, 8 + LTEXT "Copyright \251 1997-2008 Simon Tatham", 1000, 10, 10, 206, 8 LTEXT "Portions copyright Robert de Bath, Joris van Rantwijk, Delian", 1001, 10, 26, 206, 8 LTEXT "Delchev, Andreas Schultz, Jeroen Massar, Wez Furlong, Nicolas", 1002, 10, 34, 206, 8 diff --git a/windows/puttygen.rc b/windows/puttygen.rc index df755fee..d353d244 100644 --- a/windows/puttygen.rc +++ b/windows/puttygen.rc @@ -38,7 +38,7 @@ BEGIN PUSHBUTTON "View &Licence", 101, 6, 52, 70, 14 CTEXT "PuTTYgen", 102, 10, 6, 120, 8 CTEXT "", 100, 10, 16, 120, 16 - CTEXT "\251 1997-2007 Simon Tatham. All rights reserved.", + CTEXT "\251 1997-2008 Simon Tatham. All rights reserved.", 103, 10, 34, 120, 16 END @@ -50,7 +50,7 @@ FONT 8, "MS Shell Dlg" BEGIN DEFPUSHBUTTON "OK", IDOK, 98, 243, 44, 14 - LTEXT "Copyright \251 1997-2007 Simon Tatham", 1000, 10, 10, 206, 8 + LTEXT "Copyright \251 1997-2008 Simon Tatham", 1000, 10, 10, 206, 8 LTEXT "Portions copyright Robert de Bath, Joris van Rantwijk, Delian", 1001, 10, 26, 206, 8 LTEXT "Delchev, Andreas Schultz, Jeroen Massar, Wez Furlong, Nicolas", 1002, 10, 34, 206, 8 diff --git a/windows/version.rc2 b/windows/version.rc2 index 3c7d1b57..405fa4e6 100644 --- a/windows/version.rc2 +++ b/windows/version.rc2 @@ -115,7 +115,7 @@ BEGIN VALUE "OriginalFilename", APPNAME VALUE "FileVersion", VERSION_TEXT VALUE "ProductVersion", VERSION_TEXT - VALUE "LegalCopyright", "Copyright \251 1997-2007 Simon Tatham." + VALUE "LegalCopyright", "Copyright \251 1997-2008 Simon Tatham." #if (!defined SNAPSHOT) && (!defined RELEASE) /* Only if VS_FF_PRIVATEBUILD. */ VALUE "PrivateBuild", VERSION_TEXT /* NBI */ diff --git a/windows/win_res.rc2 b/windows/win_res.rc2 index be9b95e7..04660c30 100644 --- a/windows/win_res.rc2 +++ b/windows/win_res.rc2 @@ -26,7 +26,7 @@ BEGIN PUSHBUTTON "Visit &Web Site", IDA_WEB, 84, 52, 70, 14 CTEXT "PuTTY", IDA_TEXT1, 10, 6, 194, 8 CTEXT "", IDA_VERSION, 10, 16, 194, 16 - CTEXT "\251 1997-2007 Simon Tatham. All rights reserved.", + CTEXT "\251 1997-2008 Simon Tatham. All rights reserved.", IDA_TEXT2, 10, 34, 194, 16 END @@ -58,7 +58,7 @@ FONT 8, "MS Shell Dlg" BEGIN DEFPUSHBUTTON "OK", IDOK, 98, 243, 44, 14 - LTEXT "Copyright \251 1997-2007 Simon Tatham", 1000, 10, 10, 206, 8 + LTEXT "Copyright \251 1997-2008 Simon Tatham", 1000, 10, 10, 206, 8 LTEXT "Portions copyright Robert de Bath, Joris van Rantwijk, Delian", 1001, 10, 26, 206, 8 LTEXT "Delchev, Andreas Schultz, Jeroen Massar, Wez Furlong, Nicolas", 1002, 10, 34, 206, 8 From 4aa9b6a0da9a9e29d9a9d515af06502148e53823 Mon Sep 17 00:00:00 2001 From: Simon Tatham Date: Fri, 7 Mar 2008 18:30:37 +0000 Subject: [PATCH 123/124] Fix a cursor positioning infelicity. The scenario: I start a small, say 80x24, pterm. I do some work in it, generating plenty of scrollback, and eventually I `less' a file. `less' switches to the alt screen. Then I want more vertical space to look at the file, so I enlarge the window to more like 80x60. When I quit `less' and switch back to the primary screen, some scrollback has been pulled down into the screen, as expected - but the saved _cursor position_ is still at line 24, not at the bottom of the new terminal where the prompt it goes with has moved to. Solution: term_size() should adjust the alt-screen saved cursor positions as well as the normal cursor position. (Curiously, the problem doesn't happen on my home Debian box, even without this fix. It happens on my RH9 box at work, though.) [originally from svn r7911] --- terminal.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/terminal.c b/terminal.c index 079a738a..3a2ab620 100644 --- a/terminal.c +++ b/terminal.c @@ -1614,6 +1614,8 @@ void term_size(Terminal *term, int newrows, int newcols, int newsavelines) addpos234(term->screen, line, 0); term->curs.y += 1; term->savecurs.y += 1; + term->alt_y += 1; + term->alt_savecurs.y += 1; } else { /* Add a new blank line at the bottom of the screen. */ line = newline(term, newcols, FALSE); @@ -1634,6 +1636,8 @@ void term_size(Terminal *term, int newrows, int newcols, int newsavelines) term->tempsblines += 1; term->curs.y -= 1; term->savecurs.y -= 1; + term->alt_y -= 1; + term->alt_savecurs.y -= 1; } term->rows -= 1; } @@ -1693,12 +1697,26 @@ void term_size(Terminal *term, int newrows, int newcols, int newsavelines) term->savecurs.y = 0; if (term->savecurs.y >= newrows) term->savecurs.y = newrows - 1; + if (term->savecurs.x >= newcols) + term->savecurs.x = newcols - 1; + if (term->alt_savecurs.y < 0) + term->alt_savecurs.y = 0; + if (term->alt_savecurs.y >= newrows) + term->alt_savecurs.y = newrows - 1; + if (term->alt_savecurs.x >= newcols) + term->alt_savecurs.x = newcols - 1; if (term->curs.y < 0) term->curs.y = 0; if (term->curs.y >= newrows) term->curs.y = newrows - 1; if (term->curs.x >= newcols) term->curs.x = newcols - 1; + if (term->alt_y < 0) + term->alt_y = 0; + if (term->alt_y >= newrows) + term->alt_y = newrows - 1; + if (term->alt_x >= newcols) + term->alt_x = newcols - 1; term->alt_x = term->alt_y = 0; term->wrapnext = term->alt_wnext = FALSE; From d7eda6d99cb6b7c2e09489dcf13b23c4cfcc61a2 Mon Sep 17 00:00:00 2001 From: Simon Tatham Date: Sun, 9 Mar 2008 15:32:20 +0000 Subject: [PATCH 124/124] Under OS X Leopard, we seem not to consistently get the Tab key translated for us. Be prepared to do it manually as a fallback. [originally from svn r7913] --- unix/gtkwin.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/unix/gtkwin.c b/unix/gtkwin.c index a377ad65..d31037b6 100644 --- a/unix/gtkwin.c +++ b/unix/gtkwin.c @@ -723,6 +723,13 @@ gint key_event(GtkWidget *widget, GdkEventKey *event, gpointer data) end = 1 + sprintf(output+1, "\033[Z"); use_ucsoutput = FALSE; } + /* And normal Tab is Tab, if the keymap hasn't already told us. + * (Curiously, at least one version of the MacOS 10.5 X server + * doesn't translate Tab for us. */ + if (event->keyval == GDK_Tab && end <= 1) { + output[1] = '\t'; + end = 2; + } /* * NetHack keypad mode.