Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Difference From 62dff762278fcc2e To e00724f199c90822
2014-01-09
| ||
04:22 | Added putty-cac current state Leaf check-in: 62dff76227 user: rkeene tags: putty-cac | |
2013-11-22
| ||
13:41 | Pass the right number of entries to SetEntriesInAcl! check-in: 074d8b4052 user: simon tags: trunk | |
13:41 | Include the numeric error code in win_strerror's output. This will be useful if someone gets a mysterious Windows error on a system configured into a language we don't speak - if they cut and paste the error message to send to us, then we won't have to try to translate it. check-in: e00724f199 user: simon tags: trunk | |
2013-11-18
| ||
16:34 | Fix up the Windows help context stuff for the new connection sharing controls. check-in: 5d4d60ef11 user: jacob tags: trunk | |
2011-12-10
| ||
12:08 | Tag 0.62 release. Leaf check-in: 9c44b3c894 user: simon tags: putty-0.62, putty-branch-0.61 | |
Changes to .fossil-settings/ignore-glob.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | *.DSA *.DSA/* *.GID *.GID/* *.RES *.RES/* *.RSA *.RSA/* *.cnt *.cnt/* *.dsp *.dsp/* *.dsw *.dsw/* *.exe | > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | *.DSA *.DSA/* *.GID *.GID/* *.RES *.RES/* *.RSA *.RSA/* *.a *.a/* *.cnt *.cnt/* *.dsp *.dsp/* *.dsw *.dsw/* *.exe |
︙ | ︙ | |||
40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 | *.rsp/* *.td2 *.td2/* *.tds *.tds/* .bmake .bmake/* .svn/*.DSA .svn/*.DSA/* .svn/*.GID .svn/*.GID/* .svn/*.RES .svn/*.RES/* .svn/*.RSA .svn/*.RSA/* .svn/*.cnt .svn/*.cnt/* .svn/*.dsp .svn/*.dsp/* .svn/*.dsw .svn/*.dsw/* .svn/*.exe | > > > > | 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 | *.rsp/* *.td2 *.td2/* *.tds *.tds/* .bmake .bmake/* .deps .deps/* .svn/*.DSA .svn/*.DSA/* .svn/*.GID .svn/*.GID/* .svn/*.RES .svn/*.RES/* .svn/*.RSA .svn/*.RSA/* .svn/*.a .svn/*.a/* .svn/*.cnt .svn/*.cnt/* .svn/*.dsp .svn/*.dsp/* .svn/*.dsw .svn/*.dsw/* .svn/*.exe |
︙ | ︙ | |||
86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 | .svn/*.rsp/* .svn/*.td2 .svn/*.td2/* .svn/*.tds .svn/*.tds/* .svn/.bmake .svn/.bmake/* .svn/MSVC .svn/MSVC/* .svn/Makefile.bor .svn/Makefile.bor/* .svn/Makefile.cyg .svn/Makefile.cyg/* .svn/Makefile.lcc .svn/Makefile.lcc/* .svn/Makefile.vc .svn/Makefile.vc/* .svn/Output .svn/Output/* .svn/build.log .svn/build.log/* .svn/build.out .svn/build.out/* .svn/local .svn/local/* .svn/pageant .svn/pageant/* .svn/plink .svn/plink/* .svn/pscp .svn/pscp/* .svn/psftp .svn/psftp/* .svn/putty .svn/putty/* .svn/puttygen .svn/puttygen/* .svn/puttytel .svn/puttytel/* MSVC MSVC/* Makefile.bor Makefile.bor/* Makefile.cyg Makefile.cyg/* Makefile.lcc Makefile.lcc/* Makefile.vc Makefile.vc/* Output Output/* build.log build.log/* build.out build.out/* charset/sbcsdat.c charset/sbcsdat.c/* contrib/cygtermd/cygtermd.exe contrib/cygtermd/cygtermd.exe/* doc/*.1 doc/*.1/* doc/*.GID doc/*.GID/* doc/*.chm | > > > > > > > > > > > > > > > > > > | 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 | .svn/*.rsp/* .svn/*.td2 .svn/*.td2/* .svn/*.tds .svn/*.tds/* .svn/.bmake .svn/.bmake/* .svn/.deps .svn/.deps/* .svn/MSVC .svn/MSVC/* .svn/Makefile .svn/Makefile.bor .svn/Makefile.bor/* .svn/Makefile.cyg .svn/Makefile.cyg/* .svn/Makefile.lcc .svn/Makefile.lcc/* .svn/Makefile.vc .svn/Makefile.vc/* .svn/Makefile/* .svn/Output .svn/Output/* .svn/build.log .svn/build.log/* .svn/build.out .svn/build.out/* .svn/config.status .svn/config.status/* .svn/empty.h .svn/empty.h/* .svn/local .svn/local/* .svn/pageant .svn/pageant/* .svn/plink .svn/plink/* .svn/pscp .svn/pscp/* .svn/psftp .svn/psftp/* .svn/pterm .svn/pterm/* .svn/putty .svn/putty/* .svn/puttygen .svn/puttygen/* .svn/puttytel .svn/puttytel/* .svn/stamp-h1 .svn/stamp-h1/* .svn/uxconfig.h .svn/uxconfig.h/* MSVC MSVC/* Makefile Makefile.bor Makefile.bor/* Makefile.cyg Makefile.cyg/* Makefile.lcc Makefile.lcc/* Makefile.vc Makefile.vc/* Makefile/* Output Output/* build.log build.log/* build.out build.out/* charset/sbcsdat.c charset/sbcsdat.c/* config.status config.status/* contrib/cygtermd/cygtermd.exe contrib/cygtermd/cygtermd.exe/* doc/*.1 doc/*.1/* doc/*.GID doc/*.GID/* doc/*.chm |
︙ | ︙ | |||
166 167 168 169 170 171 172 173 174 175 176 177 178 179 | doc/*.info/* doc/*.log doc/*.log/* doc/*.txt doc/*.txt/* doc/vstr.but doc/vstr.but/* icons/*.c icons/*.c/* icons/*.ico icons/*.ico/* icons/*.png icons/*.png/* icons/*.xpm | > > | 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 | doc/*.info/* doc/*.log doc/*.log/* doc/*.txt doc/*.txt/* doc/vstr.but doc/vstr.but/* empty.h empty.h/* icons/*.c icons/*.c/* icons/*.ico icons/*.ico/* icons/*.png icons/*.png/* icons/*.xpm |
︙ | ︙ | |||
196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 | pageant/* plink plink/* pscp pscp/* psftp psftp/* putty putty/* puttygen puttygen/* puttytel puttytel/* testdata/bignum.txt testdata/bignum.txt/* unix/*.log unix/*.log/* unix/Makefile unix/Makefile.gtk unix/Makefile.gtk/* unix/Makefile.in unix/Makefile.in/* unix/Makefile.local unix/Makefile.local/* unix/Makefile.ux unix/Makefile.ux/* unix/Makefile/* unix/aclocal.m4 unix/aclocal.m4/* unix/autom4te.cache unix/autom4te.cache/* unix/config.status unix/config.status/* unix/configure unix/configure/* unix/install-sh unix/install-sh/* unix/local unix/local/* unix/plink unix/plink/* unix/pscp unix/pscp/* unix/psftp unix/psftp/* unix/pterm unix/pterm/* unix/putty unix/putty/* unix/puttygen unix/puttygen/* unix/puttytel unix/puttytel/* unix/uxconfig.h unix/uxconfig.h/* unix/uxconfig.in unix/uxconfig.in/* windows/*.DSA windows/*.DSA/* windows/*.GID windows/*.GID/* windows/*.RES windows/*.RES/* windows/*.RSA | > > > > > > > > > > > > > > > > > > > > | 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 | pageant/* plink plink/* pscp pscp/* psftp psftp/* pterm pterm/* putty putty/* puttygen puttygen/* puttytel puttytel/* stamp-h1 stamp-h1/* testdata/bignum.txt testdata/bignum.txt/* unix/*.log unix/*.log/* unix/.deps unix/.deps/* unix/Makefile unix/Makefile.am unix/Makefile.am/* unix/Makefile.gtk unix/Makefile.gtk/* unix/Makefile.in unix/Makefile.in/* unix/Makefile.local unix/Makefile.local/* unix/Makefile.ux unix/Makefile.ux/* unix/Makefile/* unix/aclocal.m4 unix/aclocal.m4/* unix/autom4te.cache unix/autom4te.cache/* unix/compile unix/compile/* unix/config.status unix/config.status/* unix/configure unix/configure/* unix/depcomp unix/depcomp/* unix/empty.h unix/empty.h/* unix/install-sh unix/install-sh/* unix/local unix/local/* unix/missing unix/missing/* unix/plink unix/plink/* unix/pscp unix/pscp/* unix/psftp unix/psftp/* unix/pterm unix/pterm/* unix/putty unix/putty/* unix/puttygen unix/puttygen/* unix/puttytel unix/puttytel/* unix/stamp-h1 unix/stamp-h1/* unix/uxconfig.h unix/uxconfig.h/* unix/uxconfig.in unix/uxconfig.in/* uxconfig.h uxconfig.h/* windows/*.DSA windows/*.DSA/* windows/*.GID windows/*.GID/* windows/*.RES windows/*.RES/* windows/*.RSA |
︙ | ︙ |
Added Buildscr.cv.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | # -*- sh -*- # Build script to scan PuTTY with the downloadable Coverity scanner # and generate a tar file to upload to their open-source scanning # service. module putty # Preparations. in putty do ./mkfiles.pl in putty do ./mkauto.sh in putty/doc do make # Scan the Unix build, on a 64-bit system to differentiate as much as # possible from the other scan of the cross-platform files. delegate covscan64 in putty do ./configure in putty do cov-build --dir cov-int make in putty do tar czvf cov-int.tar.gz cov-int return putty/cov-int.tar.gz enddelegate # Scan the Windows build, by means of building with Winelib (since as # of 2013-07-22, the Coverity Scan website doesn't offer a 32-bit # Windows scanner for download). delegate covscan32wine in putty do tar xzvf cov-int.tar.gz in putty/windows do cov-build --dir ../cov-int make -f Makefile.cyg CC=winegcc RC=wrc in putty do tar czvf cov-int.tar.gz cov-int return putty/cov-int.tar.gz enddelegate # Provide the revision number as one of the build outputs, to make it # easy to construct a curl upload command which will annotate it # appropriately when uploaded. in putty do echo $(revision) > revision.txt deliver putty/revision.txt $@ deliver putty/cov-int.tar.gz $@ |
Changes to CHECKLST.txt.
︙ | ︙ | |||
173 174 175 176 177 178 179 | is available. - After running webupdate, run update-rsync on chiark and verify that the rsync mirror package (~/ftp/putty-website-mirror) contains a subdirectory for the new version and mentions it in its .htaccess. - Announce the release! | > | > | > | < > | 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 | is available. - After running webupdate, run update-rsync on chiark and verify that the rsync mirror package (~/ftp/putty-website-mirror) contains a subdirectory for the new version and mentions it in its .htaccess. - Announce the release! + Construct a release announcement email whose message body is the announcement written above, and which includes the following headers: * Reply-To: <putty@projects.tartarus.org> * Subject: PuTTY X.YZ is released + Mail that release announcement to <putty-announce@lists.tartarus.org>. + Post it to comp.security.ssh. + Mention it in <TDHTT> on mono. - Relax (slightly). After the release ----------------- |
︙ | ︙ |
Changes to LATEST.VER.
|
| | | 1 | 0.63 |
Changes to LICENCE.
|
| < < < < < < < < | < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 | PuTTY is copyright 1997-2013 Simon Tatham. Portions copyright Robert de Bath, Joris van Rantwijk, Delian Delchev, Andreas Schultz, Jeroen Massar, Wez Furlong, Nicolas Barry, Justin Bradford, Ben Harris, Malcolm Smith, Ahmad Khalifa, Markus Kuhn, Colin Watson, and CORE SDI S.A. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: |
︙ | ︙ |
Changes to README.
︙ | ︙ | |||
36 37 38 39 40 41 42 | MSVC/putty/putty.dsp builds PuTTY itself, MSVC/plink/plink.dsp builds Plink, and so on. - windows/Makefile.bor is for the Borland C compiler. Type `make -f Makefile.bor' while in the `windows' subdirectory to build all the PuTTY binaries. | | | 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | MSVC/putty/putty.dsp builds PuTTY itself, MSVC/plink/plink.dsp builds Plink, and so on. - windows/Makefile.bor is for the Borland C compiler. Type `make -f Makefile.bor' while in the `windows' subdirectory to build all the PuTTY binaries. - windows/Makefile.cyg is for Cygwin / MinGW installations. Type `make -f Makefile.cyg' while in the `windows' subdirectory to build all the PuTTY binaries. You'll probably need quite a recent version of the w32api package. Note that by default the multiple monitor and HTML Help support are excluded from the Cygwin build, since at the time of writing Cygwin doesn't include the necessary headers. |
︙ | ︙ | |||
61 62 63 64 65 66 67 | toolchains fairly quickly. Please report any problems with the other toolchains mentioned above. For building on Unix: - unix/configure is for Unix and GTK. If you don't have GTK, you should still be able to build the command-line utilities (PSCP, | | | > > > > | > > > > > > > > > > > > > > > | | > | > > > > | > > | | | > > | 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 | toolchains fairly quickly. Please report any problems with the other toolchains mentioned above. For building on Unix: - unix/configure is for Unix and GTK. If you don't have GTK, you should still be able to build the command-line utilities (PSCP, PSFTP, Plink, PuTTYgen) using this script. To use it, change into the `unix' subdirectory, run `./configure' and then `make'. Or you can do the same in the top-level directory (we provide a little wrapper that invokes configure one level down), which is more like a normal Unix source archive but doesn't do so well at keeping the per-platform stuff in each platform's subdirectory; it's up to you. Note that Unix PuTTY has mostly only been tested on Linux so far; portability problems such as BSD-style ptys or different header file requirements are expected. - unix/Makefile.gtk and unix/Makefile.ux are for non-autoconfigured builds. These makefiles expect you to change into the `unix' subdirectory, then run `make -f Makefile.gtk' or `make -f Makefile.ux' respectively. Makefile.gtk builds all the programs but relies on Gtk, whereas Makefile.ux builds only the command-line utilities and has no Gtk dependence. - For the graphical utilities, Gtk+-1.2 and Gtk+-2.0 should both be supported. If you have both installed, you can manually specify which one you want by giving the option '--with-gtk=1' or '--with-gtk=2' to the configure script. (2 is the default, of course.) In the absence of either, the configure script will automatically construct a Makefile which builds only the command-line utilities; you can manually create this condition by giving configure the option '--without-gtk'. - pterm would like to be setuid or setgid, as appropriate, to permit it to write records of user logins to /var/run/utmp and /var/log/wtmp. (Of course it will not use this privilege for anything else, and in particular it will drop all privileges before starting up complex subsystems like GTK.) By default the makefile will not attempt to add privileges to the pterm executable at 'make install' time, but you can ask it to do so by running configure with the option '--enable-setuid=USER' or '--enable-setgid=GROUP'. - The Unix Makefiles have an `install' target. Note that by default it tries to install `man' pages; if you have fetched the source via Subversion then you will need to have built these using Halibut first - see below. - It's also possible to build the Windows version of PuTTY to run on Unix by using Winelib. To do this, change to the `windows' directory and run `make -f Makefile.cyg CC=winegcc RC=wrc'. All of the Makefiles are generated automatically from the file `Recipe' by the Perl script `mkfiles.pl' (except for the Unix one, which is generated by the `configure' script; mkfiles.pl only generates the input to automake). Additions and corrections to Recipe, mkfiles.pl and/or configure.ac are much more useful than additions and corrections to the actual Makefiles, Makefile.am or Makefile.in. The Unix `configure' script and its various requirements are generated by the shell script `mkauto.sh', which requires GNU Autoconf, GNU Automake, and Gtk; if you've got the source from Subversion rather than using one of our source snapshots, you'll need to run this yourself. The input file to Automake is generated by mkfiles.pl along with all the rest of the makefiles, so you will need to run mkfiles.pl and then mkauto.sh. Documentation (in various formats including Windows Help and Unix `man' pages) is built from the Halibut (`.but') files in the `doc' subdirectory using `doc/Makefile'. If you aren't using one of our source snapshots, you'll need to do this yourself. Halibut can be found at <http://www.chiark.greenend.org.uk/~sgtatham/halibut/>. |
︙ | ︙ |
Changes to Recipe.
︙ | ︙ | |||
15 16 17 18 19 20 21 | !makefile vc windows/Makefile.vc !makefile vcproj windows/MSVC !makefile cygwin windows/Makefile.cyg !makefile borland windows/Makefile.bor !makefile lcc windows/Makefile.lcc !makefile gtk unix/Makefile.gtk !makefile unix unix/Makefile.ux | | | 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | !makefile vc windows/Makefile.vc !makefile vcproj windows/MSVC !makefile cygwin windows/Makefile.cyg !makefile borland windows/Makefile.bor !makefile lcc windows/Makefile.lcc !makefile gtk unix/Makefile.gtk !makefile unix unix/Makefile.ux !makefile am unix/Makefile.am !makefile osx macosx/Makefile !makefile devcppproj windows/DEVCPP # Source directories. !srcdir charset/ !srcdir windows/ !srcdir unix/ !srcdir macosx/ |
︙ | ︙ | |||
109 110 111 112 113 114 115 116 117 118 119 120 121 122 | # # - RCFL=/DASCIICTLS (Windows only) # Uses ASCII rather than Unicode to specify the tab control in # the resource file. Probably most useful when compiling with # Cygnus/mingw32, whose resource compiler may have less of a # problem with it. # # - XFLAGS=/DTELNET_DEFAULT # Causes PuTTY to default to the Telnet protocol (in the absence # of Default Settings and so on to the contrary). Normally PuTTY # will default to SSH. # # - XFLAGS=/DDEBUG # Causes PuTTY to enable internal debugging. | > > > > > | 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 | # # - RCFL=/DASCIICTLS (Windows only) # Uses ASCII rather than Unicode to specify the tab control in # the resource file. Probably most useful when compiling with # Cygnus/mingw32, whose resource compiler may have less of a # problem with it. # # - COMPAT=/DNO_SECUREZEROMEMORY (Windows only) # Disables PuTTY's use of SecureZeroMemory(), which is missing # from some environments' header files. This is enabled by # default in the Cygwin Makefile. # # - XFLAGS=/DTELNET_DEFAULT # Causes PuTTY to default to the Telnet protocol (in the absence # of Default Settings and so on to the contrary). Normally PuTTY # will default to SSH. # # - XFLAGS=/DDEBUG # Causes PuTTY to enable internal debugging. |
︙ | ︙ | |||
165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 | if test -z "$(VER)" && (cd ..; md5sum -c manifest); then \ $(CC) $(COMPAT) $(XFLAGS) $(CFLAGS) `cat ../version.def` -c ../version.c; \ else \ $(CC) $(COMPAT) $(XFLAGS) $(CFLAGS) $(VER) -c ../version.c; \ fi !end !specialobj gtk version # Add VER to Windows resource targets, and force them to be rebuilt every # time, on the assumption that they will contain version information. !begin vc vars CFLAGS = $(CFLAGS) /DHAS_GSSAPI /DSECURITY_WIN32 RCFLAGS = $(RCFLAGS) $(VER) !end !begin cygwin vars # XXX GNU-ism, but it's probably all right for a Cygwin/MinGW Makefile. RCFLAGS += $(patsubst -D%,--define %,$(VER)) !end !begin borland vars # Borland doesn't support +=. This probably shouldn't work, but seems to. RCFLAGS = $(RCFLAGS) $(VER) !end | > > > > > > > > > > > > > > > > | 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 | if test -z "$(VER)" && (cd ..; md5sum -c manifest); then \ $(CC) $(COMPAT) $(XFLAGS) $(CFLAGS) `cat ../version.def` -c ../version.c; \ else \ $(CC) $(COMPAT) $(XFLAGS) $(CFLAGS) $(VER) -c ../version.c; \ fi !end !specialobj gtk version # In the automake build, we have to do the whole job by supplying # extra CFLAGS, so we have to put the if statement inside one big # backtick expression. We also force rebuilding via a -D option that # makes version.o include empty.h, which we construct ourselves and # touch whenever any source file is updated. !cflags am version $(VER) -DINCLUDE_EMPTY_H `if test -z "$(VER)" && (cd $(srcdir)/..; md5sum -c manifest >/dev/null 2>&1); then cat $(srcdir)/../version.def; else echo "$(VER)"; fi` !begin am BUILT_SOURCES = empty.h empty.h: $(allsources) echo '/* Empty file touched by automake makefile to force rebuild of version.o */' >$@ !end !begin >empty.h /* Empty file touched by automake makefile to force rebuild of version.o */ !end # Add VER to Windows resource targets, and force them to be rebuilt every # time, on the assumption that they will contain version information. !begin vc vars CFLAGS = $(CFLAGS) /DHAS_GSSAPI /DSECURITY_WIN32 RCFLAGS = $(RCFLAGS) $(VER) !end !begin cygwin vars CFLAGS += -DSECURITY_WIN32 # XXX GNU-ism, but it's probably all right for a Cygwin/MinGW Makefile. RCFLAGS += $(patsubst -D%,--define %,$(VER)) !end !begin borland vars # Borland doesn't support +=. This probably shouldn't work, but seems to. RCFLAGS = $(RCFLAGS) $(VER) !end |
︙ | ︙ | |||
223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 | install-strip: $(MAKE) install INSTALL_PROGRAM="$(INSTALL_PROGRAM) -s" !end !begin osx vars CFLAGS += -DMACOSX !end # Random symbols. !begin cygwin vars # _WIN32_IE is required to expose identifiers that only make sense on # systems with IE5+ installed, such as some arguments to SHGetFolderPath(). # WINVER etc perform a similar function for FlashWindowEx(). CFLAGS += -D_WIN32_IE=0x0500 CFLAGS += -DWINVER=0x0500 -D_WIN32_WINDOWS=0x0410 -D_WIN32_WINNT=0x0500 !end # ------------------------------------------------------------ # Definitions of object groups. A group name, followed by an =, # followed by any number of objects or other already-defined group # names. A line beginning `+' is assumed to continue the previous # line. # Terminal emulator and its (platform-independent) dependencies. TERMINAL = terminal wcwidth ldiscucs logging tree234 minibidi | > > > > > > > > > > > > > > > | | | | > | | | | 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 | install-strip: $(MAKE) install INSTALL_PROGRAM="$(INSTALL_PROGRAM) -s" !end !begin osx vars CFLAGS += -DMACOSX !end # List the man pages for the automake makefile. !begin am man1_MANS = ../doc/plink.1 ../doc/pscp.1 ../doc/psftp.1 ../doc/pterm.1 \ ../doc/putty.1 ../doc/puttygen.1 ../doc/puttytel.1 !end # In automake, chgrp/chmod pterm after installation, if configured to. !begin am if HAVE_SETID_CMD install-exec-local: @SETID_CMD@ $(bindir)/pterm chmod @SETID_MODE@ $(bindir)/pterm endif !end # Random symbols. !begin cygwin vars # _WIN32_IE is required to expose identifiers that only make sense on # systems with IE5+ installed, such as some arguments to SHGetFolderPath(). # WINVER etc perform a similar function for FlashWindowEx(). CFLAGS += -D_WIN32_IE=0x0500 CFLAGS += -DWINVER=0x0500 -D_WIN32_WINDOWS=0x0410 -D_WIN32_WINNT=0x0500 !end # ------------------------------------------------------------ # Definitions of object groups. A group name, followed by an =, # followed by any number of objects or other already-defined group # names. A line beginning `+' is assumed to continue the previous # line. # Terminal emulator and its (platform-independent) dependencies. TERMINAL = terminal wcwidth ldiscucs logging tree234 minibidi + config dialog conf # GUI front end and terminal emulator (putty, puttytel). GUITERM = TERMINAL window windlg winctrls sizetip winucs winprint + winutils wincfg sercfg winhelp winjump # Same thing on Unix. UXTERM = TERMINAL uxcfg sercfg uxucs uxprint timing callback GTKTERM = UXTERM gtkwin gtkcfg gtkdlg gtkfont gtkcols xkeysym OSXTERM = UXTERM osxwin osxdlg osxctrls # Non-SSH back ends (putty, puttytel, plink). NONSSH = telnet raw rlogin ldisc pinger # SSH back end (putty, plink, pscp, psftp). SSH = ssh sshcrc sshdes sshmd5 sshrsa sshrand sshsha sshblowf + sshdh sshcrcda sshpubk sshzlib sshdss x11fwd portfwd + sshaes sshsh256 sshsh512 sshbn wildcard pinger ssharcf + sshgssc pgssapi sshshare WINSSH = SSH winnoise winsecur winpgntc wingss winshare winnps winnpc + winhsock errsock UXSSH = SSH uxnoise uxagentc uxgss uxshare # SFTP implementation (pscp, psftp). SFTP = sftp int64 logging # Miscellaneous objects appearing in all the network utilities (not # Pageant or PuTTYgen). MISC = timing callback misc version settings tree234 proxy conf WINMISC = MISC winstore winnet winhandl cmdline windefs winmisc winproxy + wintime winhsock errsock UXMISC = MISC uxstore uxsel uxnet cmdline uxmisc uxproxy time OSXMISC = MISC uxstore uxsel osxsel uxnet uxmisc uxproxy time # Character set library, for use in pterm. CHARSET = sbcsdat slookup sbcs utf8 toucs fromucs xenc mimeenc macenc localenc # Standard libraries. |
︙ | ︙ | |||
303 304 305 306 307 308 309 | # colon, followed by a list of objects. Also in the list may be the # keywords [G] for Windows GUI app, [C] for Console app, [X] for # X/GTK Unix app, [U] for command-line Unix app. putty : [G] GUITERM NONSSH WINSSH W_BE_ALL WINMISC winx11 putty.res LIBS puttytel : [G] GUITERM NONSSH W_BE_NOSSH WINMISC puttytel.res nogss LIBS plink : [C] winplink wincons NONSSH WINSSH W_BE_ALL logging WINMISC | | | | | | | | 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 | # colon, followed by a list of objects. Also in the list may be the # keywords [G] for Windows GUI app, [C] for Console app, [X] for # X/GTK Unix app, [U] for command-line Unix app. putty : [G] GUITERM NONSSH WINSSH W_BE_ALL WINMISC winx11 putty.res LIBS puttytel : [G] GUITERM NONSSH W_BE_NOSSH WINMISC puttytel.res nogss LIBS plink : [C] winplink wincons NONSSH WINSSH W_BE_ALL logging WINMISC + winx11 plink.res winnojmp noterm LIBS pscp : [C] pscp winsftp wincons WINSSH BE_SSH SFTP wildcard WINMISC + pscp.res winnojmp LIBS psftp : [C] psftp winsftp wincons WINSSH BE_SSH SFTP wildcard WINMISC + psftp.res winnojmp LIBS pageant : [G] winpgnt sshrsa sshpubk sshdes sshbn sshmd5 version tree234 + misc sshaes sshsha winsecur winpgntc sshdss sshsh256 sshsh512 + winutils winmisc winhelp conf pageant.res LIBS puttygen : [G] winpgen sshrsag sshdssg sshprime sshdes sshbn sshmd5 version + sshrand winnoise sshsha winstore misc winctrls sshrsa sshdss winmisc + sshpubk sshaes sshsh256 sshsh512 import winutils puttygen.res + tree234 notiming winhelp winnojmp conf LIBS wintime pterm : [X] GTKTERM uxmisc misc ldisc settings uxpty uxsel BE_NONE uxstore + uxsignal CHARSET cmdline uxpterm version time xpmpterm xpmptcfg + nogss putty : [X] GTKTERM uxmisc misc ldisc settings uxsel U_BE_ALL uxstore + uxsignal CHARSET uxputty NONSSH UXSSH UXMISC ux_x11 xpmputty + xpmpucfg puttytel : [X] GTKTERM uxmisc misc ldisc settings uxsel U_BE_NOSSH + uxstore uxsignal CHARSET uxputty NONSSH UXMISC xpmputty xpmpucfg + nogss plink : [U] uxplink uxcons NONSSH UXSSH U_BE_ALL logging UXMISC uxsignal + ux_x11 noterm puttygen : [U] cmdgen sshrsag sshdssg sshprime sshdes sshbn sshmd5 version + sshrand uxnoise sshsha misc sshrsa sshdss uxcons uxstore uxmisc + sshpubk sshaes sshsh256 sshsh512 import puttygen.res time tree234 + uxgen notiming conf pscp : [U] pscp uxsftp uxcons UXSSH BE_SSH SFTP wildcard UXMISC psftp : [U] psftp uxsftp uxcons UXSSH BE_SSH SFTP wildcard UXMISC PuTTY : [MX] osxmain OSXTERM OSXMISC CHARSET U_BE_ALL NONSSH UXSSH + ux_x11 uxpty uxsignal testback putty.icns info.plist |
Added callback.c.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 | /* * Facility for queueing callback functions to be run from the * top-level event loop after the current top-level activity finishes. */ #include <stddef.h> #include "putty.h" struct callback { struct callback *next; toplevel_callback_fn_t fn; void *ctx; }; struct callback *cbhead = NULL, *cbtail = NULL; toplevel_callback_notify_fn_t notify_frontend = NULL; void *frontend = NULL; void request_callback_notifications(toplevel_callback_notify_fn_t fn, void *fr) { notify_frontend = fn; frontend = fr; } void queue_toplevel_callback(toplevel_callback_fn_t fn, void *ctx) { struct callback *cb; cb = snew(struct callback); cb->fn = fn; cb->ctx = ctx; /* If the front end has requested notification of pending * callbacks, and we didn't already have one queued, let it know * we do have one now. */ if (notify_frontend && !cbhead) notify_frontend(frontend); if (cbtail) cbtail->next = cb; else cbhead = cb; cbtail = cb; cb->next = NULL; } void run_toplevel_callbacks(void) { if (cbhead) { struct callback *cb = cbhead; /* * Careful ordering here. We call the function _before_ * advancing cbhead (though, of course, we must free cb * _after_ advancing it). This means that if the very last * callback schedules another callback, cbhead does not become * NULL at any point, and so the frontend notification * function won't be needlessly pestered. */ cb->fn(cb->ctx); cbhead = cb->next; sfree(cb); if (!cbhead) cbtail = NULL; } } int toplevel_callback_pending(void) { return cbhead != NULL; } |
Deleted capi.c.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted capi.h.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Changes to charset/charset.h.
︙ | ︙ | |||
28 29 30 31 32 33 34 35 36 37 38 39 40 41 | CS_ISO8859_11, CS_ISO8859_13, CS_ISO8859_14, CS_ISO8859_15, CS_ISO8859_16, CS_CP437, CS_CP850, CS_CP866, CS_CP1250, CS_CP1251, CS_CP1252, CS_CP1253, CS_CP1254, CS_CP1255, | > | 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | CS_ISO8859_11, CS_ISO8859_13, CS_ISO8859_14, CS_ISO8859_15, CS_ISO8859_16, CS_CP437, CS_CP850, CS_CP852, CS_CP866, CS_CP1250, CS_CP1251, CS_CP1252, CS_CP1253, CS_CP1254, CS_CP1255, |
︙ | ︙ | |||
94 95 96 97 98 99 100 | * The sequence of `errlen' wide characters pointed to by `errstr' * will be used to indicate a conversion error. If `errstr' is * NULL, `errlen' will be ignored, and the library will choose * something sensible to do on its own. For Unicode, this will be * U+FFFD (REPLACEMENT CHARACTER). */ | > | | 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 | * The sequence of `errlen' wide characters pointed to by `errstr' * will be used to indicate a conversion error. If `errstr' is * NULL, `errlen' will be ignored, and the library will choose * something sensible to do on its own. For Unicode, this will be * U+FFFD (REPLACEMENT CHARACTER). */ int charset_to_unicode(const char **input, int *inlen, wchar_t *output, int outlen, int charset, charset_state *state, const wchar_t *errstr, int errlen); /* * Routine to convert Unicode to an MB/SB character set. * * This routine accepts some number of Unicode characters, updates |
︙ | ︙ | |||
117 118 119 120 121 122 123 | * The sequence of `errlen' characters pointed to by `errstr' will * be used to indicate a conversion error. If `errstr' is NULL, * `errlen' will be ignored, and the library will choose something * sensible to do on its own (which will vary depending on the * output charset). */ | > | | 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 | * The sequence of `errlen' characters pointed to by `errstr' will * be used to indicate a conversion error. If `errstr' is NULL, * `errlen' will be ignored, and the library will choose something * sensible to do on its own (which will vary depending on the * output charset). */ int charset_from_unicode(const wchar_t **input, int *inlen, char *output, int outlen, int charset, charset_state *state, const char *errstr, int errlen); /* * Convert X11 encoding names to and from our charset identifiers. */ const char *charset_to_xenc(int charset); |
︙ | ︙ |
Changes to charset/fromucs.c.
︙ | ︙ | |||
36 37 38 39 40 41 42 | outlen--; } } else { param->stopped = 1; } } | > | | 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | outlen--; } } else { param->stopped = 1; } } int charset_from_unicode(const wchar_t **input, int *inlen, char *output, int outlen, int charset, charset_state *state, const char *errstr, int errlen) { charset_spec const *spec = charset_find_spec(charset); charset_state localstate; struct charset_emit_param param; |
︙ | ︙ |
Changes to charset/localenc.c.
︙ | ︙ | |||
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 | static const struct { const char *name; int charset; int return_in_enum; /* enumeration misses some charsets */ } localencs[] = { { "<UNKNOWN>", CS_NONE, 0 }, { "ISO-8859-1", CS_ISO8859_1, 1 }, { "ISO-8859-1 with X11 line drawing", CS_ISO8859_1_X11, 0 }, { "ISO-8859-2", CS_ISO8859_2, 1 }, { "ISO-8859-3", CS_ISO8859_3, 1 }, { "ISO-8859-4", CS_ISO8859_4, 1 }, { "ISO-8859-5", CS_ISO8859_5, 1 }, { "ISO-8859-6", CS_ISO8859_6, 1 }, { "ISO-8859-7", CS_ISO8859_7, 1 }, { "ISO-8859-8", CS_ISO8859_8, 1 }, { "ISO-8859-9", CS_ISO8859_9, 1 }, { "ISO-8859-10", CS_ISO8859_10, 1 }, { "ISO-8859-11", CS_ISO8859_11, 1 }, { "ISO-8859-13", CS_ISO8859_13, 1 }, { "ISO-8859-14", CS_ISO8859_14, 1 }, { "ISO-8859-15", CS_ISO8859_15, 1 }, { "ISO-8859-16", CS_ISO8859_16, 1 }, { "CP437", CS_CP437, 1 }, { "CP850", CS_CP850, 1 }, { "CP866", CS_CP866, 1 }, { "CP1250", CS_CP1250, 1 }, { "CP1251", CS_CP1251, 1 }, { "CP1252", CS_CP1252, 1 }, { "CP1253", CS_CP1253, 1 }, { "CP1254", CS_CP1254, 1 }, { "CP1255", CS_CP1255, 1 }, | > > | 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | static const struct { const char *name; int charset; int return_in_enum; /* enumeration misses some charsets */ } localencs[] = { { "<UNKNOWN>", CS_NONE, 0 }, { "UTF-8", CS_UTF8, 1 }, { "ISO-8859-1", CS_ISO8859_1, 1 }, { "ISO-8859-1 with X11 line drawing", CS_ISO8859_1_X11, 0 }, { "ISO-8859-2", CS_ISO8859_2, 1 }, { "ISO-8859-3", CS_ISO8859_3, 1 }, { "ISO-8859-4", CS_ISO8859_4, 1 }, { "ISO-8859-5", CS_ISO8859_5, 1 }, { "ISO-8859-6", CS_ISO8859_6, 1 }, { "ISO-8859-7", CS_ISO8859_7, 1 }, { "ISO-8859-8", CS_ISO8859_8, 1 }, { "ISO-8859-9", CS_ISO8859_9, 1 }, { "ISO-8859-10", CS_ISO8859_10, 1 }, { "ISO-8859-11", CS_ISO8859_11, 1 }, { "ISO-8859-13", CS_ISO8859_13, 1 }, { "ISO-8859-14", CS_ISO8859_14, 1 }, { "ISO-8859-15", CS_ISO8859_15, 1 }, { "ISO-8859-16", CS_ISO8859_16, 1 }, { "CP437", CS_CP437, 1 }, { "CP850", CS_CP850, 1 }, { "CP852", CS_CP852, 1 }, { "CP866", CS_CP866, 1 }, { "CP1250", CS_CP1250, 1 }, { "CP1251", CS_CP1251, 1 }, { "CP1252", CS_CP1252, 1 }, { "CP1253", CS_CP1253, 1 }, { "CP1254", CS_CP1254, 1 }, { "CP1255", CS_CP1255, 1 }, |
︙ | ︙ | |||
70 71 72 73 74 75 76 | { "Mac Cyrillic (old)", CS_MAC_CYRILLIC_OLD, 0 }, { "Mac Ukraine", CS_MAC_UKRAINE, 1 }, { "Mac VT100", CS_MAC_VT100, 1 }, { "Mac VT100 (old)", CS_MAC_VT100_OLD, 0 }, { "VISCII", CS_VISCII, 1 }, { "HP ROMAN8", CS_HP_ROMAN8, 1 }, { "DEC MCS", CS_DEC_MCS, 1 }, | < | 72 73 74 75 76 77 78 79 80 81 82 83 84 85 | { "Mac Cyrillic (old)", CS_MAC_CYRILLIC_OLD, 0 }, { "Mac Ukraine", CS_MAC_UKRAINE, 1 }, { "Mac VT100", CS_MAC_VT100, 1 }, { "Mac VT100 (old)", CS_MAC_VT100_OLD, 0 }, { "VISCII", CS_VISCII, 1 }, { "HP ROMAN8", CS_HP_ROMAN8, 1 }, { "DEC MCS", CS_DEC_MCS, 1 }, }; const char *charset_to_localenc(int charset) { int i; for (i = 0; i < (int)lenof(localencs); i++) |
︙ | ︙ |
Changes to charset/mimeenc.c.
︙ | ︙ | |||
131 132 133 134 135 136 137 138 139 140 141 142 143 144 | { "csPC8CodePage437", CS_CP437 }, { "IBM850", CS_CP850 }, { "cp850", CS_CP850 }, { "850", CS_CP850 }, { "csPC850Multilingual", CS_CP850 }, { "IBM866", CS_CP866 }, { "cp866", CS_CP866 }, { "866", CS_CP866 }, { "csIBM866", CS_CP866 }, { "windows-1250", CS_CP1250 }, | > > > > > | 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 | { "csPC8CodePage437", CS_CP437 }, { "IBM850", CS_CP850 }, { "cp850", CS_CP850 }, { "850", CS_CP850 }, { "csPC850Multilingual", CS_CP850 }, { "IBM852", CS_CP852 }, { "cp852", CS_CP852 }, { "852", CS_CP852 }, { "csIBM852", CS_CP852 }, { "IBM866", CS_CP866 }, { "cp866", CS_CP866 }, { "866", CS_CP866 }, { "csIBM866", CS_CP866 }, { "windows-1250", CS_CP1250 }, |
︙ | ︙ |
Changes to charset/sbcs.dat.
︙ | ︙ | |||
390 391 392 393 394 395 396 397 398 399 400 401 402 403 | 0430 0431 0432 0433 0434 0435 0436 0437 0438 0439 043a 043b 043c 043d 043e 043f 2591 2592 2593 2502 2524 2561 2562 2556 2555 2563 2551 2557 255d 255c 255b 2510 2514 2534 252c 251c 2500 253c 255e 255f 255a 2554 2569 2566 2560 2550 256c 2567 2568 2564 2565 2559 2558 2552 2553 256b 256a 2518 250c 2588 2584 258c 2590 2580 0440 0441 0442 0443 0444 0445 0446 0447 0448 0449 044a 044b 044c 044d 044e 044f 0401 0451 0404 0454 0407 0457 040e 045e 00b0 2219 00b7 221a 2116 00a4 25a0 00a0 Here are some Windows code pages, generated by this piece of Bourne shell: for i in 1250 1251 1252 1253 1254 1255 1256 1257 1258; do echo charset CS_CP$i gensbcs http://www.unicode.org/Public/MAPPINGS/VENDORS/MICSFT/WINDOWS/CP$i.TXT echo | > > > > > > > > > > > > > > > > > > > > > > | 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 | 0430 0431 0432 0433 0434 0435 0436 0437 0438 0439 043a 043b 043c 043d 043e 043f 2591 2592 2593 2502 2524 2561 2562 2556 2555 2563 2551 2557 255d 255c 255b 2510 2514 2534 252c 251c 2500 253c 255e 255f 255a 2554 2569 2566 2560 2550 256c 2567 2568 2564 2565 2559 2558 2552 2553 256b 256a 2518 250c 2588 2584 258c 2590 2580 0440 0441 0442 0443 0444 0445 0446 0447 0448 0449 044a 044b 044c 044d 044e 044f 0401 0451 0404 0454 0407 0457 040e 045e 00b0 2219 00b7 221a 2116 00a4 25a0 00a0 Another old DOS code page, submitted by a user and checked against the translation table at http://msdn.microsoft.com/en-us/goglobal/cc305161.aspx . charset CS_CP852 0000 0001 0002 0003 0004 0005 0006 0007 0008 0009 000a 000b 000c 000d 000e 000f 0010 0011 0012 0013 0014 0015 0016 0017 0018 0019 001a 001b 001c 001d 001e 001f 0020 0021 0022 0023 0024 0025 0026 0027 0028 0029 002a 002b 002c 002d 002e 002f 0030 0031 0032 0033 0034 0035 0036 0037 0038 0039 003a 003b 003c 003d 003e 003f 0040 0041 0042 0043 0044 0045 0046 0047 0048 0049 004a 004b 004c 004d 004e 004f 0050 0051 0052 0053 0054 0055 0056 0057 0058 0059 005a 005b 005c 005d 005e 005f 0060 0061 0062 0063 0064 0065 0066 0067 0068 0069 006a 006b 006c 006d 006e 006f 0070 0071 0072 0073 0074 0075 0076 0077 0078 0079 007a 007b 007c 007d 007e 007f 00c7 00fc 00e9 00e2 00e4 016f 0107 00e7 0142 00eb 0150 0151 00ee 0179 00c4 0106 00c9 0139 013a 00f4 00f6 013d 013e 015a 015b 00d6 00dc 0164 0165 0141 00d7 010d 00e1 00ed 00f3 00fa 0104 0105 017d 017e 0118 0119 00ac 017a 010c 015f 00ab 00bb 2591 2592 2593 2502 2524 00c1 00c2 011a 015e 2563 2551 2557 255d 017b 017c 2510 2514 2534 252c 251c 2500 253c 0102 0103 255a 2554 2569 2566 2560 2550 256c 00a4 0111 0110 010e 00cb 010f 0147 00cd 00ce 011b 2518 250c 2588 2584 0162 016e 2580 00d3 00df 00d4 0143 0144 0148 0160 0161 0154 00da 0155 0170 00fd 00dd 0163 00b4 00ad 02dd 02db 02c7 02d8 00a7 00f7 00b8 00b0 00a8 02d9 0171 0158 0159 25a0 00a0 Here are some Windows code pages, generated by this piece of Bourne shell: for i in 1250 1251 1252 1253 1254 1255 1256 1257 1258; do echo charset CS_CP$i gensbcs http://www.unicode.org/Public/MAPPINGS/VENDORS/MICSFT/WINDOWS/CP$i.TXT echo |
︙ | ︙ |
Changes to charset/toucs.c.
︙ | ︙ | |||
42 43 44 45 46 47 48 | outlen--; } } else { param->stopped = 1; } } | > | | 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 | outlen--; } } else { param->stopped = 1; } } int charset_to_unicode(const char **input, int *inlen, wchar_t *output, int outlen, int charset, charset_state *state, const wchar_t *errstr, int errlen) { charset_spec const *spec = charset_find_spec(charset); charset_state localstate; struct unicode_emit_param param; |
︙ | ︙ |
Changes to charset/xenc.c.
︙ | ︙ | |||
42 43 44 45 46 47 48 49 50 51 52 53 54 55 | /* * Unofficial encoding names found in the wild. */ { "iso8859-16", CS_ISO8859_16 }, { "koi8-u", CS_KOI8_U }, { "ibm-cp437", CS_CP437 }, { "ibm-cp850", CS_CP850 }, { "ibm-cp866", CS_CP866 }, { "microsoft-cp1250", CS_CP1250 }, { "microsoft-cp1251", CS_CP1251 }, { "microsoft-cp1252", CS_CP1252 }, { "microsoft-cp1253", CS_CP1253 }, { "microsoft-cp1254", CS_CP1254 }, { "microsoft-cp1255", CS_CP1255 }, | > | 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 | /* * Unofficial encoding names found in the wild. */ { "iso8859-16", CS_ISO8859_16 }, { "koi8-u", CS_KOI8_U }, { "ibm-cp437", CS_CP437 }, { "ibm-cp850", CS_CP850 }, { "ibm-cp852", CS_CP852 }, { "ibm-cp866", CS_CP866 }, { "microsoft-cp1250", CS_CP1250 }, { "microsoft-cp1251", CS_CP1251 }, { "microsoft-cp1252", CS_CP1252 }, { "microsoft-cp1253", CS_CP1253 }, { "microsoft-cp1254", CS_CP1254 }, { "microsoft-cp1255", CS_CP1255 }, |
︙ | ︙ |
Changes to cmdgen.c.
︙ | ︙ | |||
97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 | fprintf(stderr, "FATAL ERROR: "); va_start(ap, p); vfprintf(stderr, p, ap); va_end(ap); fputc('\n', stderr); cleanup_exit(1); } /* * Stubs to let everything else link sensibly. */ void log_eventlog(void *handle, const char *event) { } char *x_get_default(const char *key) { return NULL; } void sk_cleanup(void) { } void showversion(void) { | > > > > > > > > > > < < | < | 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 | fprintf(stderr, "FATAL ERROR: "); va_start(ap, p); vfprintf(stderr, p, ap); va_end(ap); fputc('\n', stderr); cleanup_exit(1); } void nonfatal(char *p, ...) { va_list ap; fprintf(stderr, "ERROR: "); va_start(ap, p); vfprintf(stderr, p, ap); va_end(ap); fputc('\n', stderr); } /* * Stubs to let everything else link sensibly. */ void log_eventlog(void *handle, const char *event) { } char *x_get_default(const char *key) { return NULL; } void sk_cleanup(void) { } void showversion(void) { printf("puttygen: %s\n", ver); } void usage(int standalone) { fprintf(stderr, "Usage: puttygen ( keyfile | -t type [ -b bits ] )\n" " [ -C comment ] [ -P ] [ -q ]\n" |
︙ | ︙ | |||
253 254 255 256 257 258 259 | return dupstr(buffer); } int main(int argc, char **argv) { char *infile = NULL; | | < | | 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 | return dupstr(buffer); } int main(int argc, char **argv) { char *infile = NULL; Filename *infilename = NULL, *outfilename = NULL; enum { NOKEYGEN, RSA1, RSA2, DSA } keytype = NOKEYGEN; char *outfile = NULL, *outfiletmp = NULL; enum { PRIVATE, PUBLIC, PUBLICO, FP, OPENSSH, SSHCOM } outtype = PRIVATE; int bits = 2048; char *comment = NULL, *origcomment = NULL; int change_passphrase = FALSE; int errs = FALSE, nogo = FALSE; int intype = SSH_KEYTYPE_UNOPENABLE; int sshver = 0; struct ssh2_userkey *ssh2key = NULL; struct RSAKey *ssh1key = NULL; |
︙ | ︙ | |||
532 533 534 535 536 537 538 | /* * Analyse the type of the input file, in case this affects our * course of action. */ if (infile) { infilename = filename_from_str(infile); | | | 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 | /* * Analyse the type of the input file, in case this affects our * course of action. */ if (infile) { infilename = filename_from_str(infile); intype = key_type(infilename); switch (intype) { /* * It would be nice here to be able to load _public_ * key files, in any of a number of forms, and (a) * convert them to other public key types, (b) print * out their fingerprints. Or, I suppose, for real |
︙ | ︙ | |||
664 665 666 667 668 669 670 | 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); | | | 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 | 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); smemclr(entropy, bits/8); sfree(entropy); if (keytype == DSA) { struct dss_key *dsskey = snew(struct dss_key); dsa_generate(dsskey, bits, progressfn, &prog); ssh2key = snew(struct ssh2_userkey); ssh2key->data = dsskey; |
︙ | ︙ | |||
703 704 705 706 707 708 709 | assert(infile != NULL); /* * Find out whether the input key is encrypted. */ if (intype == SSH_KEYTYPE_SSH1) | | | | | | 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 | assert(infile != NULL); /* * Find out whether the input key is encrypted. */ if (intype == SSH_KEYTYPE_SSH1) encrypted = rsakey_encrypted(infilename, &origcomment); else if (intype == SSH_KEYTYPE_SSH2) encrypted = ssh2_userkey_encrypted(infilename, &origcomment); else encrypted = import_encrypted(infilename, intype, &origcomment); /* * If so, ask for a passphrase. */ if (encrypted && load_encrypted) { prompts_t *p = new_prompts(NULL); int ret; p->to_server = FALSE; p->name = dupstr("SSH key passphrase"); add_prompt(p, dupstr("Enter passphrase to load key: "), FALSE); ret = console_get_userpass_input(p, NULL, 0); assert(ret >= 0); if (!ret) { free_prompts(p); perror("puttygen: unable to read passphrase"); return 1; } else { |
︙ | ︙ | |||
742 743 744 745 746 747 748 | case SSH_KEYTYPE_SSH1: ssh1key = snew(struct RSAKey); if (!load_encrypted) { void *vblob; unsigned char *blob; int n, l, bloblen; | | > > > | | > | | | | | > | | | 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 | case SSH_KEYTYPE_SSH1: ssh1key = snew(struct RSAKey); if (!load_encrypted) { void *vblob; unsigned char *blob; int n, l, bloblen; ret = rsakey_pubblob(infilename, &vblob, &bloblen, &origcomment, &error); blob = (unsigned char *)vblob; n = 4; /* skip modulus bits */ l = ssh1_read_bignum(blob + n, bloblen - n, &ssh1key->exponent); if (l < 0) { error = "SSH-1 public key blob was too short"; } else { n += l; l = ssh1_read_bignum(blob + n, bloblen - n, &ssh1key->modulus); if (l < 0) { error = "SSH-1 public key blob was too short"; } else n += l; } ssh1key->comment = dupstr(origcomment); ssh1key->private_exponent = NULL; ssh1key->p = NULL; ssh1key->q = NULL; ssh1key->iqmp = NULL; } else { ret = loadrsakey(infilename, ssh1key, passphrase, &error); } if (ret > 0) error = NULL; else if (!error) error = "unknown error"; break; case SSH_KEYTYPE_SSH2: if (!load_encrypted) { ssh2blob = ssh2_userkey_loadpub(infilename, &ssh2alg, &ssh2bloblen, NULL, &error); if (ssh2blob) { ssh2algf = find_pubkey_alg(ssh2alg); if (ssh2algf) bits = ssh2algf->pubkey_bits(ssh2blob, ssh2bloblen); else bits = -1; } } else { ssh2key = ssh2_load_userkey(infilename, passphrase, &error); } if ((ssh2key && ssh2key != SSH2_WRONG_PASSPHRASE) || ssh2blob) error = NULL; else if (!error) { if (ssh2key == SSH2_WRONG_PASSPHRASE) error = "wrong passphrase"; else error = "unknown error"; } break; case SSH_KEYTYPE_OPENSSH: case SSH_KEYTYPE_SSHCOM: ssh2key = import_ssh2(infilename, intype, passphrase, &error); if (ssh2key) { if (ssh2key != SSH2_WRONG_PASSPHRASE) error = NULL; else error = "wrong passphrase"; } else if (!error) error = "unknown error"; |
︙ | ︙ | |||
842 843 844 845 846 847 848 | */ if (change_passphrase || keytype != NOKEYGEN) { prompts_t *p = new_prompts(NULL); int ret; p->to_server = FALSE; p->name = dupstr("New SSH key passphrase"); | | | | | 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 | */ if (change_passphrase || keytype != NOKEYGEN) { prompts_t *p = new_prompts(NULL); int ret; p->to_server = FALSE; p->name = dupstr("New SSH key passphrase"); add_prompt(p, dupstr("Enter passphrase to save key: "), FALSE); add_prompt(p, dupstr("Re-enter passphrase to verify: "), FALSE); ret = console_get_userpass_input(p, NULL, 0); assert(ret >= 0); if (!ret) { free_prompts(p); perror("puttygen: unable to read new passphrase"); return 1; } else { if (strcmp(p->prompts[0]->result, p->prompts[1]->result)) { free_prompts(p); fprintf(stderr, "puttygen: passphrases do not match\n"); return 1; } if (passphrase) { smemclr(passphrase, strlen(passphrase)); sfree(passphrase); } passphrase = dupstr(p->prompts[0]->result); free_prompts(p); if (!*passphrase) { sfree(passphrase); passphrase = NULL; |
︙ | ︙ | |||
888 889 890 891 892 893 894 | switch (outtype) { int ret; case PRIVATE: if (sshver == 1) { assert(ssh1key); | | | | 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 | switch (outtype) { int ret; case PRIVATE: if (sshver == 1) { assert(ssh1key); ret = saversakey(outfilename, ssh1key, passphrase); if (!ret) { fprintf(stderr, "puttygen: unable to save SSH-1 private key\n"); return 1; } } else { assert(ssh2key); ret = ssh2_save_userkey(outfilename, ssh2key, passphrase); if (!ret) { fprintf(stderr, "puttygen: unable to save SSH-2 private key\n"); return 1; } } if (outfiletmp) { if (!move(outfiletmp, outfile)) |
︙ | ︙ | |||
1019 1020 1021 1022 1023 1024 1025 | } break; case OPENSSH: case SSHCOM: assert(sshver == 2); assert(ssh2key); | | | | 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 | } break; case OPENSSH: case SSHCOM: assert(sshver == 2); assert(ssh2key); ret = export_ssh2(outfilename, outtype, ssh2key, passphrase); if (!ret) { fprintf(stderr, "puttygen: unable to export key\n"); return 1; } if (outfiletmp) { if (!move(outfiletmp, outfile)) return 1; /* rename failed */ } break; } if (passphrase) { smemclr(passphrase, strlen(passphrase)); sfree(passphrase); } if (ssh1key) freersakey(ssh1key); if (ssh2key) { ssh2key->alg->freekey(ssh2key->data); |
︙ | ︙ |
Changes to cmdline.c.
︙ | ︙ | |||
59 60 61 62 63 64 65 | static char *cmdline_password = NULL; void cmdline_cleanup(void) { int pri; if (cmdline_password) { | | | 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 | static char *cmdline_password = NULL; void cmdline_cleanup(void) { int pri; if (cmdline_password) { smemclr(cmdline_password, strlen(cmdline_password)); sfree(cmdline_password); cmdline_password = NULL; } for (pri = 0; pri < NPRIORITIES; pri++) { sfree(saves[pri].params); saves[pri].params = NULL; |
︙ | ︙ | |||
101 102 103 104 105 106 107 | /* * If we've tried once, return utter failure (no more passwords left * to try). */ if (tried_once) return 0; | | < < | < | 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 | /* * If we've tried once, return utter failure (no more passwords left * to try). */ if (tried_once) return 0; prompt_set_result(p->prompts[0], cmdline_password); smemclr(cmdline_password, strlen(cmdline_password)); sfree(cmdline_password); cmdline_password = NULL; tried_once = 1; return 1; } /* * Here we have a flags word which describes the capabilities of * the particular tool on whose behalf we're running. We will * refuse certain command-line options if a particular tool * inherently can't do anything sensible. For example, the file |
︙ | ︙ | |||
158 159 160 161 162 163 164 | #define RETURN(x) do { \ if ((x) == 2 && !value) return -2; \ ret = x; \ if (need_save < 0) return x; \ } while (0) | | | | | > > | | > > | | > > | > | > | > | < | < | < | < | < < < < < < < < < < < < < < < < < > > > > > > > > > > | | > > > > | | > > > > > > > > > > > > > > > | < > > | < < | < < < < < < < > > > > > | < > | | | | | | | | | | | | | | | | | | | > | > > | | | < < | < < < < < < < < | | | | | | | | | | | | | | | 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 | #define RETURN(x) do { \ if ((x) == 2 && !value) return -2; \ ret = x; \ if (need_save < 0) return x; \ } while (0) int cmdline_process_param(char *p, char *value, int need_save, Conf *conf) { int ret = 0; if (!strcmp(p, "-load")) { RETURN(2); /* This parameter must be processed immediately rather than being * saved. */ do_defaults(value, conf); loaded_session = TRUE; cmdline_session_name = dupstr(value); return 2; } if (!strcmp(p, "-ssh")) { RETURN(1); UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK); SAVEABLE(0); default_protocol = PROT_SSH; default_port = 22; conf_set_int(conf, CONF_protocol, default_protocol); conf_set_int(conf, CONF_port, default_port); return 1; } if (!strcmp(p, "-telnet")) { RETURN(1); UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK); SAVEABLE(0); default_protocol = PROT_TELNET; default_port = 23; conf_set_int(conf, CONF_protocol, default_protocol); conf_set_int(conf, CONF_port, default_port); return 1; } if (!strcmp(p, "-rlogin")) { RETURN(1); UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK); SAVEABLE(0); default_protocol = PROT_RLOGIN; default_port = 513; conf_set_int(conf, CONF_protocol, default_protocol); conf_set_int(conf, CONF_port, default_port); return 1; } if (!strcmp(p, "-raw")) { RETURN(1); UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK); SAVEABLE(0); default_protocol = PROT_RAW; conf_set_int(conf, CONF_protocol, default_protocol); } if (!strcmp(p, "-serial")) { RETURN(1); /* Serial is not NONNETWORK in an odd sense of the word */ UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK); SAVEABLE(0); default_protocol = PROT_SERIAL; conf_set_int(conf, CONF_protocol, default_protocol); /* The host parameter will already be loaded into CONF_host, * so copy it across */ conf_set_str(conf, CONF_serline, conf_get_str(conf, CONF_host)); } if (!strcmp(p, "-v")) { RETURN(1); flags |= FLAG_VERBOSE; } if (!strcmp(p, "-l")) { RETURN(2); UNAVAILABLE_IN(TOOLTYPE_NONNETWORK); SAVEABLE(0); conf_set_str(conf, CONF_username, value); } if (!strcmp(p, "-loghost")) { RETURN(2); UNAVAILABLE_IN(TOOLTYPE_NONNETWORK); SAVEABLE(0); conf_set_str(conf, CONF_loghost, value); } if ((!strcmp(p, "-L") || !strcmp(p, "-R") || !strcmp(p, "-D"))) { char type, *q, *qq, *key, *val; RETURN(2); UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK); SAVEABLE(0); if (strcmp(p, "-D")) { /* * For -L or -R forwarding types: * * We expect _at least_ two colons in this string. The * possible formats are `sourceport:desthost:destport', * or `sourceip:sourceport:desthost:destport' if you're * specifying a particular loopback address. We need to * replace the one between source and dest with a \t; * this means we must find the second-to-last colon in * the string. * * (This looks like a foolish way of doing it given the * existence of strrchr, but it's more efficient than * two strrchrs - not to mention that the second strrchr * would require us to modify the input string!) */ type = p[1]; /* 'L' or 'R' */ q = qq = strchr(value, ':'); while (qq) { char *qqq = strchr(qq+1, ':'); if (qqq) q = qq; qq = qqq; } if (!q) { cmdline_error("-%c expects at least two colons in its" " argument", type); return ret; } key = dupprintf("%c%.*s", type, (int)(q - value), value); val = dupstr(q+1); } else { /* * Dynamic port forwardings are entered under the same key * as if they were local (because they occupy the same * port space - a local and a dynamic forwarding on the * same local port are mutually exclusive), with the * special value "D" (which can be distinguished from * anything in the ordinary -L case by containing no * colon). */ key = dupprintf("L%s", value); val = dupstr("D"); } conf_set_str_str(conf, CONF_portfwd, key, val); sfree(key); sfree(val); } if ((!strcmp(p, "-nc"))) { char *host, *portp; RETURN(2); UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK); SAVEABLE(0); portp = strchr(value, ':'); if (!portp) { cmdline_error("-nc expects argument of form 'host:port'"); return ret; } host = dupprintf("%.*s", (int)(portp - value), value); conf_set_str(conf, CONF_ssh_nc_host, host); conf_set_int(conf, CONF_ssh_nc_port, atoi(portp + 1)); sfree(host); } if (!strcmp(p, "-m")) { char *filename, *command; int cmdlen, cmdsize; FILE *fp; int c, d; RETURN(2); UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK); SAVEABLE(0); filename = value; cmdlen = cmdsize = 0; command = NULL; fp = fopen(filename, "r"); if (!fp) { cmdline_error("unable to open command file \"%s\"", filename); return ret; } do { c = fgetc(fp); d = c; if (c == EOF) d = 0; if (cmdlen >= cmdsize) { cmdsize = cmdlen + 512; command = sresize(command, cmdsize, char); } command[cmdlen++] = d; } while (c != EOF); fclose(fp); conf_set_str(conf, CONF_remote_cmd, command); conf_set_str(conf, CONF_remote_cmd2, ""); conf_set_int(conf, CONF_nopty, TRUE); /* command => no terminal */ sfree(command); } if (!strcmp(p, "-P")) { RETURN(2); UNAVAILABLE_IN(TOOLTYPE_NONNETWORK); SAVEABLE(1); /* lower priority than -ssh,-telnet */ conf_set_int(conf, CONF_port, atoi(value)); } if (!strcmp(p, "-pw")) { RETURN(2); UNAVAILABLE_IN(TOOLTYPE_NONNETWORK); 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 (conf_get_int(conf, CONF_protocol) != PROT_SSH) cmdline_error("the -pw option can only be used with the " "SSH protocol"); 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. */ smemclr(value, strlen(value)); } } if (!strcmp(p, "-agent") || !strcmp(p, "-pagent") || !strcmp(p, "-pageant")) { RETURN(1); UNAVAILABLE_IN(TOOLTYPE_NONNETWORK); SAVEABLE(0); conf_set_int(conf, CONF_tryagent, TRUE); } if (!strcmp(p, "-noagent") || !strcmp(p, "-nopagent") || !strcmp(p, "-nopageant")) { RETURN(1); UNAVAILABLE_IN(TOOLTYPE_NONNETWORK); SAVEABLE(0); conf_set_int(conf, CONF_tryagent, FALSE); } if (!strcmp(p, "-A")) { RETURN(1); UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK); SAVEABLE(0); conf_set_int(conf, CONF_agentfwd, 1); } if (!strcmp(p, "-a")) { RETURN(1); UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK); SAVEABLE(0); conf_set_int(conf, CONF_agentfwd, 0); } if (!strcmp(p, "-X")) { RETURN(1); UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK); SAVEABLE(0); conf_set_int(conf, CONF_x11_forward, 1); } if (!strcmp(p, "-x")) { RETURN(1); UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK); SAVEABLE(0); conf_set_int(conf, CONF_x11_forward, 0); } if (!strcmp(p, "-t")) { RETURN(1); UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK); SAVEABLE(1); /* lower priority than -m */ conf_set_int(conf, CONF_nopty, 0); } if (!strcmp(p, "-T")) { RETURN(1); UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK); SAVEABLE(1); conf_set_int(conf, CONF_nopty, 1); } if (!strcmp(p, "-N")) { RETURN(1); UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK); SAVEABLE(0); conf_set_int(conf, CONF_ssh_no_shell, 1); } if (!strcmp(p, "-C")) { RETURN(1); UNAVAILABLE_IN(TOOLTYPE_NONNETWORK); SAVEABLE(0); conf_set_int(conf, CONF_compression, 1); } if (!strcmp(p, "-1")) { RETURN(1); UNAVAILABLE_IN(TOOLTYPE_NONNETWORK); SAVEABLE(0); conf_set_int(conf, CONF_sshprot, 0); /* ssh protocol 1 only */ } if (!strcmp(p, "-2")) { RETURN(1); UNAVAILABLE_IN(TOOLTYPE_NONNETWORK); SAVEABLE(0); conf_set_int(conf, CONF_sshprot, 3); /* ssh protocol 2 only */ } if (!strcmp(p, "-i")) { Filename *fn; RETURN(2); UNAVAILABLE_IN(TOOLTYPE_NONNETWORK); SAVEABLE(0); fn = filename_from_str(value); conf_set_filename(conf, CONF_keyfile, fn); filename_free(fn); } if (!strcmp(p, "-4") || !strcmp(p, "-ipv4")) { RETURN(1); SAVEABLE(1); conf_set_int(conf, CONF_addressfamily, ADDRTYPE_IPV4); } if (!strcmp(p, "-6") || !strcmp(p, "-ipv6")) { RETURN(1); SAVEABLE(1); conf_set_int(conf, CONF_addressfamily, ADDRTYPE_IPV6); } if (!strcmp(p, "-sercfg")) { char* nextitem; RETURN(2); UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK); SAVEABLE(1); if (conf_get_int(conf, CONF_protocol) != PROT_SERIAL) cmdline_error("the -sercfg option can only be used with the " "serial protocol"); /* Value[0] contains one or more , separated values, like 19200,8,n,1,X */ nextitem = value; while (nextitem[0] != '\0') { int length, skip; char *end = strchr(nextitem, ','); if (!end) { length = strlen(nextitem); skip = 0; } else { length = end - nextitem; nextitem[length] = '\0'; skip = 1; } if (length == 1) { switch (*nextitem) { case '1': case '2': conf_set_int(conf, CONF_serstopbits, 2 * (*nextitem-'0')); break; case '5': case '6': case '7': case '8': case '9': conf_set_int(conf, CONF_serdatabits, *nextitem-'0'); break; case 'n': conf_set_int(conf, CONF_serparity, SER_PAR_NONE); break; case 'o': conf_set_int(conf, CONF_serparity, SER_PAR_ODD); break; case 'e': conf_set_int(conf, CONF_serparity, SER_PAR_EVEN); break; case 'm': conf_set_int(conf, CONF_serparity, SER_PAR_MARK); break; case 's': conf_set_int(conf, CONF_serparity, SER_PAR_SPACE); break; case 'N': conf_set_int(conf, CONF_serflow, SER_FLOW_NONE); break; case 'X': conf_set_int(conf, CONF_serflow, SER_FLOW_XONXOFF); break; case 'R': conf_set_int(conf, CONF_serflow, SER_FLOW_RTSCTS); break; case 'D': conf_set_int(conf, CONF_serflow, SER_FLOW_DSRDTR); break; default: cmdline_error("Unrecognised suboption \"-sercfg %c\"", *nextitem); } } else if (length == 3 && !strncmp(nextitem,"1.5",3)) { /* Messy special case */ conf_set_int(conf, CONF_serstopbits, 3); } else { int serspeed = atoi(nextitem); if (serspeed != 0) { conf_set_int(conf, CONF_serspeed, serspeed); } else { cmdline_error("Unrecognised suboption \"-sercfg %s\"", nextitem); } } nextitem += length + skip; } } return ret; /* unrecognised */ } void cmdline_run_saved(Conf *conf) { int pri, i; for (pri = 0; pri < NPRIORITIES; pri++) for (i = 0; i < saves[pri].nsaved; i++) cmdline_process_param(saves[pri].params[i].p, saves[pri].params[i].value, 0, conf); } |
Added conf.c.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 | /* * conf.c: implementation of the internal storage format used for * the configuration of a PuTTY session. */ #include <stdio.h> #include <stddef.h> #include <assert.h> #include "tree234.h" #include "putty.h" /* * Enumeration of types used in keys and values. */ typedef enum { TYPE_NONE, TYPE_INT, TYPE_STR, TYPE_FILENAME, TYPE_FONT } Type; /* * Arrays which allow us to look up the subkey and value types for a * given primary key id. */ #define CONF_SUBKEYTYPE_DEF(valtype, keytype, keyword) TYPE_ ## keytype, static int subkeytypes[] = { CONFIG_OPTIONS(CONF_SUBKEYTYPE_DEF) }; #define CONF_VALUETYPE_DEF(valtype, keytype, keyword) TYPE_ ## valtype, static int valuetypes[] = { CONFIG_OPTIONS(CONF_VALUETYPE_DEF) }; /* * Configuration keys are primarily integers (big enum of all the * different configurable options); some keys have string-designated * subkeys, such as the list of environment variables (subkeys * defined by the variable names); some have integer-designated * subkeys (wordness, colours, preference lists). */ struct key { int primary; union { int i; char *s; } secondary; }; struct value { union { int intval; char *stringval; Filename *fileval; FontSpec *fontval; } u; }; struct conf_entry { struct key key; struct value value; }; struct conf_tag { tree234 *tree; }; /* * Because 'struct key' is the first element in 'struct conf_entry', * it's safe (guaranteed by the C standard) to cast arbitrarily back * and forth between the two types. Therefore, we only need one * comparison function, which can double as a main sort function for * the tree (comparing two conf_entry structures with each other) * and a search function (looking up an externally supplied key). */ static int conf_cmp(void *av, void *bv) { struct key *a = (struct key *)av; struct key *b = (struct key *)bv; if (a->primary < b->primary) return -1; else if (a->primary > b->primary) return +1; switch (subkeytypes[a->primary]) { case TYPE_INT: if (a->secondary.i < b->secondary.i) return -1; else if (a->secondary.i > b->secondary.i) return +1; return 0; case TYPE_STR: return strcmp(a->secondary.s, b->secondary.s); default: return 0; } } /* * Free any dynamic data items pointed to by a 'struct key'. We * don't free the structure itself, since it's probably part of a * larger allocated block. */ static void free_key(struct key *key) { if (subkeytypes[key->primary] == TYPE_STR) sfree(key->secondary.s); } /* * Copy a 'struct key' into another one, copying its dynamic data * if necessary. */ static void copy_key(struct key *to, struct key *from) { to->primary = from->primary; switch (subkeytypes[to->primary]) { case TYPE_INT: to->secondary.i = from->secondary.i; break; case TYPE_STR: to->secondary.s = dupstr(from->secondary.s); break; } } /* * Free any dynamic data items pointed to by a 'struct value'. We * don't free the value itself, since it's probably part of a larger * allocated block. */ static void free_value(struct value *val, int type) { if (type == TYPE_STR) sfree(val->u.stringval); else if (type == TYPE_FILENAME) filename_free(val->u.fileval); else if (type == TYPE_FONT) fontspec_free(val->u.fontval); } /* * Copy a 'struct value' into another one, copying its dynamic data * if necessary. */ static void copy_value(struct value *to, struct value *from, int type) { switch (type) { case TYPE_INT: to->u.intval = from->u.intval; break; case TYPE_STR: to->u.stringval = dupstr(from->u.stringval); break; case TYPE_FILENAME: to->u.fileval = filename_copy(from->u.fileval); break; case TYPE_FONT: to->u.fontval = fontspec_copy(from->u.fontval); break; } } /* * Free an entire 'struct conf_entry' and its dynamic data. */ static void free_entry(struct conf_entry *entry) { free_key(&entry->key); free_value(&entry->value, valuetypes[entry->key.primary]); sfree(entry); } Conf *conf_new(void) { Conf *conf = snew(struct conf_tag); conf->tree = newtree234(conf_cmp); return conf; } static void conf_clear(Conf *conf) { struct conf_entry *entry; while ((entry = delpos234(conf->tree, 0)) != NULL) free_entry(entry); } void conf_free(Conf *conf) { conf_clear(conf); freetree234(conf->tree); sfree(conf); } static void conf_insert(Conf *conf, struct conf_entry *entry) { struct conf_entry *oldentry = add234(conf->tree, entry); if (oldentry && oldentry != entry) { del234(conf->tree, oldentry); free_entry(oldentry); oldentry = add234(conf->tree, entry); assert(oldentry == entry); } } void conf_copy_into(Conf *newconf, Conf *oldconf) { struct conf_entry *entry, *entry2; int i; conf_clear(newconf); for (i = 0; (entry = index234(oldconf->tree, i)) != NULL; i++) { entry2 = snew(struct conf_entry); copy_key(&entry2->key, &entry->key); copy_value(&entry2->value, &entry->value, valuetypes[entry->key.primary]); add234(newconf->tree, entry2); } } Conf *conf_copy(Conf *oldconf) { Conf *newconf = conf_new(); conf_copy_into(newconf, oldconf); return newconf; } int conf_get_int(Conf *conf, int primary) { struct key key; struct conf_entry *entry; assert(subkeytypes[primary] == TYPE_NONE); assert(valuetypes[primary] == TYPE_INT); key.primary = primary; entry = find234(conf->tree, &key, NULL); assert(entry); return entry->value.u.intval; } int conf_get_int_int(Conf *conf, int primary, int secondary) { struct key key; struct conf_entry *entry; assert(subkeytypes[primary] == TYPE_INT); assert(valuetypes[primary] == TYPE_INT); key.primary = primary; key.secondary.i = secondary; entry = find234(conf->tree, &key, NULL); assert(entry); return entry->value.u.intval; } char *conf_get_str(Conf *conf, int primary) { struct key key; struct conf_entry *entry; assert(subkeytypes[primary] == TYPE_NONE); assert(valuetypes[primary] == TYPE_STR); key.primary = primary; entry = find234(conf->tree, &key, NULL); assert(entry); return entry->value.u.stringval; } char *conf_get_str_str_opt(Conf *conf, int primary, const char *secondary) { struct key key; struct conf_entry *entry; assert(subkeytypes[primary] == TYPE_STR); assert(valuetypes[primary] == TYPE_STR); key.primary = primary; key.secondary.s = (char *)secondary; entry = find234(conf->tree, &key, NULL); return entry ? entry->value.u.stringval : NULL; } char *conf_get_str_str(Conf *conf, int primary, const char *secondary) { char *ret = conf_get_str_str_opt(conf, primary, secondary); assert(ret); return ret; } char *conf_get_str_strs(Conf *conf, int primary, char *subkeyin, char **subkeyout) { struct key key; struct conf_entry *entry; assert(subkeytypes[primary] == TYPE_STR); assert(valuetypes[primary] == TYPE_STR); key.primary = primary; if (subkeyin) { key.secondary.s = subkeyin; entry = findrel234(conf->tree, &key, NULL, REL234_GT); } else { key.secondary.s = ""; entry = findrel234(conf->tree, &key, NULL, REL234_GE); } if (!entry || entry->key.primary != primary) return NULL; *subkeyout = entry->key.secondary.s; return entry->value.u.stringval; } char *conf_get_str_nthstrkey(Conf *conf, int primary, int n) { struct key key; struct conf_entry *entry; int index; assert(subkeytypes[primary] == TYPE_STR); assert(valuetypes[primary] == TYPE_STR); key.primary = primary; key.secondary.s = ""; entry = findrelpos234(conf->tree, &key, NULL, REL234_GE, &index); if (!entry || entry->key.primary != primary) return NULL; entry = index234(conf->tree, index + n); if (!entry || entry->key.primary != primary) return NULL; return entry->key.secondary.s; } Filename *conf_get_filename(Conf *conf, int primary) { struct key key; struct conf_entry *entry; assert(subkeytypes[primary] == TYPE_NONE); assert(valuetypes[primary] == TYPE_FILENAME); key.primary = primary; entry = find234(conf->tree, &key, NULL); assert(entry); return entry->value.u.fileval; } FontSpec *conf_get_fontspec(Conf *conf, int primary) { struct key key; struct conf_entry *entry; assert(subkeytypes[primary] == TYPE_NONE); assert(valuetypes[primary] == TYPE_FONT); key.primary = primary; entry = find234(conf->tree, &key, NULL); assert(entry); return entry->value.u.fontval; } void conf_set_int(Conf *conf, int primary, int value) { struct conf_entry *entry = snew(struct conf_entry); assert(subkeytypes[primary] == TYPE_NONE); assert(valuetypes[primary] == TYPE_INT); entry->key.primary = primary; entry->value.u.intval = value; conf_insert(conf, entry); } void conf_set_int_int(Conf *conf, int primary, int secondary, int value) { struct conf_entry *entry = snew(struct conf_entry); assert(subkeytypes[primary] == TYPE_INT); assert(valuetypes[primary] == TYPE_INT); entry->key.primary = primary; entry->key.secondary.i = secondary; entry->value.u.intval = value; conf_insert(conf, entry); } void conf_set_str(Conf *conf, int primary, const char *value) { struct conf_entry *entry = snew(struct conf_entry); assert(subkeytypes[primary] == TYPE_NONE); assert(valuetypes[primary] == TYPE_STR); entry->key.primary = primary; entry->value.u.stringval = dupstr(value); conf_insert(conf, entry); } void conf_set_str_str(Conf *conf, int primary, const char *secondary, const char *value) { struct conf_entry *entry = snew(struct conf_entry); assert(subkeytypes[primary] == TYPE_STR); assert(valuetypes[primary] == TYPE_STR); entry->key.primary = primary; entry->key.secondary.s = dupstr(secondary); entry->value.u.stringval = dupstr(value); conf_insert(conf, entry); } void conf_del_str_str(Conf *conf, int primary, const char *secondary) { struct key key; struct conf_entry *entry; assert(subkeytypes[primary] == TYPE_STR); assert(valuetypes[primary] == TYPE_STR); key.primary = primary; key.secondary.s = (char *)secondary; entry = find234(conf->tree, &key, NULL); if (entry) { del234(conf->tree, entry); free_entry(entry); } } void conf_set_filename(Conf *conf, int primary, const Filename *value) { struct conf_entry *entry = snew(struct conf_entry); assert(subkeytypes[primary] == TYPE_NONE); assert(valuetypes[primary] == TYPE_FILENAME); entry->key.primary = primary; entry->value.u.fileval = filename_copy(value); conf_insert(conf, entry); } void conf_set_fontspec(Conf *conf, int primary, const FontSpec *value) { struct conf_entry *entry = snew(struct conf_entry); assert(subkeytypes[primary] == TYPE_NONE); assert(valuetypes[primary] == TYPE_FONT); entry->key.primary = primary; entry->value.u.fontval = fontspec_copy(value); conf_insert(conf, entry); } int conf_serialised_size(Conf *conf) { int i; struct conf_entry *entry; int size = 0; for (i = 0; (entry = index234(conf->tree, i)) != NULL; i++) { size += 4; /* primary key */ switch (subkeytypes[entry->key.primary]) { case TYPE_INT: size += 4; break; case TYPE_STR: size += 1 + strlen(entry->key.secondary.s); break; } switch (valuetypes[entry->key.primary]) { case TYPE_INT: size += 4; break; case TYPE_STR: size += 1 + strlen(entry->value.u.stringval); break; case TYPE_FILENAME: size += filename_serialise(entry->value.u.fileval, NULL); break; case TYPE_FONT: size += fontspec_serialise(entry->value.u.fontval, NULL); break; } } size += 4; /* terminator value */ return size; } void conf_serialise(Conf *conf, void *vdata) { unsigned char *data = (unsigned char *)vdata; int i, len; struct conf_entry *entry; for (i = 0; (entry = index234(conf->tree, i)) != NULL; i++) { PUT_32BIT_MSB_FIRST(data, entry->key.primary); data += 4; switch (subkeytypes[entry->key.primary]) { case TYPE_INT: PUT_32BIT_MSB_FIRST(data, entry->key.secondary.i); data += 4; break; case TYPE_STR: len = strlen(entry->key.secondary.s); memcpy(data, entry->key.secondary.s, len); data += len; *data++ = 0; break; } switch (valuetypes[entry->key.primary]) { case TYPE_INT: PUT_32BIT_MSB_FIRST(data, entry->value.u.intval); data += 4; break; case TYPE_STR: len = strlen(entry->value.u.stringval); memcpy(data, entry->value.u.stringval, len); data += len; *data++ = 0; break; case TYPE_FILENAME: data += filename_serialise(entry->value.u.fileval, data); break; case TYPE_FONT: data += fontspec_serialise(entry->value.u.fontval, data); break; } } PUT_32BIT_MSB_FIRST(data, 0xFFFFFFFFU); } int conf_deserialise(Conf *conf, void *vdata, int maxsize) { unsigned char *data = (unsigned char *)vdata; unsigned char *start = data; struct conf_entry *entry; unsigned primary; int used; unsigned char *zero; while (maxsize >= 4) { primary = GET_32BIT_MSB_FIRST(data); data += 4, maxsize -= 4; if (primary >= N_CONFIG_OPTIONS) break; entry = snew(struct conf_entry); entry->key.primary = primary; switch (subkeytypes[entry->key.primary]) { case TYPE_INT: if (maxsize < 4) { sfree(entry); goto done; } entry->key.secondary.i = toint(GET_32BIT_MSB_FIRST(data)); data += 4, maxsize -= 4; break; case TYPE_STR: zero = memchr(data, 0, maxsize); if (!zero) { sfree(entry); goto done; } entry->key.secondary.s = dupstr((char *)data); maxsize -= (zero + 1 - data); data = zero + 1; break; } switch (valuetypes[entry->key.primary]) { case TYPE_INT: if (maxsize < 4) { if (subkeytypes[entry->key.primary] == TYPE_STR) sfree(entry->key.secondary.s); sfree(entry); goto done; } entry->value.u.intval = toint(GET_32BIT_MSB_FIRST(data)); data += 4, maxsize -= 4; break; case TYPE_STR: zero = memchr(data, 0, maxsize); if (!zero) { if (subkeytypes[entry->key.primary] == TYPE_STR) sfree(entry->key.secondary.s); sfree(entry); goto done; } entry->value.u.stringval = dupstr((char *)data); maxsize -= (zero + 1 - data); data = zero + 1; break; case TYPE_FILENAME: entry->value.u.fileval = filename_deserialise(data, maxsize, &used); if (!entry->value.u.fileval) { if (subkeytypes[entry->key.primary] == TYPE_STR) sfree(entry->key.secondary.s); sfree(entry); goto done; } data += used; maxsize -= used; break; case TYPE_FONT: entry->value.u.fontval = fontspec_deserialise(data, maxsize, &used); if (!entry->value.u.fontval) { if (subkeytypes[entry->key.primary] == TYPE_STR) sfree(entry->key.secondary.s); sfree(entry); goto done; } data += used; maxsize -= used; break; } conf_insert(conf, entry); } done: return (int)(data - start); } |
Changes to config.c.
1 2 3 4 5 6 7 8 9 10 11 12 | /* * config.c - the platform-independent parts of the PuTTY * configuration box. */ #include <assert.h> #include <stdlib.h> #include "putty.h" #include "dialog.h" #include "storage.h" | < < < < < < < < < < < < < < > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | | < | > > | > | | | | | | > > > | | | | > | | > > | > > | | > | | | > > | | | | | | | | | | | | | | | | | | | > | < | | > | | | > | < | > | | | > | | > > | | | > | | < | | > > > > > > > > > | | < < < | < < < < < < | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | < < < < | < < < < < | < < < | < < < < < < < < < < < < < < | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | < | < < < < < < < < < < < < < < < | | < < < | < < < | | | < | < < < < < < < < < < < < < < < < < | > | < | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 | /* * config.c - the platform-independent parts of the PuTTY * configuration box. */ #include <assert.h> #include <stdlib.h> #include "putty.h" #include "dialog.h" #include "storage.h" #define PRINTER_DISABLED_STRING "None (printing disabled)" #define HOST_BOX_TITLE "Host Name (or IP address)" #define PORT_BOX_TITLE "Port" void conf_radiobutton_handler(union control *ctrl, void *dlg, void *data, int event) { int button; Conf *conf = (Conf *)data; /* * For a standard radio button set, the context parameter gives * the primary key (CONF_foo), and the extra data per button * gives the value the target field should take if that button * is the one selected. */ if (event == EVENT_REFRESH) { int val = conf_get_int(conf, ctrl->radio.context.i); for (button = 0; button < ctrl->radio.nbuttons; button++) if (val == ctrl->radio.buttondata[button].i) break; /* We expected that `break' to happen, in all circumstances. */ assert(button < ctrl->radio.nbuttons); dlg_radiobutton_set(ctrl, dlg, button); } else if (event == EVENT_VALCHANGE) { button = dlg_radiobutton_get(ctrl, dlg); assert(button >= 0 && button < ctrl->radio.nbuttons); conf_set_int(conf, ctrl->radio.context.i, ctrl->radio.buttondata[button].i); } } #define CHECKBOX_INVERT (1<<30) void conf_checkbox_handler(union control *ctrl, void *dlg, void *data, int event) { int key, invert; Conf *conf = (Conf *)data; /* * For a standard checkbox, the context parameter gives the * primary key (CONF_foo), optionally ORed with CHECKBOX_INVERT. */ key = ctrl->checkbox.context.i; if (key & CHECKBOX_INVERT) { key &= ~CHECKBOX_INVERT; invert = 1; } else invert = 0; /* * C lacks a logical XOR, so the following code uses the idiom * (!a ^ !b) to obtain the logical XOR of a and b. (That is, 1 * iff exactly one of a and b is nonzero, otherwise 0.) */ if (event == EVENT_REFRESH) { int val = conf_get_int(conf, key); dlg_checkbox_set(ctrl, dlg, (!val ^ !invert)); } else if (event == EVENT_VALCHANGE) { conf_set_int(conf, key, !dlg_checkbox_get(ctrl,dlg) ^ !invert); } } void conf_editbox_handler(union control *ctrl, void *dlg, void *data, int event) { /* * The standard edit-box handler expects the main `context' * field to contain the primary key. The secondary `context2' * field indicates the type of this field: * * - if context2 > 0, the field is a string. * - if context2 == -1, the field is an int and the edit box * is numeric. * - if context2 < -1, the field is an int and the edit box is * _floating_, and (-context2) gives the scale. (E.g. if * context2 == -1000, then typing 1.2 into the box will set * the field to 1200.) */ int key = ctrl->editbox.context.i; int length = ctrl->editbox.context2.i; Conf *conf = (Conf *)data; if (length > 0) { if (event == EVENT_REFRESH) { char *field = conf_get_str(conf, key); dlg_editbox_set(ctrl, dlg, field); } else if (event == EVENT_VALCHANGE) { char *field = dlg_editbox_get(ctrl, dlg); conf_set_str(conf, key, field); sfree(field); } } else if (length < 0) { if (event == EVENT_REFRESH) { char str[80]; int value = conf_get_int(conf, key); if (length == -1) sprintf(str, "%d", value); else sprintf(str, "%g", (double)value / (double)(-length)); dlg_editbox_set(ctrl, dlg, str); } else if (event == EVENT_VALCHANGE) { char *str = dlg_editbox_get(ctrl, dlg); if (length == -1) conf_set_int(conf, key, atoi(str)); else conf_set_int(conf, key, (int)((-length) * atof(str))); sfree(str); } } } void conf_filesel_handler(union control *ctrl, void *dlg, void *data, int event) { int key = ctrl->fileselect.context.i; Conf *conf = (Conf *)data; if (event == EVENT_REFRESH) { dlg_filesel_set(ctrl, dlg, conf_get_filename(conf, key)); } else if (event == EVENT_VALCHANGE) { Filename *filename = dlg_filesel_get(ctrl, dlg); conf_set_filename(conf, key, filename); filename_free(filename); } } void conf_fontsel_handler(union control *ctrl, void *dlg, void *data, int event) { int key = ctrl->fontselect.context.i; Conf *conf = (Conf *)data; if (event == EVENT_REFRESH) { dlg_fontsel_set(ctrl, dlg, conf_get_fontspec(conf, key)); } else if (event == EVENT_VALCHANGE) { FontSpec *fontspec = dlg_fontsel_get(ctrl, dlg); conf_set_fontspec(conf, key, fontspec); fontspec_free(fontspec); } } static void config_host_handler(union control *ctrl, void *dlg, void *data, int event) { Conf *conf = (Conf *)data; /* * This function works just like the standard edit box handler, * only it has to choose the control's label and text from two * different places depending on the protocol. */ if (event == EVENT_REFRESH) { if (conf_get_int(conf, CONF_protocol) == PROT_SERIAL) { /* * This label text is carefully chosen to contain an n, * since that's the shortcut for the host name control. */ dlg_label_change(ctrl, dlg, "Serial line"); dlg_editbox_set(ctrl, dlg, conf_get_str(conf, CONF_serline)); } else { dlg_label_change(ctrl, dlg, HOST_BOX_TITLE); dlg_editbox_set(ctrl, dlg, conf_get_str(conf, CONF_host)); } } else if (event == EVENT_VALCHANGE) { char *s = dlg_editbox_get(ctrl, dlg); if (conf_get_int(conf, CONF_protocol) == PROT_SERIAL) conf_set_str(conf, CONF_serline, s); else conf_set_str(conf, CONF_host, s); sfree(s); } } static void config_port_handler(union control *ctrl, void *dlg, void *data, int event) { Conf *conf = (Conf *)data; char buf[80]; /* * This function works similarly to the standard edit box handler, * only it has to choose the control's label and text from two * different places depending on the protocol. */ if (event == EVENT_REFRESH) { if (conf_get_int(conf, CONF_protocol) == PROT_SERIAL) { /* * This label text is carefully chosen to contain a p, * since that's the shortcut for the port control. */ dlg_label_change(ctrl, dlg, "Speed"); sprintf(buf, "%d", conf_get_int(conf, CONF_serspeed)); } else { dlg_label_change(ctrl, dlg, PORT_BOX_TITLE); if (conf_get_int(conf, CONF_port) != 0) sprintf(buf, "%d", conf_get_int(conf, CONF_port)); else /* Display an (invalid) port of 0 as blank */ buf[0] = '\0'; } dlg_editbox_set(ctrl, dlg, buf); } else if (event == EVENT_VALCHANGE) { char *s = dlg_editbox_get(ctrl, dlg); int i = atoi(s); sfree(s); if (conf_get_int(conf, CONF_protocol) == PROT_SERIAL) conf_set_int(conf, CONF_serspeed, i); else conf_set_int(conf, CONF_port, i); } } struct hostport { union control *host, *port; }; /* * We export this function so that platform-specific config * routines can use it to conveniently identify the protocol radio * buttons in order to add to them. */ void config_protocolbuttons_handler(union control *ctrl, void *dlg, void *data, int event) { int button; Conf *conf = (Conf *)data; struct hostport *hp = (struct hostport *)ctrl->radio.context.p; /* * This function works just like the standard radio-button * handler, except that it also has to change the setting of * the port box, and refresh both host and port boxes when. We * expect the context parameter to point at a hostport * structure giving the `union control's for both. */ if (event == EVENT_REFRESH) { int protocol = conf_get_int(conf, CONF_protocol); for (button = 0; button < ctrl->radio.nbuttons; button++) if (protocol == ctrl->radio.buttondata[button].i) break; /* We expected that `break' to happen, in all circumstances. */ assert(button < ctrl->radio.nbuttons); dlg_radiobutton_set(ctrl, dlg, button); } else if (event == EVENT_VALCHANGE) { int oldproto = conf_get_int(conf, CONF_protocol); int newproto, port; button = dlg_radiobutton_get(ctrl, dlg); assert(button >= 0 && button < ctrl->radio.nbuttons); newproto = ctrl->radio.buttondata[button].i; conf_set_int(conf, CONF_protocol, newproto); if (oldproto != newproto) { Backend *ob = backend_from_proto(oldproto); Backend *nb = backend_from_proto(newproto); assert(ob); assert(nb); /* Iff the user hasn't changed the port from the old protocol's * default, update it with the new protocol's default. * (This includes a "default" of 0, implying that there is no * sensible default for that protocol; in this case it's * displayed as a blank.) * This helps with the common case of tabbing through the * controls in order and setting a non-default port before * getting to the protocol; we want that non-default port * to be preserved. */ port = conf_get_int(conf, CONF_port); if (port == ob->default_port) conf_set_int(conf, CONF_port, nb->default_port); } dlg_refresh(hp->host, dlg); dlg_refresh(hp->port, dlg); } } static void loggingbuttons_handler(union control *ctrl, void *dlg, void *data, int event) { int button; Conf *conf = (Conf *)data; /* This function works just like the standard radio-button handler, * but it has to fall back to "no logging" in situations where the * configured logging type isn't applicable. */ if (event == EVENT_REFRESH) { int logtype = conf_get_int(conf, CONF_logtype); for (button = 0; button < ctrl->radio.nbuttons; button++) if (logtype == ctrl->radio.buttondata[button].i) break; /* We fell off the end, so we lack the configured logging type */ if (button == ctrl->radio.nbuttons) { button = 0; conf_set_int(conf, CONF_logtype, LGTYP_NONE); } dlg_radiobutton_set(ctrl, dlg, button); } else if (event == EVENT_VALCHANGE) { button = dlg_radiobutton_get(ctrl, dlg); assert(button >= 0 && button < ctrl->radio.nbuttons); conf_set_int(conf, CONF_logtype, ctrl->radio.buttondata[button].i); } } static void numeric_keypad_handler(union control *ctrl, void *dlg, void *data, int event) { int button; Conf *conf = (Conf *)data; /* * This function works much like the standard radio button * handler, but it has to handle two fields in Conf. */ if (event == EVENT_REFRESH) { if (conf_get_int(conf, CONF_nethack_keypad)) button = 2; else if (conf_get_int(conf, CONF_app_keypad)) button = 1; else button = 0; assert(button < ctrl->radio.nbuttons); dlg_radiobutton_set(ctrl, dlg, button); } else if (event == EVENT_VALCHANGE) { button = dlg_radiobutton_get(ctrl, dlg); assert(button >= 0 && button < ctrl->radio.nbuttons); if (button == 2) { conf_set_int(conf, CONF_app_keypad, FALSE); conf_set_int(conf, CONF_nethack_keypad, TRUE); } else { conf_set_int(conf, CONF_app_keypad, (button != 0)); conf_set_int(conf, CONF_nethack_keypad, FALSE); } } } static void cipherlist_handler(union control *ctrl, void *dlg, void *data, int event) { Conf *conf = (Conf *)data; if (event == EVENT_REFRESH) { int i; static const struct { char *s; int c; } ciphers[] = { { "3DES", CIPHER_3DES }, { "Blowfish", CIPHER_BLOWFISH }, { "DES", CIPHER_DES }, { "AES (SSH-2 only)", CIPHER_AES }, { "Arcfour (SSH-2 only)", CIPHER_ARCFOUR }, { "-- warn below here --", CIPHER_WARN } }; /* Set up the "selected ciphers" box. */ /* (cipherlist assumed to contain all ciphers) */ dlg_update_start(ctrl, dlg); dlg_listbox_clear(ctrl, dlg); for (i = 0; i < CIPHER_MAX; i++) { int c = conf_get_int_int(conf, CONF_ssh_cipherlist, i); int j; char *cstr = NULL; for (j = 0; j < (sizeof ciphers) / (sizeof ciphers[0]); j++) { if (ciphers[j].c == c) { cstr = ciphers[j].s; break; } } dlg_listbox_addwithid(ctrl, dlg, cstr, c); } dlg_update_done(ctrl, dlg); } else if (event == EVENT_VALCHANGE) { int i; /* Update array to match the list box. */ for (i=0; i < CIPHER_MAX; i++) conf_set_int_int(conf, CONF_ssh_cipherlist, i, dlg_listbox_getid(ctrl, dlg, i)); } } #ifndef NO_GSSAPI static void gsslist_handler(union control *ctrl, void *dlg, void *data, int event) { Conf *conf = (Conf *)data; if (event == EVENT_REFRESH) { int i; dlg_update_start(ctrl, dlg); dlg_listbox_clear(ctrl, dlg); for (i = 0; i < ngsslibs; i++) { int id = conf_get_int_int(conf, CONF_ssh_gsslist, i); assert(id >= 0 && id < ngsslibs); dlg_listbox_addwithid(ctrl, dlg, gsslibnames[id], id); } dlg_update_done(ctrl, dlg); } else if (event == EVENT_VALCHANGE) { int i; /* Update array to match the list box. */ for (i=0; i < ngsslibs; i++) conf_set_int_int(conf, CONF_ssh_gsslist, i, dlg_listbox_getid(ctrl, dlg, i)); } } #endif static void kexlist_handler(union control *ctrl, void *dlg, void *data, int event) { Conf *conf = (Conf *)data; if (event == EVENT_REFRESH) { int i; static const struct { char *s; int k; } kexes[] = { { "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 } }; /* Set up the "kex preference" box. */ /* (kexlist assumed to contain all algorithms) */ dlg_update_start(ctrl, dlg); dlg_listbox_clear(ctrl, dlg); for (i = 0; i < KEX_MAX; i++) { int k = conf_get_int_int(conf, CONF_ssh_kexlist, i); int j; char *kstr = NULL; for (j = 0; j < (sizeof kexes) / (sizeof kexes[0]); j++) { if (kexes[j].k == k) { kstr = kexes[j].s; break; } } dlg_listbox_addwithid(ctrl, dlg, kstr, k); } dlg_update_done(ctrl, dlg); } else if (event == EVENT_VALCHANGE) { int i; /* Update array to match the list box. */ for (i=0; i < KEX_MAX; i++) conf_set_int_int(conf, CONF_ssh_kexlist, i, dlg_listbox_getid(ctrl, dlg, i)); } } static void printerbox_handler(union control *ctrl, void *dlg, void *data, int event) { Conf *conf = (Conf *)data; if (event == EVENT_REFRESH) { int nprinters, i; printer_enum *pe; char *printer; dlg_update_start(ctrl, dlg); /* * Some backends may wish to disable the drop-down list on * this edit box. Be prepared for this. */ if (ctrl->editbox.has_list) { dlg_listbox_clear(ctrl, dlg); dlg_listbox_add(ctrl, dlg, PRINTER_DISABLED_STRING); pe = printer_start_enum(&nprinters); for (i = 0; i < nprinters; i++) dlg_listbox_add(ctrl, dlg, printer_get_name(pe, i)); printer_finish_enum(pe); } printer = conf_get_str(conf, CONF_printer); if (!printer) printer = PRINTER_DISABLED_STRING; dlg_editbox_set(ctrl, dlg, printer); dlg_update_done(ctrl, dlg); } else if (event == EVENT_VALCHANGE) { char *printer = dlg_editbox_get(ctrl, dlg); if (!strcmp(printer, PRINTER_DISABLED_STRING)) printer[0] = '\0'; conf_set_str(conf, CONF_printer, printer); sfree(printer); } } static void codepage_handler(union control *ctrl, void *dlg, void *data, int event) { Conf *conf = (Conf *)data; if (event == EVENT_REFRESH) { int i; const char *cp, *thiscp; dlg_update_start(ctrl, dlg); thiscp = cp_name(decode_codepage(conf_get_str(conf, CONF_line_codepage))); dlg_listbox_clear(ctrl, dlg); for (i = 0; (cp = cp_enumerate(i)) != NULL; i++) dlg_listbox_add(ctrl, dlg, cp); dlg_editbox_set(ctrl, dlg, thiscp); conf_set_str(conf, CONF_line_codepage, thiscp); dlg_update_done(ctrl, dlg); } else if (event == EVENT_VALCHANGE) { char *codepage = dlg_editbox_get(ctrl, dlg); conf_set_str(conf, CONF_line_codepage, cp_name(decode_codepage(codepage))); sfree(codepage); } } static void sshbug_handler(union control *ctrl, void *dlg, void *data, int event) { Conf *conf = (Conf *)data; if (event == EVENT_REFRESH) { /* * We must fetch the previously configured value from the Conf * before we start modifying the drop-down list, otherwise the * spurious SELCHANGE we trigger in the process will overwrite * the value we wanted to keep. */ int oldconf = conf_get_int(conf, ctrl->listbox.context.i); dlg_update_start(ctrl, dlg); dlg_listbox_clear(ctrl, dlg); dlg_listbox_addwithid(ctrl, dlg, "Auto", AUTO); dlg_listbox_addwithid(ctrl, dlg, "Off", FORCE_OFF); dlg_listbox_addwithid(ctrl, dlg, "On", FORCE_ON); switch (oldconf) { case AUTO: dlg_listbox_select(ctrl, dlg, 0); break; case FORCE_OFF: dlg_listbox_select(ctrl, dlg, 1); break; case FORCE_ON: dlg_listbox_select(ctrl, dlg, 2); break; } dlg_update_done(ctrl, dlg); } else if (event == EVENT_SELCHANGE) { int i = dlg_listbox_index(ctrl, dlg); if (i < 0) i = AUTO; else i = dlg_listbox_getid(ctrl, dlg, i); conf_set_int(conf, ctrl->listbox.context.i, i); } } struct sessionsaver_data { union control *editbox, *listbox, *loadbutton, *savebutton, *delbutton; union control *okbutton, *cancelbutton; struct sesslist sesslist; int midsession; char *savedsession; /* the current contents of ssd->editbox */ }; static void sessionsaver_data_free(void *ssdv) { struct sessionsaver_data *ssd = (struct sessionsaver_data *)ssdv; sfree(ssd->savedsession); sfree(ssd); } /* * Helper function to load the session selected in the list box, if * any, as this is done in more than one place below. Returns 0 for * failure. */ static int load_selected_session(struct sessionsaver_data *ssd, void *dlg, Conf *conf, int *maybe_launch) { int i = dlg_listbox_index(ssd->listbox, dlg); int isdef; if (i < 0) { dlg_beep(dlg); return 0; } isdef = !strcmp(ssd->sesslist.sessions[i], "Default Settings"); load_settings(ssd->sesslist.sessions[i], conf); sfree(ssd->savedsession); ssd->savedsession = dupstr(isdef ? "" : ssd->sesslist.sessions[i]); if (maybe_launch) *maybe_launch = !isdef; dlg_refresh(NULL, dlg); /* Restore the selection, which might have been clobbered by * changing the value of the edit box. */ dlg_listbox_select(ssd->listbox, dlg, i); return 1; } static void sessionsaver_handler(union control *ctrl, void *dlg, void *data, int event) { Conf *conf = (Conf *)data; struct sessionsaver_data *ssd = (struct sessionsaver_data *)ctrl->generic.context.p; if (event == EVENT_REFRESH) { if (ctrl == ssd->editbox) { dlg_editbox_set(ctrl, dlg, ssd->savedsession); } else if (ctrl == ssd->listbox) { int i; dlg_update_start(ctrl, dlg); dlg_listbox_clear(ctrl, dlg); for (i = 0; i < ssd->sesslist.nsessions; i++) dlg_listbox_add(ctrl, dlg, ssd->sesslist.sessions[i]); dlg_update_done(ctrl, dlg); } } else if (event == EVENT_VALCHANGE) { int top, bottom, halfway, i; if (ctrl == ssd->editbox) { sfree(ssd->savedsession); ssd->savedsession = dlg_editbox_get(ctrl, dlg); top = ssd->sesslist.nsessions; bottom = -1; while (top-bottom > 1) { halfway = (top+bottom)/2; i = strcmp(ssd->savedsession, ssd->sesslist.sessions[halfway]); if (i <= 0 ) { top = halfway; } else { bottom = halfway; } } if (top == ssd->sesslist.nsessions) { |
︙ | ︙ | |||
832 833 834 835 836 837 838 | /* * The user has double-clicked a session, or hit Load. * We must load the selected session, and then * terminate the configuration dialog _if_ there was a * double-click on the list box _and_ that session * contains a hostname. */ | | | | | < | < | < | | < | | 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 | /* * The user has double-clicked a session, or hit Load. * We must load the selected session, and then * terminate the configuration dialog _if_ there was a * double-click on the list box _and_ that session * contains a hostname. */ if (load_selected_session(ssd, dlg, conf, &mbl) && (mbl && ctrl == ssd->listbox && conf_launchable(conf))) { dlg_end(dlg, 1); /* it's all over, and succeeded */ } } else if (ctrl == ssd->savebutton) { int isdef = !strcmp(ssd->savedsession, "Default Settings"); if (!ssd->savedsession[0]) { int i = dlg_listbox_index(ssd->listbox, dlg); if (i < 0) { dlg_beep(dlg); return; } isdef = !strcmp(ssd->sesslist.sessions[i], "Default Settings"); sfree(ssd->savedsession); ssd->savedsession = dupstr(isdef ? "" : ssd->sesslist.sessions[i]); } { char *errmsg = save_settings(ssd->savedsession, conf); if (errmsg) { dlg_error_msg(dlg, errmsg); sfree(errmsg); } } get_sesslist(&ssd->sesslist, FALSE); get_sesslist(&ssd->sesslist, TRUE); |
︙ | ︙ | |||
889 890 891 892 893 894 895 | * Annoying special case. If the `Open' button is * pressed while no host name is currently set, _and_ * the session list previously had the focus, _and_ * there was a session selected in that which had a * valid host name in it, then load it and go. */ if (dlg_last_focused(ctrl, dlg) == ssd->listbox && | | | | < > | | < > > | | | > | | > | | 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 | * Annoying special case. If the `Open' button is * pressed while no host name is currently set, _and_ * the session list previously had the focus, _and_ * there was a session selected in that which had a * valid host name in it, then load it and go. */ if (dlg_last_focused(ctrl, dlg) == ssd->listbox && !conf_launchable(conf)) { Conf *conf2 = conf_new(); int mbl = FALSE; if (!load_selected_session(ssd, dlg, conf2, &mbl)) { dlg_beep(dlg); conf_free(conf2); return; } /* If at this point we have a valid session, go! */ if (mbl && conf_launchable(conf2)) { conf_copy_into(conf, conf2); dlg_end(dlg, 1); } else dlg_beep(dlg); conf_free(conf2); return; } /* * Otherwise, do the normal thing: if we have a valid * session, get going. */ if (conf_launchable(conf)) { dlg_end(dlg, 1); } else dlg_beep(dlg); } else if (ctrl == ssd->cancelbutton) { dlg_end(dlg, 0); } } } struct charclass_data { union control *listbox, *editbox, *button; }; static void charclass_handler(union control *ctrl, void *dlg, void *data, int event) { Conf *conf = (Conf *)data; struct charclass_data *ccd = (struct charclass_data *)ctrl->generic.context.p; if (event == EVENT_REFRESH) { if (ctrl == ccd->listbox) { int i; dlg_update_start(ctrl, dlg); dlg_listbox_clear(ctrl, dlg); for (i = 0; i < 128; i++) { char str[100]; sprintf(str, "%d\t(0x%02X)\t%c\t%d", i, i, (i >= 0x21 && i != 0x7F) ? i : ' ', conf_get_int_int(conf, CONF_wordness, i)); dlg_listbox_add(ctrl, dlg, str); } dlg_update_done(ctrl, dlg); } } else if (event == EVENT_ACTION) { if (ctrl == ccd->button) { char *str; int i, n; str = dlg_editbox_get(ccd->editbox, dlg); n = atoi(str); sfree(str); for (i = 0; i < 128; i++) { if (dlg_listbox_issel(ccd->listbox, dlg, i)) conf_set_int_int(conf, CONF_wordness, i, n); } dlg_refresh(ccd->listbox, dlg); } } } struct colour_data { |
︙ | ︙ | |||
981 982 983 984 985 986 987 | "ANSI Cyan", "ANSI Cyan Bold", "ANSI White", "ANSI White Bold" }; static void colour_handler(union control *ctrl, void *dlg, void *data, int event) { | | | 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 | "ANSI Cyan", "ANSI Cyan Bold", "ANSI White", "ANSI White Bold" }; static void colour_handler(union control *ctrl, void *dlg, void *data, int event) { Conf *conf = (Conf *)data; struct colour_data *cd = (struct colour_data *)ctrl->generic.context.p; int update = FALSE, clear = FALSE, r, g, b; if (event == EVENT_REFRESH) { if (ctrl == cd->listbox) { int i; |
︙ | ︙ | |||
1005 1006 1007 1008 1009 1010 1011 | if (ctrl == cd->listbox) { /* The user has selected a colour. Update the RGB text. */ int i = dlg_listbox_index(ctrl, dlg); if (i < 0) { clear = TRUE; } else { clear = FALSE; | | | | | | | > | | | | | | | | | | 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 | if (ctrl == cd->listbox) { /* The user has selected a colour. Update the RGB text. */ int i = dlg_listbox_index(ctrl, dlg); if (i < 0) { clear = TRUE; } else { clear = FALSE; r = conf_get_int_int(conf, CONF_colours, i*3+0); g = conf_get_int_int(conf, CONF_colours, i*3+1); b = conf_get_int_int(conf, CONF_colours, i*3+2); } update = TRUE; } } else if (event == EVENT_VALCHANGE) { if (ctrl == cd->redit || ctrl == cd->gedit || ctrl == cd->bedit) { /* The user has changed the colour using the edit boxes. */ char *str; int i, cval; str = dlg_editbox_get(ctrl, dlg); cval = atoi(str); sfree(str); if (cval > 255) cval = 255; if (cval < 0) cval = 0; i = dlg_listbox_index(cd->listbox, dlg); if (i >= 0) { if (ctrl == cd->redit) conf_set_int_int(conf, CONF_colours, i*3+0, cval); else if (ctrl == cd->gedit) conf_set_int_int(conf, CONF_colours, i*3+1, cval); else if (ctrl == cd->bedit) conf_set_int_int(conf, CONF_colours, i*3+2, cval); } } } else if (event == EVENT_ACTION) { if (ctrl == cd->button) { int i = dlg_listbox_index(cd->listbox, dlg); if (i < 0) { dlg_beep(dlg); return; } /* * Start a colour selector, which will send us an * EVENT_CALLBACK when it's finished and allow us to * pick up the results. */ dlg_coloursel_start(ctrl, dlg, conf_get_int_int(conf, CONF_colours, i*3+0), conf_get_int_int(conf, CONF_colours, i*3+1), conf_get_int_int(conf, CONF_colours, i*3+2)); } } else if (event == EVENT_CALLBACK) { if (ctrl == cd->button) { int i = dlg_listbox_index(cd->listbox, dlg); /* * Collect the results of the colour selector. Will * return nonzero on success, or zero if the colour * selector did nothing (user hit Cancel, for example). */ if (dlg_coloursel_results(ctrl, dlg, &r, &g, &b)) { conf_set_int_int(conf, CONF_colours, i*3+0, r); conf_set_int_int(conf, CONF_colours, i*3+1, g); conf_set_int_int(conf, CONF_colours, i*3+2, b); clear = FALSE; update = TRUE; } } } if (update) { |
︙ | ︙ | |||
1089 1090 1091 1092 1093 1094 1095 | union control *modelist, *valradio, *valbox; union control *addbutton, *rembutton, *listbox; }; static void ttymodes_handler(union control *ctrl, void *dlg, void *data, int event) { | | | | | > | | < < | | < | < < < < < | < < | < < | | < < | < < < < < < < < | < > | > > > < < | | | < | | | | | | | | | < < < | < < < | | | > > > | < | | | > < < | | > > | < < < < < < < | < < | | | > > | < | < | < | < < < < < < < < < | | | < < < < | < < | < < < < < | < < > < < > | | > > > | > > > > > > > > > > > > > > > > > > | < | < < | > > | | | | < | | > < | < | < | > > | | | < < < < | < < < | < < < | | | > | < | < > | | > > > | | < | < | < | > > > | > | < < | | < < < | | > | < | | | > | < > > | > > > < | < < < < < < < < < < < | < < < | | < < | < < > < < > < < < < < | > > | 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 | union control *modelist, *valradio, *valbox; union control *addbutton, *rembutton, *listbox; }; static void ttymodes_handler(union control *ctrl, void *dlg, void *data, int event) { Conf *conf = (Conf *)data; struct ttymodes_data *td = (struct ttymodes_data *)ctrl->generic.context.p; if (event == EVENT_REFRESH) { if (ctrl == td->listbox) { char *key, *val; dlg_update_start(ctrl, dlg); dlg_listbox_clear(ctrl, dlg); for (val = conf_get_str_strs(conf, CONF_ttymodes, NULL, &key); val != NULL; val = conf_get_str_strs(conf, CONF_ttymodes, key, &key)) { char *disp = dupprintf("%s\t%s", key, (val[0] == 'A') ? "(auto)" : val+1); dlg_listbox_add(ctrl, dlg, disp); sfree(disp); } dlg_update_done(ctrl, dlg); } else if (ctrl == td->modelist) { int i; dlg_update_start(ctrl, dlg); dlg_listbox_clear(ctrl, dlg); for (i = 0; ttymodes[i]; i++) dlg_listbox_add(ctrl, dlg, ttymodes[i]); dlg_listbox_select(ctrl, dlg, 0); /* *shrug* */ dlg_update_done(ctrl, dlg); } else if (ctrl == td->valradio) { dlg_radiobutton_set(ctrl, dlg, 0); } } else if (event == EVENT_ACTION) { if (ctrl == td->addbutton) { int ind = dlg_listbox_index(td->modelist, dlg); if (ind >= 0) { char type = dlg_radiobutton_get(td->valradio, dlg) ? 'V' : 'A'; const char *key; char *str, *val; /* Construct new entry */ key = ttymodes[ind]; str = dlg_editbox_get(td->valbox, dlg); val = dupprintf("%c%s", type, str); sfree(str); conf_set_str_str(conf, CONF_ttymodes, key, val); sfree(val); dlg_refresh(td->listbox, dlg); } else dlg_beep(dlg); } else if (ctrl == td->rembutton) { int i = 0; char *key, *val; int multisel = dlg_listbox_index(td->listbox, dlg) < 0; for (val = conf_get_str_strs(conf, CONF_ttymodes, NULL, &key); val != NULL; val = conf_get_str_strs(conf, CONF_ttymodes, key, &key)) { 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.) */ int ind = 0; val++; while (ttymodes[ind]) { if (!strcmp(ttymodes[ind], key)) 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); } conf_del_str_str(conf, CONF_ttymodes, key); } i++; } dlg_refresh(td->listbox, dlg); } } } struct environ_data { union control *varbox, *valbox, *addbutton, *rembutton, *listbox; }; static void environ_handler(union control *ctrl, void *dlg, void *data, int event) { Conf *conf = (Conf *)data; struct environ_data *ed = (struct environ_data *)ctrl->generic.context.p; if (event == EVENT_REFRESH) { if (ctrl == ed->listbox) { char *key, *val; dlg_update_start(ctrl, dlg); dlg_listbox_clear(ctrl, dlg); for (val = conf_get_str_strs(conf, CONF_environmt, NULL, &key); val != NULL; val = conf_get_str_strs(conf, CONF_environmt, key, &key)) { char *p = dupprintf("%s\t%s", key, val); dlg_listbox_add(ctrl, dlg, p); sfree(p); } dlg_update_done(ctrl, dlg); } } else if (event == EVENT_ACTION) { if (ctrl == ed->addbutton) { char *key, *val, *str; key = dlg_editbox_get(ed->varbox, dlg); if (!*key) { sfree(key); dlg_beep(dlg); return; } val = dlg_editbox_get(ed->valbox, dlg); if (!*val) { sfree(key); sfree(val); dlg_beep(dlg); return; } conf_set_str_str(conf, CONF_environmt, key, val); str = dupcat(key, "\t", val, NULL); dlg_editbox_set(ed->varbox, dlg, ""); dlg_editbox_set(ed->valbox, dlg, ""); sfree(str); sfree(key); sfree(val); dlg_refresh(ed->listbox, dlg); } else if (ctrl == ed->rembutton) { int i = dlg_listbox_index(ed->listbox, dlg); if (i < 0) { dlg_beep(dlg); } else { char *key, *val; key = conf_get_str_nthstrkey(conf, CONF_environmt, i); if (key) { /* Populate controls with the entry we're about to delete * for ease of editing */ val = conf_get_str_str(conf, CONF_environmt, key); dlg_editbox_set(ed->varbox, dlg, key); dlg_editbox_set(ed->valbox, dlg, val); /* And delete it */ conf_del_str_str(conf, CONF_environmt, key); } } dlg_refresh(ed->listbox, dlg); } } } struct portfwd_data { union control *addbutton, *rembutton, *listbox; union control *sourcebox, *destbox, *direction; #ifndef NO_IPV6 union control *addressfamily; #endif }; static void portfwd_handler(union control *ctrl, void *dlg, void *data, int event) { Conf *conf = (Conf *)data; struct portfwd_data *pfd = (struct portfwd_data *)ctrl->generic.context.p; if (event == EVENT_REFRESH) { if (ctrl == pfd->listbox) { char *key, *val; dlg_update_start(ctrl, dlg); dlg_listbox_clear(ctrl, dlg); for (val = conf_get_str_strs(conf, CONF_portfwd, NULL, &key); val != NULL; val = conf_get_str_strs(conf, CONF_portfwd, key, &key)) { char *p; if (!strcmp(val, "D")) { char *L; /* * A dynamic forwarding is stored as L12345=D or * 6L12345=D (since it's mutually exclusive with * L12345=anything else), but displayed as D12345 * to match the fiction that 'Local', 'Remote' and * 'Dynamic' are three distinct modes and also to * align with OpenSSH's command line option syntax * that people will already be used to. So, for * display purposes, find the L in the key string * and turn it into a D. */ p = dupprintf("%s\t", key); L = strchr(p, 'L'); if (L) *L = 'D'; } else p = dupprintf("%s\t%s", key, val); dlg_listbox_add(ctrl, dlg, p); sfree(p); } dlg_update_done(ctrl, dlg); } else if (ctrl == pfd->direction) { /* * Default is Local. */ dlg_radiobutton_set(ctrl, dlg, 0); #ifndef NO_IPV6 } else if (ctrl == pfd->addressfamily) { dlg_radiobutton_set(ctrl, dlg, 0); #endif } } else if (event == EVENT_ACTION) { if (ctrl == pfd->addbutton) { char *family, *type, *src, *key, *val; int whichbutton; #ifndef NO_IPV6 whichbutton = dlg_radiobutton_get(pfd->addressfamily, dlg); if (whichbutton == 1) family = "4"; else if (whichbutton == 2) family = "6"; else family = ""; #endif whichbutton = dlg_radiobutton_get(pfd->direction, dlg); if (whichbutton == 0) type = "L"; else if (whichbutton == 1) type = "R"; else type = "D"; src = dlg_editbox_get(pfd->sourcebox, dlg); if (!*src) { dlg_error_msg(dlg, "You need to specify a source port number"); sfree(src); return; } if (*type != 'D') { val = dlg_editbox_get(pfd->destbox, dlg); if (!*val || !strchr(val, ':')) { dlg_error_msg(dlg, "You need to specify a destination address\n" "in the form \"host.name:port\""); sfree(src); sfree(val); return; } } else { type = "L"; val = dupstr("D"); /* special case */ } key = dupcat(family, type, src, NULL); sfree(src); if (conf_get_str_str_opt(conf, CONF_portfwd, key)) { dlg_error_msg(dlg, "Specified forwarding already exists"); } else { conf_set_str_str(conf, CONF_portfwd, key, val); } sfree(key); sfree(val); dlg_refresh(pfd->listbox, dlg); } else if (ctrl == pfd->rembutton) { int i = dlg_listbox_index(pfd->listbox, dlg); if (i < 0) { dlg_beep(dlg); } else { char *key, *val, *p; key = conf_get_str_nthstrkey(conf, CONF_portfwd, i); if (key) { static const char *const afs = "A46"; static const char *const dirs = "LRD"; char *afp; int dir; #ifndef NO_IPV6 int idx; #endif /* Populate controls with the entry we're about to delete * for ease of editing */ p = key; afp = strchr(afs, *p); #ifndef NO_IPV6 idx = afp ? afp-afs : 0; #endif if (afp) p++; #ifndef NO_IPV6 dlg_radiobutton_set(pfd->addressfamily, dlg, idx); #endif dir = *p; val = conf_get_str_str(conf, CONF_portfwd, key); if (!strcmp(val, "D")) { dir = 'D'; val = ""; } dlg_radiobutton_set(pfd->direction, dlg, strchr(dirs, dir) - dirs); p++; dlg_editbox_set(pfd->sourcebox, dlg, p); dlg_editbox_set(pfd->destbox, dlg, val); /* And delete it */ conf_del_str_str(conf, CONF_portfwd, key); } } dlg_refresh(pfd->listbox, dlg); } } } void setup_config_box(struct controlbox *b, int midsession, int protocol, int protcfginfo) { struct controlset *s; struct sessionsaver_data *ssd; struct charclass_data *ccd; struct colour_data *cd; struct ttymodes_data *td; struct environ_data *ed; struct portfwd_data *pfd; union control *c; char *str; ssd = (struct sessionsaver_data *) ctrl_alloc_with_free(b, sizeof(struct sessionsaver_data), sessionsaver_data_free); memset(ssd, 0, sizeof(*ssd)); ssd->savedsession = dupstr(""); ssd->midsession = midsession; /* * The standard panel that appears at the bottom of all panels: * Open, Cancel, Apply etc. */ s = ctrl_getset(b, "", "", ""); |
︙ | ︙ | |||
1608 1609 1610 1611 1612 1613 1614 | } else { /* Disable the Delete button mid-session too, for UI consistency. */ ssd->delbutton = NULL; } ctrl_columns(s, 1, 100); s = ctrl_getset(b, "Session", "otheropts", NULL); | | | | | | | | | 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 | } else { /* Disable the Delete button mid-session too, for UI consistency. */ ssd->delbutton = NULL; } ctrl_columns(s, 1, 100); s = ctrl_getset(b, "Session", "otheropts", NULL); ctrl_radiobuttons(s, "Close window on exit:", 'x', 4, HELPCTX(session_coe), conf_radiobutton_handler, I(CONF_close_on_exit), "Always", I(FORCE_ON), "Never", I(FORCE_OFF), "Only on clean exit", I(AUTO), NULL); /* * The Session/Logging panel. */ ctrl_settitle(b, "Session/Logging", "Options controlling session logging"); s = ctrl_getset(b, "Session/Logging", "main", NULL); |
︙ | ︙ | |||
1639 1640 1641 1642 1643 1644 1645 | } else { sshlogname = NULL; /* this will disable both buttons */ sshrawlogname = NULL; /* this will just placate optimisers */ } ctrl_radiobuttons(s, "Session logging:", NO_SHORTCUT, 2, HELPCTX(logging_main), loggingbuttons_handler, | | | | | | | | | | | | | | < | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | < | | | | | | | | | | | 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 | } else { sshlogname = NULL; /* this will disable both buttons */ sshrawlogname = NULL; /* this will just placate optimisers */ } ctrl_radiobuttons(s, "Session logging:", NO_SHORTCUT, 2, HELPCTX(logging_main), loggingbuttons_handler, I(CONF_logtype), "None", 't', I(LGTYP_NONE), "Printable output", 'p', I(LGTYP_ASCII), "All session output", 'l', I(LGTYP_DEBUG), sshlogname, 's', I(LGTYP_PACKETS), sshrawlogname, 'r', I(LGTYP_SSHRAW), NULL); } ctrl_filesel(s, "Log file name:", 'f', NULL, TRUE, "Select session log file name", HELPCTX(logging_filename), conf_filesel_handler, I(CONF_logfilename)); ctrl_text(s, "(Log file name can contain &Y, &M, &D for date," " &T for time, and &H for host name)", HELPCTX(logging_filename)); ctrl_radiobuttons(s, "What to do if the log file already exists:", 'e', 1, HELPCTX(logging_exists), conf_radiobutton_handler, I(CONF_logxfovr), "Always overwrite it", I(LGXF_OVR), "Always append to the end of it", I(LGXF_APN), "Ask the user every time", I(LGXF_ASK), NULL); ctrl_checkbox(s, "Flush log file frequently", 'u', HELPCTX(logging_flush), conf_checkbox_handler, I(CONF_logflush)); if ((midsession && protocol == 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', HELPCTX(logging_ssh_omit_password), conf_checkbox_handler, I(CONF_logomitpass)); ctrl_checkbox(s, "Omit session data", 'd', HELPCTX(logging_ssh_omit_data), conf_checkbox_handler, I(CONF_logomitdata)); } /* * The Terminal panel. */ ctrl_settitle(b, "Terminal", "Options controlling the terminal emulation"); s = ctrl_getset(b, "Terminal", "general", "Set various terminal options"); ctrl_checkbox(s, "Auto wrap mode initially on", 'w', HELPCTX(terminal_autowrap), conf_checkbox_handler, I(CONF_wrap_mode)); ctrl_checkbox(s, "DEC Origin Mode initially on", 'd', HELPCTX(terminal_decom), conf_checkbox_handler, I(CONF_dec_om)); ctrl_checkbox(s, "Implicit CR in every LF", 'r', HELPCTX(terminal_lfhascr), conf_checkbox_handler, I(CONF_lfhascr)); ctrl_checkbox(s, "Implicit LF in every CR", 'f', HELPCTX(terminal_crhaslf), conf_checkbox_handler, I(CONF_crhaslf)); ctrl_checkbox(s, "Use background colour to erase screen", 'e', HELPCTX(terminal_bce), conf_checkbox_handler, I(CONF_bce)); ctrl_checkbox(s, "Enable blinking text", 'n', HELPCTX(terminal_blink), conf_checkbox_handler, I(CONF_blinktext)); ctrl_editbox(s, "Answerback to ^E:", 's', 100, HELPCTX(terminal_answerback), conf_editbox_handler, I(CONF_answerback), I(1)); s = ctrl_getset(b, "Terminal", "ldisc", "Line discipline options"); ctrl_radiobuttons(s, "Local echo:", 'l', 3, HELPCTX(terminal_localecho), conf_radiobutton_handler,I(CONF_localecho), "Auto", I(AUTO), "Force on", I(FORCE_ON), "Force off", I(FORCE_OFF), NULL); ctrl_radiobuttons(s, "Local line editing:", 't', 3, HELPCTX(terminal_localedit), conf_radiobutton_handler,I(CONF_localedit), "Auto", I(AUTO), "Force on", I(FORCE_ON), "Force off", I(FORCE_OFF), NULL); s = ctrl_getset(b, "Terminal", "printing", "Remote-controlled printing"); ctrl_combobox(s, "Printer to send ANSI printer output to:", 'p', 100, HELPCTX(terminal_printing), printerbox_handler, P(NULL), P(NULL)); /* * The Terminal/Keyboard panel. */ ctrl_settitle(b, "Terminal/Keyboard", "Options controlling the effects of keys"); s = ctrl_getset(b, "Terminal/Keyboard", "mappings", "Change the sequences sent by:"); ctrl_radiobuttons(s, "The Backspace key", 'b', 2, HELPCTX(keyboard_backspace), conf_radiobutton_handler, I(CONF_bksp_is_delete), "Control-H", I(0), "Control-? (127)", I(1), NULL); ctrl_radiobuttons(s, "The Home and End keys", 'e', 2, HELPCTX(keyboard_homeend), conf_radiobutton_handler, I(CONF_rxvt_homeend), "Standard", I(0), "rxvt", I(1), NULL); ctrl_radiobuttons(s, "The Function keys and keypad", 'f', 3, HELPCTX(keyboard_funkeys), conf_radiobutton_handler, I(CONF_funky_type), "ESC[n~", I(0), "Linux", I(1), "Xterm R6", I(2), "VT400", I(3), "VT100+", I(4), "SCO", I(5), NULL); s = ctrl_getset(b, "Terminal/Keyboard", "appkeypad", "Application keypad settings:"); ctrl_radiobuttons(s, "Initial state of cursor keys:", 'r', 3, HELPCTX(keyboard_appcursor), conf_radiobutton_handler, I(CONF_app_cursor), "Normal", I(0), "Application", I(1), NULL); ctrl_radiobuttons(s, "Initial state of numeric keypad:", 'n', 3, HELPCTX(keyboard_appkeypad), numeric_keypad_handler, P(NULL), "Normal", I(0), "Application", I(1), "NetHack", I(2), NULL); /* * The Terminal/Bell panel. */ ctrl_settitle(b, "Terminal/Bell", "Options controlling the terminal bell"); s = ctrl_getset(b, "Terminal/Bell", "style", "Set the style of bell"); ctrl_radiobuttons(s, "Action to happen when a bell occurs:", 'b', 1, HELPCTX(bell_style), conf_radiobutton_handler, I(CONF_beep), "None (bell disabled)", I(BELL_DISABLED), "Make default system alert sound", I(BELL_DEFAULT), "Visual bell (flash window)", I(BELL_VISUAL), NULL); s = ctrl_getset(b, "Terminal/Bell", "overload", "Control the bell overload behaviour"); ctrl_checkbox(s, "Bell is temporarily disabled when over-used", 'd', HELPCTX(bell_overload), conf_checkbox_handler, I(CONF_bellovl)); ctrl_editbox(s, "Over-use means this many bells...", 'm', 20, HELPCTX(bell_overload), conf_editbox_handler, I(CONF_bellovl_n), I(-1)); ctrl_editbox(s, "... in this many seconds", 't', 20, HELPCTX(bell_overload), conf_editbox_handler, I(CONF_bellovl_t), I(-TICKSPERSEC)); ctrl_text(s, "The bell is re-enabled after a few seconds of silence.", HELPCTX(bell_overload)); ctrl_editbox(s, "Seconds of silence required", 's', 20, HELPCTX(bell_overload), conf_editbox_handler, I(CONF_bellovl_s), I(-TICKSPERSEC)); /* * The Terminal/Features panel. */ ctrl_settitle(b, "Terminal/Features", "Enabling and disabling advanced terminal features"); s = ctrl_getset(b, "Terminal/Features", "main", NULL); ctrl_checkbox(s, "Disable application cursor keys mode", 'u', HELPCTX(features_application), conf_checkbox_handler, I(CONF_no_applic_c)); ctrl_checkbox(s, "Disable application keypad mode", 'k', HELPCTX(features_application), conf_checkbox_handler, I(CONF_no_applic_k)); ctrl_checkbox(s, "Disable xterm-style mouse reporting", 'x', HELPCTX(features_mouse), conf_checkbox_handler, I(CONF_no_mouse_rep)); ctrl_checkbox(s, "Disable remote-controlled terminal resizing", 's', HELPCTX(features_resize), conf_checkbox_handler, I(CONF_no_remote_resize)); ctrl_checkbox(s, "Disable switching to alternate terminal screen", 'w', HELPCTX(features_altscreen), conf_checkbox_handler, I(CONF_no_alt_screen)); ctrl_checkbox(s, "Disable remote-controlled window title changing", 't', HELPCTX(features_retitle), conf_checkbox_handler, I(CONF_no_remote_wintitle)); ctrl_radiobuttons(s, "Response to remote title query (SECURITY):", 'q', 3, HELPCTX(features_qtitle), conf_radiobutton_handler, I(CONF_remote_qtitle_action), "None", I(TITLE_NONE), "Empty string", I(TITLE_EMPTY), "Window title", I(TITLE_REAL), NULL); ctrl_checkbox(s, "Disable destructive backspace on server sending ^?",'b', HELPCTX(features_dbackspace), conf_checkbox_handler, I(CONF_no_dbackspace)); ctrl_checkbox(s, "Disable remote-controlled character set configuration", 'r', HELPCTX(features_charset), conf_checkbox_handler, I(CONF_no_remote_charset)); ctrl_checkbox(s, "Disable Arabic text shaping", 'l', HELPCTX(features_arabicshaping), conf_checkbox_handler, I(CONF_arabicshaping)); ctrl_checkbox(s, "Disable bidirectional text display", 'd', HELPCTX(features_bidi), conf_checkbox_handler, I(CONF_bidi)); /* * The Window panel. */ str = dupprintf("Options controlling %s's window", appname); ctrl_settitle(b, "Window", str); sfree(str); s = ctrl_getset(b, "Window", "size", "Set the size of the window"); ctrl_columns(s, 2, 50, 50); c = ctrl_editbox(s, "Columns", 'm', 100, HELPCTX(window_size), conf_editbox_handler, I(CONF_width), I(-1)); c->generic.column = 0; c = ctrl_editbox(s, "Rows", 'r', 100, HELPCTX(window_size), conf_editbox_handler, I(CONF_height),I(-1)); c->generic.column = 1; ctrl_columns(s, 1, 100); s = ctrl_getset(b, "Window", "scrollback", "Control the scrollback in the window"); ctrl_editbox(s, "Lines of scrollback", 's', 50, HELPCTX(window_scrollback), conf_editbox_handler, I(CONF_savelines), I(-1)); ctrl_checkbox(s, "Display scrollbar", 'd', HELPCTX(window_scrollback), conf_checkbox_handler, I(CONF_scrollbar)); ctrl_checkbox(s, "Reset scrollback on keypress", 'k', HELPCTX(window_scrollback), conf_checkbox_handler, I(CONF_scroll_on_key)); ctrl_checkbox(s, "Reset scrollback on display activity", 'p', HELPCTX(window_scrollback), conf_checkbox_handler, I(CONF_scroll_on_disp)); ctrl_checkbox(s, "Push erased text into scrollback", 'e', HELPCTX(window_erased), conf_checkbox_handler, I(CONF_erase_to_scrollback)); /* * The Window/Appearance panel. */ str = dupprintf("Configure the appearance of %s's window", appname); ctrl_settitle(b, "Window/Appearance", str); sfree(str); s = ctrl_getset(b, "Window/Appearance", "cursor", "Adjust the use of the cursor"); ctrl_radiobuttons(s, "Cursor appearance:", NO_SHORTCUT, 3, HELPCTX(appearance_cursor), conf_radiobutton_handler, I(CONF_cursor_type), "Block", 'l', I(0), "Underline", 'u', I(1), "Vertical line", 'v', I(2), NULL); ctrl_checkbox(s, "Cursor blinks", 'b', HELPCTX(appearance_cursor), conf_checkbox_handler, I(CONF_blink_cur)); s = ctrl_getset(b, "Window/Appearance", "font", "Font settings"); ctrl_fontsel(s, "Font used in the terminal window", 'n', HELPCTX(appearance_font), conf_fontsel_handler, I(CONF_font)); s = ctrl_getset(b, "Window/Appearance", "mouse", "Adjust the use of the mouse pointer"); ctrl_checkbox(s, "Hide mouse pointer when typing in window", 'p', HELPCTX(appearance_hidemouse), conf_checkbox_handler, I(CONF_hide_mouseptr)); s = ctrl_getset(b, "Window/Appearance", "border", "Adjust the window border"); ctrl_editbox(s, "Gap between text and window edge:", 'e', 20, HELPCTX(appearance_border), conf_editbox_handler, I(CONF_window_border), I(-1)); /* * The Window/Behaviour panel. */ str = dupprintf("Configure the behaviour of %s's window", appname); ctrl_settitle(b, "Window/Behaviour", str); sfree(str); s = ctrl_getset(b, "Window/Behaviour", "title", "Adjust the behaviour of the window title"); ctrl_editbox(s, "Window title:", 't', 100, HELPCTX(appearance_title), conf_editbox_handler, I(CONF_wintitle), I(1)); ctrl_checkbox(s, "Separate window and icon titles", 'i', HELPCTX(appearance_title), conf_checkbox_handler, I(CHECKBOX_INVERT | CONF_win_name_always)); s = ctrl_getset(b, "Window/Behaviour", "main", NULL); ctrl_checkbox(s, "Warn before closing window", 'w', HELPCTX(behaviour_closewarn), conf_checkbox_handler, I(CONF_warn_on_close)); /* * The Window/Translation panel. */ ctrl_settitle(b, "Window/Translation", "Options controlling character set translation"); s = ctrl_getset(b, "Window/Translation", "trans", "Character set translation"); ctrl_combobox(s, "Remote character set:", 'r', 100, HELPCTX(translation_codepage), codepage_handler, P(NULL), P(NULL)); s = ctrl_getset(b, "Window/Translation", "tweaks", NULL); ctrl_checkbox(s, "Treat CJK ambiguous characters as wide", 'w', HELPCTX(translation_cjk_ambig_wide), conf_checkbox_handler, I(CONF_cjk_ambig_wide)); str = dupprintf("Adjust how %s handles line drawing characters", appname); s = ctrl_getset(b, "Window/Translation", "linedraw", str); sfree(str); ctrl_radiobuttons(s, "Handling of line drawing characters:", NO_SHORTCUT,1, HELPCTX(translation_linedraw), conf_radiobutton_handler, I(CONF_vtmode), "Use Unicode line drawing code points",'u',I(VT_UNICODE), "Poor man's line drawing (+, - and |)",'p',I(VT_POORMAN), NULL); ctrl_checkbox(s, "Copy and paste line drawing characters as lqqqk",'d', HELPCTX(selection_linedraw), conf_checkbox_handler, I(CONF_rawcnp)); /* * The Window/Selection panel. */ ctrl_settitle(b, "Window/Selection", "Options controlling copy and paste"); s = ctrl_getset(b, "Window/Selection", "mouse", "Control use of mouse"); ctrl_checkbox(s, "Shift overrides application's use of mouse", 'p', HELPCTX(selection_shiftdrag), conf_checkbox_handler, I(CONF_mouse_override)); ctrl_radiobuttons(s, "Default selection mode (Alt+drag does the other one):", NO_SHORTCUT, 2, HELPCTX(selection_rect), conf_radiobutton_handler, I(CONF_rect_select), "Normal", 'n', I(0), "Rectangular block", 'r', I(1), NULL); s = ctrl_getset(b, "Window/Selection", "charclass", "Control the select-one-word-at-a-time mode"); ccd = (struct charclass_data *) ctrl_alloc(b, sizeof(struct charclass_data)); |
︙ | ︙ | |||
2026 2027 2028 2029 2030 2031 2032 | */ ctrl_settitle(b, "Window/Colours", "Options controlling use of colours"); s = ctrl_getset(b, "Window/Colours", "general", "General options for colour usage"); ctrl_checkbox(s, "Allow terminal to specify ANSI colours", 'i', HELPCTX(colours_ansi), | | | | | | | > > > > | 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 | */ ctrl_settitle(b, "Window/Colours", "Options controlling use of colours"); s = ctrl_getset(b, "Window/Colours", "general", "General options for colour usage"); ctrl_checkbox(s, "Allow terminal to specify ANSI colours", 'i', HELPCTX(colours_ansi), conf_checkbox_handler, I(CONF_ansi_colour)); ctrl_checkbox(s, "Allow terminal to use xterm 256-colour mode", '2', HELPCTX(colours_xterm256), conf_checkbox_handler, I(CONF_xterm_256_colour)); ctrl_radiobuttons(s, "Indicate bolded text by changing:", 'b', 3, HELPCTX(colours_bold), conf_radiobutton_handler, I(CONF_bold_style), "The font", I(1), "The colour", I(2), "Both", I(3), NULL); str = dupprintf("Adjust the precise colours %s displays", appname); s = ctrl_getset(b, "Window/Colours", "adjust", str); sfree(str); ctrl_text(s, "Select a colour from the list, and then click the" " Modify button to change its appearance.", HELPCTX(colours_config)); |
︙ | ︙ | |||
2074 2075 2076 2077 2078 2079 2080 | if (protocol >= 0) { ctrl_settitle(b, "Connection", "Options controlling the connection"); s = ctrl_getset(b, "Connection", "keepalive", "Sending of null packets to keep session active"); ctrl_editbox(s, "Seconds between keepalives (0 to turn off)", 'k', 20, HELPCTX(connection_keepalive), | | | | | | | | | < | < | | | < | < | 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 | if (protocol >= 0) { ctrl_settitle(b, "Connection", "Options controlling the connection"); s = ctrl_getset(b, "Connection", "keepalive", "Sending of null packets to keep session active"); ctrl_editbox(s, "Seconds between keepalives (0 to turn off)", 'k', 20, HELPCTX(connection_keepalive), conf_editbox_handler, I(CONF_ping_interval), I(-1)); if (!midsession) { s = ctrl_getset(b, "Connection", "tcp", "Low-level TCP connection options"); ctrl_checkbox(s, "Disable Nagle's algorithm (TCP_NODELAY option)", 'n', HELPCTX(connection_nodelay), conf_checkbox_handler, I(CONF_tcp_nodelay)); ctrl_checkbox(s, "Enable TCP keepalives (SO_KEEPALIVE option)", 'p', HELPCTX(connection_tcpkeepalive), conf_checkbox_handler, I(CONF_tcp_keepalives)); #ifndef NO_IPV6 s = ctrl_getset(b, "Connection", "ipversion", "Internet protocol version"); ctrl_radiobuttons(s, NULL, NO_SHORTCUT, 3, HELPCTX(connection_ipversion), conf_radiobutton_handler, I(CONF_addressfamily), "Auto", 'u', I(ADDRTYPE_UNSPEC), "IPv4", '4', I(ADDRTYPE_IPV4), "IPv6", '6', I(ADDRTYPE_IPV6), NULL); #endif { char *label = backend_from_proto(PROT_SSH) ? "Logical name of remote host (e.g. for SSH key lookup):" : "Logical name of remote host:"; s = ctrl_getset(b, "Connection", "identity", "Logical name of remote host"); ctrl_editbox(s, label, 'm', 100, HELPCTX(connection_loghost), conf_editbox_handler, I(CONF_loghost), I(1)); } } /* * A sub-panel Connection/Data, containing options that * decide on data to send to the server. */ if (!midsession) { ctrl_settitle(b, "Connection/Data", "Data to send to the server"); s = ctrl_getset(b, "Connection/Data", "login", "Login details"); ctrl_editbox(s, "Auto-login username", 'u', 50, HELPCTX(connection_username), conf_editbox_handler, I(CONF_username), I(1)); { /* We assume the local username is sufficiently stable * to include on the dialog box. */ char *user = get_username(); char *userlabel = dupprintf("Use system username (%s)", user ? user : ""); sfree(user); ctrl_radiobuttons(s, "When username is not specified:", 'n', 4, HELPCTX(connection_username_from_env), conf_radiobutton_handler, I(CONF_username_from_env), "Prompt", I(FALSE), userlabel, I(TRUE), NULL); sfree(userlabel); } s = ctrl_getset(b, "Connection/Data", "term", "Terminal details"); ctrl_editbox(s, "Terminal-type string", 't', 50, HELPCTX(connection_termtype), conf_editbox_handler, I(CONF_termtype), I(1)); ctrl_editbox(s, "Terminal speeds", 's', 50, HELPCTX(connection_termspeed), conf_editbox_handler, I(CONF_termspeed), I(1)); s = ctrl_getset(b, "Connection/Data", "env", "Environment variables"); ctrl_columns(s, 2, 80, 20); ed = (struct environ_data *) ctrl_alloc(b, sizeof(struct environ_data)); ed->varbox = ctrl_editbox(s, "Variable", 'v', 60, |
︙ | ︙ | |||
2199 2200 2201 2202 2203 2204 2205 | */ ctrl_settitle(b, "Connection/Proxy", "Options controlling proxy usage"); s = ctrl_getset(b, "Connection/Proxy", "basics", NULL); ctrl_radiobuttons(s, "Proxy type:", 't', 3, HELPCTX(proxy_type), | | | | | < | | | | < | | | | | | < | | < | | < | | | | | | | | | < | 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 2060 2061 | */ ctrl_settitle(b, "Connection/Proxy", "Options controlling proxy usage"); s = ctrl_getset(b, "Connection/Proxy", "basics", NULL); ctrl_radiobuttons(s, "Proxy type:", 't', 3, HELPCTX(proxy_type), conf_radiobutton_handler, I(CONF_proxy_type), "None", I(PROXY_NONE), "SOCKS 4", I(PROXY_SOCKS4), "SOCKS 5", I(PROXY_SOCKS5), "HTTP", I(PROXY_HTTP), "Telnet", I(PROXY_TELNET), NULL); ctrl_columns(s, 2, 80, 20); c = ctrl_editbox(s, "Proxy hostname", 'y', 100, HELPCTX(proxy_main), conf_editbox_handler, I(CONF_proxy_host), I(1)); c->generic.column = 0; c = ctrl_editbox(s, "Port", 'p', 100, HELPCTX(proxy_main), conf_editbox_handler, I(CONF_proxy_port), I(-1)); c->generic.column = 1; ctrl_columns(s, 1, 100); ctrl_editbox(s, "Exclude Hosts/IPs", 'e', 100, HELPCTX(proxy_exclude), conf_editbox_handler, I(CONF_proxy_exclude_list), I(1)); ctrl_checkbox(s, "Consider proxying local host connections", 'x', HELPCTX(proxy_exclude), conf_checkbox_handler, I(CONF_even_proxy_localhost)); ctrl_radiobuttons(s, "Do DNS name lookup at proxy end:", 'd', 3, HELPCTX(proxy_dns), conf_radiobutton_handler, I(CONF_proxy_dns), "No", I(FORCE_OFF), "Auto", I(AUTO), "Yes", I(FORCE_ON), NULL); ctrl_editbox(s, "Username", 'u', 60, HELPCTX(proxy_auth), conf_editbox_handler, I(CONF_proxy_username), I(1)); c = ctrl_editbox(s, "Password", 'w', 60, HELPCTX(proxy_auth), conf_editbox_handler, I(CONF_proxy_password), I(1)); c->editbox.password = 1; ctrl_editbox(s, "Telnet command", 'm', 100, HELPCTX(proxy_command), conf_editbox_handler, I(CONF_proxy_telnet_command), I(1)); } /* * The Telnet panel exists in the base config box, and in a * mid-session reconfig box _if_ we're using Telnet. */ if (!midsession || protocol == PROT_TELNET) { /* * The Connection/Telnet panel. */ ctrl_settitle(b, "Connection/Telnet", "Options controlling Telnet connections"); s = ctrl_getset(b, "Connection/Telnet", "protocol", "Telnet protocol adjustments"); if (!midsession) { ctrl_radiobuttons(s, "Handling of OLD_ENVIRON ambiguity:", NO_SHORTCUT, 2, HELPCTX(telnet_oldenviron), conf_radiobutton_handler, I(CONF_rfc_environ), "BSD (commonplace)", 'b', I(0), "RFC 1408 (unusual)", 'f', I(1), NULL); ctrl_radiobuttons(s, "Telnet negotiation mode:", 't', 2, HELPCTX(telnet_passive), conf_radiobutton_handler, I(CONF_passive_telnet), "Passive", I(1), "Active", I(0), NULL); } ctrl_checkbox(s, "Keyboard sends Telnet special commands", 'k', HELPCTX(telnet_specialkeys), conf_checkbox_handler, I(CONF_telnet_keyboard)); ctrl_checkbox(s, "Return key sends Telnet New Line instead of ^M", 'm', HELPCTX(telnet_newline), conf_checkbox_handler, I(CONF_telnet_newline)); } if (!midsession) { /* * The Connection/Rlogin panel. */ ctrl_settitle(b, "Connection/Rlogin", "Options controlling Rlogin connections"); s = ctrl_getset(b, "Connection/Rlogin", "data", "Data to send to the server"); ctrl_editbox(s, "Local username:", 'l', 50, HELPCTX(rlogin_localuser), conf_editbox_handler, I(CONF_localusername), I(1)); } /* * All the SSH stuff is omitted in PuTTYtel, or in a reconfig * when we're not doing SSH. */ |
︙ | ︙ | |||
2336 2337 2338 2339 2340 2341 2342 | if (!midsession) { s = ctrl_getset(b, "Connection/SSH", "data", "Data to send to the server"); ctrl_editbox(s, "Remote command:", 'r', 100, HELPCTX(ssh_command), | | < | | | | > > > > > > > > > > > > > > > > > > > > | | < < < < < < < < < < < < < | 2077 2078 2079 2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 2104 2105 2106 2107 2108 2109 2110 2111 2112 2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 2124 2125 2126 2127 2128 2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141 | if (!midsession) { s = ctrl_getset(b, "Connection/SSH", "data", "Data to send to the server"); ctrl_editbox(s, "Remote command:", 'r', 100, HELPCTX(ssh_command), conf_editbox_handler, I(CONF_remote_cmd), I(1)); s = ctrl_getset(b, "Connection/SSH", "protocol", "Protocol options"); ctrl_checkbox(s, "Don't start a shell or command at all", 'n', HELPCTX(ssh_noshell), conf_checkbox_handler, I(CONF_ssh_no_shell)); } if (!midsession || protcfginfo != 1) { s = ctrl_getset(b, "Connection/SSH", "protocol", "Protocol options"); ctrl_checkbox(s, "Enable compression", 'e', HELPCTX(ssh_compress), conf_checkbox_handler, I(CONF_compression)); } if (!midsession || protcfginfo != 1) { s = ctrl_getset(b, "Connection/SSH", "sharing", "Sharing an SSH connection between PuTTY tools"); ctrl_checkbox(s, "Share SSH connections if possible", 's', HELPCTX(ssh_share), conf_checkbox_handler, I(CONF_ssh_connection_sharing)); ctrl_text(s, "Permitted roles in a shared connection:", HELPCTX(ssh_share)); ctrl_checkbox(s, "Upstream (connecting to the real server)", 'u', HELPCTX(ssh_share), conf_checkbox_handler, I(CONF_ssh_connection_sharing_upstream)); ctrl_checkbox(s, "Downstream (connecting to the upstream PuTTY)", 'd', HELPCTX(ssh_share), conf_checkbox_handler, I(CONF_ssh_connection_sharing_downstream)); } if (!midsession) { s = ctrl_getset(b, "Connection/SSH", "protocol", "Protocol options"); ctrl_radiobuttons(s, "Preferred SSH protocol version:", NO_SHORTCUT, 4, HELPCTX(ssh_protocol), conf_radiobutton_handler, I(CONF_sshprot), "1 only", 'l', I(0), "1", '1', I(1), "2", '2', I(2), "2 only", 'y', I(3), NULL); } /* * The Connection/SSH/Kex panel. (Owing to repeat key * exchange, this is all meaningful in mid-session _if_ * we're using SSH-2 or haven't decided yet.) */ if (protcfginfo != 1) { ctrl_settitle(b, "Connection/SSH/Kex", |
︙ | ︙ | |||
2402 2403 2404 2405 2406 2407 2408 | c->listbox.height = 5; s = ctrl_getset(b, "Connection/SSH/Kex", "repeat", "Options controlling key re-exchange"); ctrl_editbox(s, "Max minutes before rekey (0 for no limit)", 't', 20, HELPCTX(ssh_kex_repeat), | | | | | | < | | | | | | | | < < < < < < < < | < < < < | < < < < < < | < < < | < < < < < < < < < < < < < < < < < < < < < < < | < < < < < < | < < < < | | | < < < < < < < < < < < | < < | | | | | | | | | | | | | | | | | | | 2149 2150 2151 2152 2153 2154 2155 2156 2157 2158 2159 2160 2161 2162 2163 2164 2165 2166 2167 2168 2169 2170 2171 2172 2173 2174 2175 2176 2177 2178 2179 2180 2181 2182 2183 2184 2185 2186 2187 2188 2189 2190 2191 2192 2193 2194 2195 2196 2197 2198 2199 2200 2201 2202 2203 2204 2205 2206 2207 2208 2209 2210 2211 2212 2213 2214 2215 2216 2217 2218 2219 2220 2221 2222 2223 2224 2225 2226 2227 2228 2229 2230 2231 2232 2233 2234 2235 2236 2237 2238 2239 2240 2241 2242 2243 2244 2245 2246 2247 2248 2249 2250 2251 2252 2253 2254 2255 2256 2257 2258 2259 | c->listbox.height = 5; s = ctrl_getset(b, "Connection/SSH/Kex", "repeat", "Options controlling key re-exchange"); ctrl_editbox(s, "Max minutes before rekey (0 for no limit)", 't', 20, HELPCTX(ssh_kex_repeat), conf_editbox_handler, I(CONF_ssh_rekey_time), I(-1)); ctrl_editbox(s, "Max data before rekey (0 for no limit)", 'x', 20, HELPCTX(ssh_kex_repeat), conf_editbox_handler, I(CONF_ssh_rekey_data), I(16)); ctrl_text(s, "(Use 1M for 1 megabyte, 1G for 1 gigabyte etc)", HELPCTX(ssh_kex_repeat)); } if (!midsession || protcfginfo != 1) { /* * The Connection/SSH/Cipher panel. */ ctrl_settitle(b, "Connection/SSH/Cipher", "Options controlling SSH encryption"); s = ctrl_getset(b, "Connection/SSH/Cipher", "encryption", "Encryption options"); c = ctrl_draglist(s, "Encryption cipher selection policy:", 's', HELPCTX(ssh_ciphers), cipherlist_handler, P(NULL)); c->listbox.height = 6; ctrl_checkbox(s, "Enable legacy use of single-DES in SSH-2", 'i', HELPCTX(ssh_ciphers), conf_checkbox_handler, I(CONF_ssh2_des_cbc)); } if (!midsession) { /* * The Connection/SSH/Auth panel. */ ctrl_settitle(b, "Connection/SSH/Auth", "Options controlling SSH authentication"); s = ctrl_getset(b, "Connection/SSH/Auth", "main", NULL); ctrl_checkbox(s, "Bypass authentication entirely (SSH-2 only)", 'b', HELPCTX(ssh_auth_bypass), conf_checkbox_handler, I(CONF_ssh_no_userauth)); ctrl_checkbox(s, "Display pre-authentication banner (SSH-2 only)", 'd', HELPCTX(ssh_auth_banner), conf_checkbox_handler, I(CONF_ssh_show_banner)); s = ctrl_getset(b, "Connection/SSH/Auth", "methods", "Authentication methods"); ctrl_checkbox(s, "Attempt authentication using Pageant", 'p', HELPCTX(ssh_auth_pageant), conf_checkbox_handler, I(CONF_tryagent)); ctrl_checkbox(s, "Attempt TIS or CryptoCard auth (SSH-1)", 'm', HELPCTX(ssh_auth_tis), conf_checkbox_handler, I(CONF_try_tis_auth)); ctrl_checkbox(s, "Attempt \"keyboard-interactive\" auth (SSH-2)", 'i', HELPCTX(ssh_auth_ki), conf_checkbox_handler, I(CONF_try_ki_auth)); s = ctrl_getset(b, "Connection/SSH/Auth", "params", "Authentication parameters"); ctrl_checkbox(s, "Allow agent forwarding", 'f', HELPCTX(ssh_auth_agentfwd), conf_checkbox_handler, I(CONF_agentfwd)); ctrl_checkbox(s, "Allow attempted changes of username in SSH-2", NO_SHORTCUT, HELPCTX(ssh_auth_changeuser), conf_checkbox_handler, I(CONF_change_username)); ctrl_filesel(s, "Private key file for authentication:", 'k', FILTER_KEY_FILES, FALSE, "Select private key file", HELPCTX(ssh_auth_privkey), conf_filesel_handler, I(CONF_keyfile)); #ifndef NO_GSSAPI /* * Connection/SSH/Auth/GSSAPI, which sadly won't fit on * the main Auth panel. */ ctrl_settitle(b, "Connection/SSH/Auth/GSSAPI", "Options controlling GSSAPI authentication"); s = ctrl_getset(b, "Connection/SSH/Auth/GSSAPI", "gssapi", NULL); ctrl_checkbox(s, "Attempt GSSAPI authentication (SSH-2 only)", 't', HELPCTX(ssh_gssapi), conf_checkbox_handler, I(CONF_try_gssapi_auth)); ctrl_checkbox(s, "Allow GSSAPI credential delegation", 'l', HELPCTX(ssh_gssapi_delegation), conf_checkbox_handler, I(CONF_gssapifwd)); /* * GSSAPI library selection. */ if (ngsslibs > 1) { c = ctrl_draglist(s, "Preference order for GSSAPI libraries:", 'p', HELPCTX(ssh_gssapi_libraries), |
︙ | ︙ | |||
2599 2600 2601 2602 2603 2604 2605 | * ngsslibs to control whether the file selector is * displayed. */ ctrl_filesel(s, "User-supplied GSSAPI library path:", 's', FILTER_DYNLIB_FILES, FALSE, "Select library file", HELPCTX(ssh_gssapi_libraries), | | | | | | 2278 2279 2280 2281 2282 2283 2284 2285 2286 2287 2288 2289 2290 2291 2292 2293 2294 2295 2296 2297 2298 2299 2300 2301 2302 2303 2304 2305 2306 2307 2308 | * ngsslibs to control whether the file selector is * displayed. */ ctrl_filesel(s, "User-supplied GSSAPI library path:", 's', FILTER_DYNLIB_FILES, FALSE, "Select library file", HELPCTX(ssh_gssapi_libraries), conf_filesel_handler, I(CONF_ssh_gss_custom)); } #endif } if (!midsession) { /* * The Connection/SSH/TTY panel. */ ctrl_settitle(b, "Connection/SSH/TTY", "Remote terminal settings"); s = ctrl_getset(b, "Connection/SSH/TTY", "sshtty", NULL); ctrl_checkbox(s, "Don't allocate a pseudo-terminal", 'p', HELPCTX(ssh_nopty), conf_checkbox_handler, I(CONF_nopty)); s = ctrl_getset(b, "Connection/SSH/TTY", "ttymodes", "Terminal modes"); td = (struct ttymodes_data *) ctrl_alloc(b, sizeof(struct ttymodes_data)); ctrl_columns(s, 2, 75, 25); c = ctrl_text(s, "Terminal modes to send:", HELPCTX(ssh_ttymodes)); |
︙ | ︙ | |||
2681 2682 2683 2684 2685 2686 2687 | */ ctrl_settitle(b, "Connection/SSH/X11", "Options controlling SSH X11 forwarding"); s = ctrl_getset(b, "Connection/SSH/X11", "x11", "X11 forwarding"); ctrl_checkbox(s, "Enable X11 forwarding", 'e', HELPCTX(ssh_tunnels_x11), | | | < | | | | | | | 2360 2361 2362 2363 2364 2365 2366 2367 2368 2369 2370 2371 2372 2373 2374 2375 2376 2377 2378 2379 2380 2381 2382 2383 2384 2385 2386 2387 2388 2389 2390 2391 2392 2393 2394 2395 2396 2397 2398 2399 2400 2401 | */ ctrl_settitle(b, "Connection/SSH/X11", "Options controlling SSH X11 forwarding"); s = ctrl_getset(b, "Connection/SSH/X11", "x11", "X11 forwarding"); ctrl_checkbox(s, "Enable X11 forwarding", 'e', HELPCTX(ssh_tunnels_x11), conf_checkbox_handler,I(CONF_x11_forward)); ctrl_editbox(s, "X display location", 'x', 50, HELPCTX(ssh_tunnels_x11), conf_editbox_handler, I(CONF_x11_display), I(1)); ctrl_radiobuttons(s, "Remote X11 authentication protocol", 'u', 2, HELPCTX(ssh_tunnels_x11auth), conf_radiobutton_handler, I(CONF_x11_auth), "MIT-Magic-Cookie-1", I(X11_MIT), "XDM-Authorization-1", I(X11_XDM), NULL); } /* * The Tunnels panel _is_ still available in mid-session. */ ctrl_settitle(b, "Connection/SSH/Tunnels", "Options controlling SSH port forwarding"); s = ctrl_getset(b, "Connection/SSH/Tunnels", "portfwd", "Port forwarding"); ctrl_checkbox(s, "Local ports accept connections from other hosts",'t', HELPCTX(ssh_tunnels_portfwd_localhost), conf_checkbox_handler, I(CONF_lport_acceptall)); ctrl_checkbox(s, "Remote ports do the same (SSH-2 only)", 'p', HELPCTX(ssh_tunnels_portfwd_localhost), conf_checkbox_handler, I(CONF_rport_acceptall)); ctrl_columns(s, 3, 55, 20, 25); c = ctrl_text(s, "Forwarded ports:", HELPCTX(ssh_tunnels_portfwd)); c->generic.column = COLUMN_FIELD(0,2); /* You want to select from the list, _then_ hit Remove. So tab order * should be that way round. */ pfd = (struct portfwd_data *)ctrl_alloc(b,sizeof(struct portfwd_data)); |
︙ | ︙ | |||
2777 2778 2779 2780 2781 2782 2783 | ctrl_settitle(b, "Connection/SSH/Bugs", "Workarounds for SSH server bugs"); s = ctrl_getset(b, "Connection/SSH/Bugs", "main", "Detection of known bugs in SSH servers"); ctrl_droplist(s, "Chokes on SSH-1 ignore messages", 'i', 20, HELPCTX(ssh_bugs_ignore1), | | | | | > > > | | | | | | | 2455 2456 2457 2458 2459 2460 2461 2462 2463 2464 2465 2466 2467 2468 2469 2470 2471 2472 2473 2474 2475 2476 2477 2478 2479 2480 2481 2482 2483 2484 2485 2486 2487 2488 2489 2490 2491 2492 2493 2494 2495 | ctrl_settitle(b, "Connection/SSH/Bugs", "Workarounds for SSH server bugs"); s = ctrl_getset(b, "Connection/SSH/Bugs", "main", "Detection of known bugs in SSH servers"); ctrl_droplist(s, "Chokes on SSH-1 ignore messages", 'i', 20, HELPCTX(ssh_bugs_ignore1), sshbug_handler, I(CONF_sshbug_ignore1)); ctrl_droplist(s, "Refuses all SSH-1 password camouflage", 's', 20, HELPCTX(ssh_bugs_plainpw1), sshbug_handler, I(CONF_sshbug_plainpw1)); ctrl_droplist(s, "Chokes on SSH-1 RSA authentication", 'r', 20, HELPCTX(ssh_bugs_rsa1), sshbug_handler, I(CONF_sshbug_rsa1)); ctrl_droplist(s, "Chokes on SSH-2 ignore messages", '2', 20, HELPCTX(ssh_bugs_ignore2), sshbug_handler, I(CONF_sshbug_ignore2)); ctrl_droplist(s, "Chokes on PuTTY's SSH-2 'winadj' requests", 'j', 20, HELPCTX(ssh_bugs_winadj), sshbug_handler, I(CONF_sshbug_winadj)); ctrl_droplist(s, "Miscomputes SSH-2 HMAC keys", 'm', 20, HELPCTX(ssh_bugs_hmac2), sshbug_handler, I(CONF_sshbug_hmac2)); ctrl_droplist(s, "Miscomputes SSH-2 encryption keys", 'e', 20, HELPCTX(ssh_bugs_derivekey2), sshbug_handler, I(CONF_sshbug_derivekey2)); ctrl_droplist(s, "Requires padding on SSH-2 RSA signatures", 'p', 20, HELPCTX(ssh_bugs_rsapad2), sshbug_handler, I(CONF_sshbug_rsapad2)); ctrl_droplist(s, "Misuses the session ID in SSH-2 PK auth", 'n', 20, HELPCTX(ssh_bugs_pksessid2), sshbug_handler, I(CONF_sshbug_pksessid2)); ctrl_droplist(s, "Handles SSH-2 key re-exchange badly", 'k', 20, HELPCTX(ssh_bugs_rekey2), sshbug_handler, I(CONF_sshbug_rekey2)); ctrl_droplist(s, "Ignores SSH-2 maximum packet size", 'x', 20, HELPCTX(ssh_bugs_maxpkt2), sshbug_handler, I(CONF_sshbug_maxpkt2)); } } } |
Added configure.
> > > | 1 2 3 | #!/bin/sh $(echo "$0" | sed '$s!configure$!unix/configure!') "$@" |
Changes to contrib/cygtermd/pty.c.
︙ | ︙ | |||
118 119 120 121 122 123 124 125 | i = 0; #ifdef TIOCNOTTY if ((fd = open("/dev/tty", O_RDWR)) >= 0) { ioctl(fd, TIOCNOTTY, &i); close(fd); } #endif #ifdef TIOCSCTTY | > > > > > > > > > | > > > > > | 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 | i = 0; #ifdef TIOCNOTTY if ((fd = open("/dev/tty", O_RDWR)) >= 0) { ioctl(fd, TIOCNOTTY, &i); close(fd); } #endif /* * Make the new pty our controlling terminal. On some OSes * this is done with TIOCSCTTY; Cygwin doesn't have that, so * instead it's done by simply opening the pty without * O_NOCTTY. This code is primarily intended for Cygwin, but * it's useful to have it work in other contexts for testing * purposes, so I leave the TIOCSCTTY here anyway. */ if ((fd = open(ptyname, O_RDWR)) >= 0) { #ifdef TIOCSCTTY ioctl(fd, TIOCSCTTY, &i); #endif close(fd); } else { perror("slave pty: open"); exit(127); } tcsetpgrp(0, getpgrp()); for (i = 0; i < shdata->nenvvars; i++) putenv(shdata->envvars[i]); if (shdata->termtype) putenv(shdata->termtype); |
︙ | ︙ |
Changes to contrib/cygtermd/telnet.c.
︙ | ︙ | |||
420 421 422 423 424 425 426 | telnet->state = TOP_LEVEL; else if (c == IAC) telnet->state = SEENIAC; else { char cc = c; sel_write(telnet->pty, &cc, 1); | > | > > | 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 | telnet->state = TOP_LEVEL; else if (c == IAC) telnet->state = SEENIAC; else { char cc = c; sel_write(telnet->pty, &cc, 1); if (c == CR) telnet->state = SEENCR; else telnet->state = TOP_LEVEL; } break; case SEENIAC: if (c == DO) telnet->state = SEENDO; else if (c == DONT) telnet->state = SEENDONT; |
︙ | ︙ |
Added contrib/logparse.pl.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 | #!/usr/bin/perl use strict; use warnings; use FileHandle; my $dumpchannels = 0; my $dumpdata = 0; while ($ARGV[0] =~ /^-/) { my $opt = shift @ARGV; if ($opt eq "--") { last; # stop processing options } elsif ($opt eq "-c") { $dumpchannels = 1; } elsif ($opt eq "-d") { $dumpdata = 1; } else { die "unrecognised option '$opt'\n"; } } my @channels = (); # ultimate channel ids are indices in this array my %chan_by_id = (); # indexed by 'c%d' or 's%d' for client and server ids my %globalreq = (); # indexed by 'i' or 'o' my %packets = ( #define SSH2_MSG_DISCONNECT 1 /* 0x1 */ 'SSH2_MSG_DISCONNECT' => sub { my ($direction, $seq, $data) = @_; my ($reason, $description, $lang) = &parse("uss", $data); printf "%s\n", &str($description); }, #define SSH2_MSG_IGNORE 2 /* 0x2 */ 'SSH2_MSG_IGNORE' => sub { my ($direction, $seq, $data) = @_; my ($str) = &parse("s", $data); printf "(%d bytes)\n", length $str; }, #define SSH2_MSG_UNIMPLEMENTED 3 /* 0x3 */ 'SSH2_MSG_UNIMPLEMENTED' => sub { my ($direction, $seq, $data) = @_; my ($rseq) = &parse("u", $data); printf "i%d\n", $rseq; }, #define SSH2_MSG_DEBUG 4 /* 0x4 */ 'SSH2_MSG_DEBUG' => sub { my ($direction, $seq, $data) = @_; my ($disp, $message, $lang) = &parse("bss", $data); printf "%s\n", &str($message); }, #define SSH2_MSG_SERVICE_REQUEST 5 /* 0x5 */ 'SSH2_MSG_SERVICE_REQUEST' => sub { my ($direction, $seq, $data) = @_; my ($service) = &parse("s", $data); printf "%s\n", &str($service); }, #define SSH2_MSG_SERVICE_ACCEPT 6 /* 0x6 */ 'SSH2_MSG_SERVICE_ACCEPT' => sub { my ($direction, $seq, $data) = @_; my ($service) = &parse("s", $data); printf "%s\n", &str($service); }, #define SSH2_MSG_KEXINIT 20 /* 0x14 */ 'SSH2_MSG_KEXINIT' => sub { my ($direction, $seq, $data) = @_; print "\n"; }, #define SSH2_MSG_NEWKEYS 21 /* 0x15 */ 'SSH2_MSG_NEWKEYS' => sub { my ($direction, $seq, $data) = @_; print "\n"; }, #define SSH2_MSG_KEXDH_INIT 30 /* 0x1e */ 'SSH2_MSG_KEXDH_INIT' => sub { my ($direction, $seq, $data) = @_; print "\n"; }, #define SSH2_MSG_KEXDH_REPLY 31 /* 0x1f */ 'SSH2_MSG_KEXDH_REPLY' => sub { my ($direction, $seq, $data) = @_; print "\n"; }, #define SSH2_MSG_KEX_DH_GEX_REQUEST 30 /* 0x1e */ 'SSH2_MSG_KEX_DH_GEX_REQUEST' => sub { my ($direction, $seq, $data) = @_; print "\n"; }, #define SSH2_MSG_KEX_DH_GEX_GROUP 31 /* 0x1f */ 'SSH2_MSG_KEX_DH_GEX_GROUP' => sub { my ($direction, $seq, $data) = @_; print "\n"; }, #define SSH2_MSG_KEX_DH_GEX_INIT 32 /* 0x20 */ 'SSH2_MSG_KEX_DH_GEX_INIT' => sub { my ($direction, $seq, $data) = @_; print "\n"; }, #define SSH2_MSG_KEX_DH_GEX_REPLY 33 /* 0x21 */ 'SSH2_MSG_KEX_DH_GEX_REPLY' => sub { my ($direction, $seq, $data) = @_; print "\n"; }, #define SSH2_MSG_KEXRSA_PUBKEY 30 /* 0x1e */ 'SSH2_MSG_KEXRSA_PUBKEY' => sub { my ($direction, $seq, $data) = @_; print "\n"; }, #define SSH2_MSG_KEXRSA_SECRET 31 /* 0x1f */ 'SSH2_MSG_KEXRSA_SECRET' => sub { my ($direction, $seq, $data) = @_; print "\n"; }, #define SSH2_MSG_KEXRSA_DONE 32 /* 0x20 */ 'SSH2_MSG_KEXRSA_DONE' => sub { my ($direction, $seq, $data) = @_; print "\n"; }, #define SSH2_MSG_USERAUTH_REQUEST 50 /* 0x32 */ 'SSH2_MSG_USERAUTH_REQUEST' => sub { my ($direction, $seq, $data) = @_; my ($user, $service, $method) = &parse("sss", $data); my $out = sprintf "%s %s %s", &str($user), &str($service), &str($method); if ($method eq "publickey") { my ($real) = &parse("b", $data); $out .= " real=$real"; } elsif ($method eq "password") { my ($change) = &parse("b", $data); $out .= " change=$change"; } print "$out\n"; }, #define SSH2_MSG_USERAUTH_FAILURE 51 /* 0x33 */ 'SSH2_MSG_USERAUTH_FAILURE' => sub { my ($direction, $seq, $data) = @_; my ($options) = &parse("s", $data); printf "%s\n", &str($options); }, #define SSH2_MSG_USERAUTH_SUCCESS 52 /* 0x34 */ 'SSH2_MSG_USERAUTH_SUCCESS' => sub { my ($direction, $seq, $data) = @_; print "\n"; }, #define SSH2_MSG_USERAUTH_BANNER 53 /* 0x35 */ 'SSH2_MSG_USERAUTH_BANNER' => sub { my ($direction, $seq, $data) = @_; print "\n"; }, #define SSH2_MSG_USERAUTH_PK_OK 60 /* 0x3c */ 'SSH2_MSG_USERAUTH_PK_OK' => sub { my ($direction, $seq, $data) = @_; print "\n"; }, #define SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ 60 /* 0x3c */ 'SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ' => sub { my ($direction, $seq, $data) = @_; print "\n"; }, #define SSH2_MSG_USERAUTH_INFO_REQUEST 60 /* 0x3c */ 'SSH2_MSG_USERAUTH_INFO_REQUEST' => sub { my ($direction, $seq, $data) = @_; print "\n"; }, #define SSH2_MSG_USERAUTH_INFO_RESPONSE 61 /* 0x3d */ 'SSH2_MSG_USERAUTH_INFO_RESPONSE' => sub { my ($direction, $seq, $data) = @_; print "\n"; }, #define SSH2_MSG_GLOBAL_REQUEST 80 /* 0x50 */ 'SSH2_MSG_GLOBAL_REQUEST' => sub { my ($direction, $seq, $data) = @_; my ($type, $wantreply) = &parse("sb", $data); printf "%s (%s)", $type, $wantreply eq "yes" ? "reply" : "noreply"; my $request = [$seq, $type]; push @{$globalreq{$direction}}, $request if $wantreply eq "yes"; if ($type eq "tcpip-forward" or $type eq "cancel-tcpip-forward") { my ($addr, $port) = &parse("su", $data); printf " %s:%s", $addr, $port; push @$request, $port; } print "\n"; }, #define SSH2_MSG_REQUEST_SUCCESS 81 /* 0x51 */ 'SSH2_MSG_REQUEST_SUCCESS' => sub { my ($direction, $seq, $data) = @_; my $otherdir = ($direction eq "i" ? "o" : "i"); my $request = shift @{$globalreq{$otherdir}}; if (defined $request) { printf "to %s", $request->[0]; if ($request->[1] eq "tcpip-forward" and $request->[2] == 0) { my ($port) = &parse("u", $data); printf " port=%s", $port; } } else { print "(spurious?)"; } print "\n"; }, #define SSH2_MSG_REQUEST_FAILURE 82 /* 0x52 */ 'SSH2_MSG_REQUEST_FAILURE' => sub { my ($direction, $seq, $data) = @_; my $otherdir = ($direction eq "i" ? "o" : "i"); my $request = shift @{$globalreq{$otherdir}}; if (defined $request) { printf "to %s", $request->[0]; } else { print "(spurious?)"; } print "\n"; }, #define SSH2_MSG_CHANNEL_OPEN 90 /* 0x5a */ 'SSH2_MSG_CHANNEL_OPEN' => sub { my ($direction, $seq, $data) = @_; my ($type, $sid, $winsize, $packet) = &parse("suuu", $data); # CHANNEL_OPEN tells the other side the _sender's_ id for the # channel, so this choice between "s" and "c" prefixes is # opposite to every other message in the protocol, which all # quote the _recipient's_ id of the channel. $sid = ($direction eq "i" ? "s" : "c") . $sid; my $chan = {'id'=>$sid, 'state'=>'halfopen', 'i'=>{'win'=>0, 'seq'=>0}, 'o'=>{'win'=>0, 'seq'=>0}}; $chan->{$direction}{'win'} = $winsize; push @channels, $chan; my $index = $#channels; $chan_by_id{$sid} = $index; printf "ch%d (%s) %s (--%d)", $index, $chan->{'id'}, $type, $chan->{$direction}{'win'}; if ($type eq "x11") { my ($addr, $port) = &parse("su", $data); printf " from %s:%s", $addr, $port; } elsif ($type eq "forwarded-tcpip") { my ($saddr, $sport, $paddr, $pport) = &parse("susu", $data); printf " to %s:%s from %s:%s", $saddr, $sport, $paddr, $pport; } elsif ($type eq "direct-tcpip") { my ($daddr, $dport, $saddr, $sport) = &parse("susu", $data); printf " to %s:%s from %s:%s", $daddr, $dport, $saddr, $sport; } print "\n"; }, #define SSH2_MSG_CHANNEL_OPEN_CONFIRMATION 91 /* 0x5b */ 'SSH2_MSG_CHANNEL_OPEN_CONFIRMATION' => sub { my ($direction, $seq, $data) = @_; my ($rid, $sid, $winsize, $packet) = &parse("uuuu", $data); $rid = ($direction eq "i" ? "c" : "s") . $rid; my $index = $chan_by_id{$rid}; if (!defined $index) { printf "UNKNOWN_CHANNEL (%s) (--%d)\n", $rid, $winsize; return; } $sid = ($direction eq "i" ? "s" : "c") . $sid; $chan_by_id{$sid} = $index; my $chan = $channels[$index]; $chan->{'id'} = ($direction eq "i" ? "$rid/$sid" : "$sid/$rid"); $chan->{'state'} = 'open'; $chan->{$direction}{'win'} = $winsize; printf "ch%d (%s) (--%d)\n", $index, $chan->{'id'}, $chan->{$direction}{'win'}; }, #define SSH2_MSG_CHANNEL_OPEN_FAILURE 92 /* 0x5c */ 'SSH2_MSG_CHANNEL_OPEN_FAILURE' => sub { my ($direction, $seq, $data) = @_; my ($rid, $reason, $desc, $lang) = &parse("uuss", $data); $rid = ($direction eq "i" ? "c" : "s") . $rid; my $index = $chan_by_id{$rid}; if (!defined $index) { printf "UNKNOWN_CHANNEL (%s) %s\n", $rid, &str($reason); return; } my $chan = $channels[$index]; $chan->{'state'} = 'rejected'; printf "ch%d (%s) %s\n", $index, $chan->{'id'}, &str($reason); }, #define SSH2_MSG_CHANNEL_WINDOW_ADJUST 93 /* 0x5d */ 'SSH2_MSG_CHANNEL_WINDOW_ADJUST' => sub { my ($direction, $seq, $data) = @_; my ($rid, $bytes) = &parse("uu", $data); $rid = ($direction eq "i" ? "c" : "s") . $rid; my $index = $chan_by_id{$rid}; if (!defined $index) { printf "UNKNOWN_CHANNEL (%s) +%d\n", $rid, $bytes; return; } my $chan = $channels[$index]; $chan->{$direction}{'win'} += $bytes; printf "ch%d (%s) +%d (--%d)\n", $index, $chan->{'id'}, $bytes, $chan->{$direction}{'win'}; }, #define SSH2_MSG_CHANNEL_DATA 94 /* 0x5e */ 'SSH2_MSG_CHANNEL_DATA' => sub { my ($direction, $seq, $data) = @_; my ($rid, $bytes) = &parse("uu", $data); $rid = ($direction eq "i" ? "c" : "s") . $rid; my $index = $chan_by_id{$rid}; if (!defined $index) { printf "UNKNOWN_CHANNEL (%s), %s bytes\n", $rid, $bytes; return; } my $chan = $channels[$index]; $chan->{$direction}{'seq'} += $bytes; printf "ch%d (%s), %s bytes (%d--%d)\n", $index, $chan->{'id'}, $bytes, $chan->{$direction}{'seq'}-$bytes, $chan->{$direction}{'seq'}; my @realdata = splice @$data, 0, $bytes; if ($dumpdata) { my $filekey = $direction . "file"; if (!defined $chan->{$filekey}) { my $filename = sprintf "ch%d.%s", $index, $direction; $chan->{$filekey} = FileHandle->new(">$filename"); if (!defined $chan->{$filekey}) { die "$filename: $!\n"; } } die "channel data not present in $seq\n" if @realdata < $bytes; my $rawdata = pack "C*", @realdata; my $fh = $chan->{$filekey}; print $fh $rawdata; } if (@realdata == $bytes and defined $chan->{$direction."data"}) { my $rawdata = pack "C*", @realdata; $chan->{$direction."data"}->($chan, $index, $direction, $rawdata); } }, #define SSH2_MSG_CHANNEL_EXTENDED_DATA 95 /* 0x5f */ 'SSH2_MSG_CHANNEL_EXTENDED_DATA' => sub { my ($direction, $seq, $data) = @_; my ($rid, $type, $bytes) = &parse("uuu", $data); if ($type == 1) { $type = "SSH_EXTENDED_DATA_STDERR"; } $rid = ($direction eq "i" ? "c" : "s") . $rid; my $index = $chan_by_id{$rid}; if (!defined $index) { printf "UNKNOWN_CHANNEL (%s), type %s, %s bytes\n", $rid, $type, $bytes; return; } my $chan = $channels[$index]; $chan->{$direction}{'seq'} += $bytes; printf "ch%d (%s), type %s, %s bytes (%d--%d)\n", $index,$chan->{'id'}, $type, $bytes, $chan->{$direction}{'seq'}-$bytes, $chan->{$direction}{'seq'}; my @realdata = splice @$data, 0, $bytes; if ($dumpdata) { # We treat EXTENDED_DATA as equivalent to DATA, for the # moment. It's not clear what else would be a better thing # to do with it, and this at least is the Right Answer if # the data is going to a terminal and the aim is to debug # the terminal emulator. my $filekey = $direction . "file"; if (!defined $chan->{$filekey}) { my $filename = sprintf "ch%d.%s", $index, $direction; $chan->{$filekey} = FileHandle->new(">$filename"); if (!defined $chan->{$filekey}) { die "$filename: $!\n"; } } die "channel data not present in $seq\n" if @realdata < $bytes; my $rawdata = pack "C*", @realdata; my $fh = $chan->{$filekey}; print $fh $rawdata; } if (@realdata == $bytes and defined $chan->{$direction."data"}) { my $rawdata = pack "C*", @realdata; $chan->{$direction."data"}->($chan, $index, $direction, $rawdata); } }, #define SSH2_MSG_CHANNEL_EOF 96 /* 0x60 */ 'SSH2_MSG_CHANNEL_EOF' => sub { my ($direction, $seq, $data) = @_; my ($rid) = &parse("uu", $data); $rid = ($direction eq "i" ? "c" : "s") . $rid; my $index = $chan_by_id{$rid}; if (!defined $index) { printf "UNKNOWN_CHANNEL (%s)\n", $rid; return; } my $chan = $channels[$index]; printf "ch%d (%s)\n", $index, $chan->{'id'}; }, #define SSH2_MSG_CHANNEL_CLOSE 97 /* 0x61 */ 'SSH2_MSG_CHANNEL_CLOSE' => sub { my ($direction, $seq, $data) = @_; my ($rid) = &parse("uu", $data); $rid = ($direction eq "i" ? "c" : "s") . $rid; my $index = $chan_by_id{$rid}; if (!defined $index) { printf "UNKNOWN_CHANNEL (%s)\n", $rid; return; } my $chan = $channels[$index]; $chan->{'state'} = ($chan->{'state'} eq "open" ? "halfclosed" : $chan->{'state'} eq "halfclosed" ? "closed" : "confused"); if ($chan->{'state'} eq "closed") { $chan->{'ifile'}->close if defined $chan->{'ifile'}; $chan->{'ofile'}->close if defined $chan->{'ofile'}; } printf "ch%d (%s)\n", $index, $chan->{'id'}; }, #define SSH2_MSG_CHANNEL_REQUEST 98 /* 0x62 */ 'SSH2_MSG_CHANNEL_REQUEST' => sub { my ($direction, $seq, $data) = @_; my ($rid, $type, $wantreply) = &parse("usb", $data); $rid = ($direction eq "i" ? "c" : "s") . $rid; my $index = $chan_by_id{$rid}; my $chan; if (!defined $index) { printf "UNKNOWN_CHANNEL (%s) %s (%s)", $rid, $type, $wantreply eq "yes" ? "reply" : "noreply"; } else { $chan = $channels[$index]; printf "ch%d (%s) %s (%s)", $index, $chan->{'id'}, $type, $wantreply eq "yes" ? "reply" : "noreply"; push @{$chan->{'requests_'.$direction}}, [$seq, $type] if $wantreply eq "yes"; } if ($type eq "pty-req") { my ($term, $w, $h, $pw, $ph, $modes) = &parse("suuuus", $data); printf " %s %sx%s", &str($term), $w, $h; } elsif ($type eq "x11-req") { my ($single, $xprot, $xcookie, $xscreen) = &parse("bssu", $data); print " one-off" if $single eq "yes"; printf " %s :%s", $xprot, $xscreen; } elsif ($type eq "exec") { my ($command) = &parse("s", $data); printf " %s", &str($command); } elsif ($type eq "subsystem") { my ($subsys) = &parse("s", $data); printf " %s", &str($subsys); if ($subsys eq "sftp") { &sftp_setup($index); } } elsif ($type eq "window-change") { my ($w, $h, $pw, $ph) = &parse("uuuu", $data); printf " %sx%s", $w, $h; } elsif ($type eq "xon-xoff") { my ($can) = &parse("b", $data); printf " %s", $can; } elsif ($type eq "signal") { my ($sig) = &parse("s", $data); printf " %s", &str($sig); } elsif ($type eq "exit-status") { my ($status) = &parse("u", $data); printf " %s", $status; } elsif ($type eq "exit-signal") { my ($sig, $core, $error, $lang) = &parse("sbss", $data); printf " %s", &str($sig); print " (core dumped)" if $core eq "yes"; } print "\n"; }, #define SSH2_MSG_CHANNEL_SUCCESS 99 /* 0x63 */ 'SSH2_MSG_CHANNEL_SUCCESS' => sub { my ($direction, $seq, $data) = @_; my ($rid) = &parse("uu", $data); $rid = ($direction eq "i" ? "c" : "s") . $rid; my $index = $chan_by_id{$rid}; if (!defined $index) { printf "UNKNOWN_CHANNEL (%s)\n", $rid; return; } my $chan = $channels[$index]; printf "ch%d (%s)", $index, $chan->{'id'}; my $otherdir = ($direction eq "i" ? "o" : "i"); my $request = shift @{$chan->{'requests_' . $otherdir}}; if (defined $request) { printf " to %s", $request->[0]; } else { print " (spurious?)"; } print "\n"; }, #define SSH2_MSG_CHANNEL_FAILURE 100 /* 0x64 */ 'SSH2_MSG_CHANNEL_FAILURE' => sub { my ($direction, $seq, $data) = @_; my ($rid) = &parse("uu", $data); $rid = ($direction eq "i" ? "c" : "s") . $rid; my $index = $chan_by_id{$rid}; if (!defined $index) { printf "UNKNOWN_CHANNEL (%s)\n", $rid; return; } my $chan = $channels[$index]; printf "ch%d (%s)", $index, $chan->{'id'}; my $otherdir = ($direction eq "i" ? "o" : "i"); my $request = shift @{$chan->{'requests_' . $otherdir}}; if (defined $request) { printf " to %s", $request->[0]; } else { print " (spurious?)"; } print "\n"; }, #define SSH2_MSG_USERAUTH_GSSAPI_RESPONSE 60 'SSH2_MSG_USERAUTH_GSSAPI_RESPONSE' => sub { my ($direction, $seq, $data) = @_; print "\n"; }, #define SSH2_MSG_USERAUTH_GSSAPI_TOKEN 61 'SSH2_MSG_USERAUTH_GSSAPI_TOKEN' => sub { my ($direction, $seq, $data) = @_; print "\n"; }, #define SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE 63 'SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE' => sub { my ($direction, $seq, $data) = @_; print "\n"; }, #define SSH2_MSG_USERAUTH_GSSAPI_ERROR 64 'SSH2_MSG_USERAUTH_GSSAPI_ERROR' => sub { my ($direction, $seq, $data) = @_; print "\n"; }, #define SSH2_MSG_USERAUTH_GSSAPI_ERRTOK 65 'SSH2_MSG_USERAUTH_GSSAPI_ERRTOK' => sub { my ($direction, $seq, $data) = @_; print "\n"; }, #define SSH2_MSG_USERAUTH_GSSAPI_MIC 66 'SSH2_MSG_USERAUTH_GSSAPI_MIC' => sub { my ($direction, $seq, $data) = @_; print "\n"; }, ); my %sftp_packets = ( #define SSH_FXP_INIT 1 /* 0x1 */ 0x1 => sub { my ($chan, $index, $direction, $id, $data) = @_; my ($ver) = &parse("u", $data); printf "SSH_FXP_INIT %d\n", $ver; }, #define SSH_FXP_VERSION 2 /* 0x2 */ 0x2 => sub { my ($chan, $index, $direction, $id, $data) = @_; my ($ver) = &parse("u", $data); printf "SSH_FXP_VERSION %d\n", $ver; }, #define SSH_FXP_OPEN 3 /* 0x3 */ 0x3 => sub { my ($chan, $index, $direction, $id, $data) = @_; my ($reqid, $path, $pflags) = &parse("usu", $data); &sftp_logreq($chan, $direction, $reqid, $id, "SSH_FXP_OPEN"); printf " \"%s\" ", $path; if ($pflags eq 0) { print "0"; } else { my $sep = ""; if ($pflags & 1) { $pflags ^= 1; print "${sep}READ"; $sep = "|"; } if ($pflags & 2) { $pflags ^= 2; print "${sep}WRITE"; $sep = "|"; } if ($pflags & 4) { $pflags ^= 4; print "${sep}APPEND"; $sep = "|"; } if ($pflags & 8) { $pflags ^= 8; print "${sep}CREAT"; $sep = "|"; } if ($pflags & 16) { $pflags ^= 16; print "${sep}TRUNC"; $sep = "|"; } if ($pflags & 32) { $pflags ^= 32; print "${sep}EXCL"; $sep = "|"; } if ($pflags) { print "${sep}${pflags}"; } } print "\n"; }, #define SSH_FXP_CLOSE 4 /* 0x4 */ 0x4 => sub { my ($chan, $index, $direction, $id, $data) = @_; my ($reqid, $handle) = &parse("us", $data); &sftp_logreq($chan, $direction, $reqid, $id, "SSH_FXP_CLOSE"); printf " \"%s\"", &stringescape($handle); print "\n"; }, #define SSH_FXP_READ 5 /* 0x5 */ 0x5 => sub { my ($chan, $index, $direction, $id, $data) = @_; my ($reqid, $handle, $offset, $len) = &parse("usUu", $data); &sftp_logreq($chan, $direction, $reqid, $id, "SSH_FXP_READ"); printf " \"%s\" %d %d", &stringescape($handle), $offset, $len; print "\n"; }, #define SSH_FXP_WRITE 6 /* 0x6 */ 0x6 => sub { my ($chan, $index, $direction, $id, $data) = @_; my ($reqid, $handle, $offset, $wdata) = &parse("usUs", $data); &sftp_logreq($chan, $direction, $reqid, $id, "SSH_FXP_WRITE"); printf " \"%s\" %d [%d bytes]", &stringescape($handle), $offset, length $wdata; print "\n"; }, #define SSH_FXP_LSTAT 7 /* 0x7 */ 0x7 => sub { my ($chan, $index, $direction, $id, $data) = @_; my ($reqid, $path) = &parse("us", $data); &sftp_logreq($chan, $direction, $reqid, $id, "SSH_FXP_LSTAT"); printf " \"%s\"", $path; print "\n"; }, #define SSH_FXP_FSTAT 8 /* 0x8 */ 0x8 => sub { my ($chan, $index, $direction, $id, $data) = @_; my ($reqid, $handle) = &parse("us", $data); &sftp_logreq($chan, $direction, $reqid, $id, "SSH_FXP_FSTAT"); printf " \"%s\"", &stringescape($handle); print "\n"; }, #define SSH_FXP_SETSTAT 9 /* 0x9 */ 0x9 => sub { my ($chan, $index, $direction, $id, $data) = @_; my ($reqid, $path) = &parse("us", $data); &sftp_logreq($chan, $direction, $reqid, $id, "SSH_FXP_SETSTAT"); my $attrs = &sftp_parse_attrs($data); printf " \"%s\" %s", $path, $attrs; print "\n"; }, #define SSH_FXP_FSETSTAT 10 /* 0xa */ 0xa => sub { my ($chan, $index, $direction, $id, $data) = @_; my ($reqid, $handle) = &parse("us", $data); &sftp_logreq($chan, $direction, $reqid, $id, "SSH_FXP_FSETSTAT"); my $attrs = &sftp_parse_attrs($data); printf " \"%s\" %s", &stringescape($handle), $attrs; print "\n"; }, #define SSH_FXP_OPENDIR 11 /* 0xb */ 0xb => sub { my ($chan, $index, $direction, $id, $data) = @_; my ($reqid, $path) = &parse("us", $data); &sftp_logreq($chan, $direction, $reqid, $id, "SSH_FXP_OPENDIR"); printf " \"%s\"", $path; print "\n"; }, #define SSH_FXP_READDIR 12 /* 0xc */ 0xc => sub { my ($chan, $index, $direction, $id, $data) = @_; my ($reqid, $handle) = &parse("us", $data); &sftp_logreq($chan, $direction, $reqid, $id, "SSH_FXP_READDIR"); printf " \"%s\"", &stringescape($handle); print "\n"; }, #define SSH_FXP_REMOVE 13 /* 0xd */ 0xd => sub { my ($chan, $index, $direction, $id, $data) = @_; my ($reqid, $path) = &parse("us", $data); &sftp_logreq($chan, $direction, $reqid, $id, "SSH_FXP_REMOVE"); printf " \"%s\"", $path; print "\n"; }, #define SSH_FXP_MKDIR 14 /* 0xe */ 0xe => sub { my ($chan, $index, $direction, $id, $data) = @_; my ($reqid, $path) = &parse("us", $data); &sftp_logreq($chan, $direction, $reqid, $id, "SSH_FXP_MKDIR"); printf " \"%s\"", $path; print "\n"; }, #define SSH_FXP_RMDIR 15 /* 0xf */ 0xf => sub { my ($chan, $index, $direction, $id, $data) = @_; my ($reqid, $path) = &parse("us", $data); &sftp_logreq($chan, $direction, $reqid, $id, "SSH_FXP_RMDIR"); printf " \"%s\"", $path; print "\n"; }, #define SSH_FXP_REALPATH 16 /* 0x10 */ 0x10 => sub { my ($chan, $index, $direction, $id, $data) = @_; my ($reqid, $path) = &parse("us", $data); &sftp_logreq($chan, $direction, $reqid, $id, "SSH_FXP_REALPATH"); printf " \"%s\"", $path; print "\n"; }, #define SSH_FXP_STAT 17 /* 0x11 */ 0x11 => sub { my ($chan, $index, $direction, $id, $data) = @_; my ($reqid, $path) = &parse("us", $data); &sftp_logreq($chan, $direction, $reqid, $id, "SSH_FXP_STAT"); printf " \"%s\"", $path; print "\n"; }, #define SSH_FXP_RENAME 18 /* 0x12 */ 0x12 => sub { my ($chan, $index, $direction, $id, $data) = @_; my ($reqid, $srcpath, $dstpath) = &parse("uss", $data); &sftp_logreq($chan, $direction, $reqid, $id, "SSH_FXP_RENAME"); printf " \"%s\" \"%s\"", $srcpath, $dstpath; print "\n"; }, #define SSH_FXP_STATUS 101 /* 0x65 */ 0x65 => sub { my ($chan, $index, $direction, $id, $data) = @_; my ($reqid, $status) = &parse("uu", $data); &sftp_logreply($chan, $direction, $reqid, $id, "SSH_FXP_STATUS"); print " "; if ($status eq "0") { print "SSH_FX_OK"; } elsif ($status eq "1") { print "SSH_FX_EOF"; } elsif ($status eq "2") { print "SSH_FX_NO_SUCH_FILE"; } elsif ($status eq "3") { print "SSH_FX_PERMISSION_DENIED"; } elsif ($status eq "4") { print "SSH_FX_FAILURE"; } elsif ($status eq "5") { print "SSH_FX_BAD_MESSAGE"; } elsif ($status eq "6") { print "SSH_FX_NO_CONNECTION"; } elsif ($status eq "7") { print "SSH_FX_CONNECTION_LOST"; } elsif ($status eq "8") { print "SSH_FX_OP_UNSUPPORTED"; } else { printf "[unknown status %d]", $status; } print "\n"; }, #define SSH_FXP_HANDLE 102 /* 0x66 */ 0x66 => sub { my ($chan, $index, $direction, $id, $data) = @_; my ($reqid, $handle) = &parse("us", $data); &sftp_logreply($chan, $direction, $reqid, $id, "SSH_FXP_HANDLE"); printf " \"%s\"", &stringescape($handle); print "\n"; }, #define SSH_FXP_DATA 103 /* 0x67 */ 0x67 => sub { my ($chan, $index, $direction, $id, $data) = @_; my ($reqid, $retdata) = &parse("us", $data); &sftp_logreply($chan, $direction, $reqid, $id, "SSH_FXP_DATA"); printf " [%d bytes]", length $retdata; print "\n"; }, #define SSH_FXP_NAME 104 /* 0x68 */ 0x68 => sub { my ($chan, $index, $direction, $id, $data) = @_; my ($reqid, $count) = &parse("uu", $data); &sftp_logreply($chan, $direction, $reqid, $id, "SSH_FXP_NAME"); for my $i (1..$count) { my ($name, $longname) = &parse("ss", $data); my $attrs = &sftp_parse_attrs($data); print " [name=\"$name\", longname=\"$longname\", attrs=$attrs]"; } print "\n"; }, #define SSH_FXP_ATTRS 105 /* 0x69 */ 0x69 => sub { my ($chan, $index, $direction, $id, $data) = @_; my ($reqid) = &parse("u", $data); &sftp_logreply($chan, $direction, $reqid, $id, "SSH_FXP_ATTRS"); my $attrs = &sftp_parse_attrs($data); printf " %s", $attrs; print "\n"; }, #define SSH_FXP_EXTENDED 200 /* 0xc8 */ 0xc8 => sub { my ($chan, $index, $direction, $id, $data) = @_; my ($reqid, $type) = &parse("us", $data); &sftp_logreq($chan, $direction, $reqid, $id, "SSH_FXP_EXTENDED"); printf " \"%s\"", $type; print "\n"; }, #define SSH_FXP_EXTENDED_REPLY 201 /* 0xc9 */ 0xc9 => sub { my ($chan, $index, $direction, $id, $data) = @_; my ($reqid) = &parse("u", $data); print "\n"; &sftp_logreply($chan, $direction, $reqid,$id,"SSH_FXP_EXTENDED_REPLY"); }, ); my ($direction, $seq, $ourseq, $type, $data, $recording); my %ourseqs = ('i'=>0, 'o'=>0); $recording = 0; while (<>) { if ($recording) { if (/^ [0-9a-fA-F]{8} ((?:[0-9a-fA-F]{2} )*[0-9a-fA-F]{2})/) { push @$data, map { $_ eq "XX" ? -1 : hex $_ } split / /, $1; } else { $recording = 0; my $fullseq = "$direction$ourseq"; print "$fullseq: $type "; if (defined $packets{$type}) { $packets{$type}->($direction, $fullseq, $data); } else { printf "raw %s\n", join "", map { sprintf "%02x", $_ } @$data; } } } if (/^(Incoming|Outgoing) packet #0x([0-9a-fA-F]+), type \d+ \/ 0x[0-9a-fA-F]+ \((.*)\)/) { $direction = ($1 eq "Incoming" ? 'i' : 'o'); # $seq is the sequence number quoted in the log file. $ourseq # is our own count of the sequence number, which differs in # that it shouldn't wrap at 2^32, should anyone manage to run # this script over such a huge log file. $seq = hex $2; $ourseq = $ourseqs{$direction}++; $type = $3; $data = []; $recording = 1; } } if ($dumpchannels) { my %stateorder = ('closed'=>0, 'rejected'=>1, 'halfclosed'=>2, 'open'=>3, 'halfopen'=>4); for my $index (0..$#channels) { my $chan = $channels[$index]; my $so = $stateorder{$chan->{'state'}}; $so = 1000 unless defined $so; # any state I've missed above comes last $chan->{'index'} = sprintf "ch%d", $index; $chan->{'order'} = sprintf "%08d %08d", $so, $index; } my @sortedchannels = sort { $a->{'order'} cmp $b->{'order'} } @channels; for my $chan (@sortedchannels) { printf "%s (%s): %s\n", $chan->{'index'}, $chan->{'id'}, $chan->{'state'}; } } sub parseone { my ($type, $data) = @_; if ($type eq "u") { # uint32 my @bytes = splice @$data, 0, 4; return "<missing>" if @bytes < 4 or grep { $_<0 } @bytes; return unpack "N", pack "C*", @bytes; } elsif ($type eq "U") { # uint64 my @bytes = splice @$data, 0, 8; return "<missing>" if @bytes < 8 or grep { $_<0 } @bytes; my @words = unpack "NN", pack "C*", @bytes; return ($words[0] << 32) + $words[1]; } elsif ($type eq "b") { # boolean my $byte = shift @$data; return "<missing>" if !defined $byte or $byte < 0; return $byte ? "yes" : "no"; } elsif ($type eq "B") { # byte my $byte = shift @$data; return "<missing>" if !defined $byte or $byte < 0; return $byte; } elsif ($type eq "s" or $type eq "m") { # string, mpint my @bytes = splice @$data, 0, 4; return "<missing>" if @bytes < 4 or grep { $_<0 } @bytes; my $len = unpack "N", pack "C*", @bytes; @bytes = splice @$data, 0, $len; return "<missing>" if @bytes < $len or grep { $_<0 } @bytes; if ($type eq "mpint") { my $str = ""; if ($bytes[0] >= 128) { # Take two's complement. @bytes = map { 0xFF ^ $_ } @bytes; for my $i (reverse 0..$#bytes) { if ($bytes[$i] < 0xFF) { $bytes[$i]++; last; } else { $bytes[$i] = 0; } } $str = "-"; } $str .= "0x" . join "", map { sprintf "%02x", $_ } @bytes; return $str; } else { return pack "C*", @bytes; } } } sub parse { my ($template, $data) = @_; return map { &parseone($_, $data) } split //, $template; } sub str { # Quote as a string. If I get enthusiastic I might arrange for # strange characters inside the string to be quoted. my $str = shift @_; return "'$str'"; } sub sftp_setup { my $index = shift @_; my $chan = $channels[$index]; $chan->{'obuf'} = $chan->{'ibuf'} = ''; $chan->{'ocnt'} = $chan->{'icnt'} = 0; $chan->{'odata'} = $chan->{'idata'} = \&sftp_data; $chan->{'sftpreqs'} = {}; } sub sftp_data { my ($chan, $index, $direction, $data) = @_; my $buf = \$chan->{$direction."buf"}; my $cnt = \$chan->{$direction."cnt"}; $$buf .= $data; while (length $$buf >= 4) { my $msglen = unpack "N", $$buf; last if length $$buf < 4 + $msglen; my $msg = substr $$buf, 4, $msglen; $$buf = substr $$buf, 4 + $msglen; $msg = [unpack "C*", $msg]; my $type = shift @$msg; my $id = sprintf "ch%d_sftp_%s%d", $index, $direction, ${$cnt}++; print "$id: "; if (defined $sftp_packets{$type}) { $sftp_packets{$type}->($chan, $index, $direction, $id, $msg); } else { printf "unknown SFTP packet type %d\n", $type; } } } sub sftp_logreq { my ($chan, $direction, $reqid, $id, $name) = @_; print "$name"; if ($direction eq "o") { # requests coming _in_ are too weird to track $chan->{'sftpreqs'}->{$reqid} = $id; } } sub sftp_logreply { my ($chan, $direction, $reqid, $id, $name) = @_; print "$name"; if ($direction eq "i") { # replies going _out_ are too weird to track if (defined $chan->{'sftpreqs'}->{$reqid}) { print " to ", $chan->{'sftpreqs'}->{$reqid}; $chan->{'sftpreqs'}->{$reqid} = undef; } } } sub sftp_parse_attrs { my ($data) = @_; my ($flags) = &parse("u", $data); return $flags if $flags eq "<missing>"; my $out = "{"; my $sep = ""; if ($flags & 0x00000001) { # SSH_FILEXFER_ATTR_SIZE $out .= $sep . sprintf "size=%d", &parse("U", $data); $sep = ", "; } if ($flags & 0x00000002) { # SSH_FILEXFER_ATTR_UIDGID $out .= $sep . sprintf "uid=%d", &parse("u", $data); $out .= $sep . sprintf "gid=%d", &parse("u", $data); $sep = ", "; } if ($flags & 0x00000004) { # SSH_FILEXFER_ATTR_PERMISSIONS $out .= $sep . sprintf "perms=%#o", &parse("u", $data); $sep = ", "; } if ($flags & 0x00000008) { # SSH_FILEXFER_ATTR_ACMODTIME $out .= $sep . sprintf "atime=%d", &parse("u", $data); $out .= $sep . sprintf "mtime=%d", &parse("u", $data); $sep = ", "; } if ($flags & 0x80000000) { # SSH_FILEXFER_ATTR_EXTENDED my $extcount = &parse("u", $data); while ($extcount-- > 0) { $out .= $sep . sprintf "\"%s\"=\"%s\"", &parse("ss", $data); $sep = ", "; } } $out .= "}"; return $out; } sub stringescape { my ($str) = @_; $str =~ s!\\!\\\\!g; $str =~ s![^ -~]!sprintf "\\x%02X", ord $&!eg; return $str; } |
Changes to cproxy.c.
︙ | ︙ | |||
126 127 128 129 130 131 132 | break; case 0x03: outbuf[0] = 0x01; /* Version */ outbuf[1] = 0x01; /* One attribute */ outbuf[2] = 0x04; /* Response */ outbuf[3] = 0x10; /* Length */ hmacmd5_chap(data, p->chap_current_datalen, | > | | 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 | break; case 0x03: outbuf[0] = 0x01; /* Version */ outbuf[1] = 0x01; /* One attribute */ outbuf[2] = 0x04; /* Response */ outbuf[3] = 0x10; /* Length */ hmacmd5_chap(data, p->chap_current_datalen, conf_get_str(p->conf, CONF_proxy_password), &outbuf[4]); sk_write(p->sub_socket, (char *)outbuf, 20); break; case 0x11: /* Chose a protocol */ if (data[0] != 0x85) { plug_closing(p->plug, "Proxy error: Server chose " "CHAP of other than HMAC-MD5 but we " |
︙ | ︙ | |||
155 156 157 158 159 160 161 | } } return 0; } int proxy_socks5_selectchap(Proxy_Socket p) { | > > | | | | 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 | } } return 0; } int proxy_socks5_selectchap(Proxy_Socket p) { char *username = conf_get_str(p->conf, CONF_proxy_username); char *password = conf_get_str(p->conf, CONF_proxy_password); if (username[0] || password[0]) { char chapbuf[514]; int ulen; chapbuf[0] = '\x01'; /* Version */ chapbuf[1] = '\x02'; /* Number of attributes sent */ chapbuf[2] = '\x11'; /* First attribute - algorithms list */ chapbuf[3] = '\x01'; /* Only one CHAP algorithm */ chapbuf[4] = '\x85'; /* ...and it's HMAC-MD5, the core one */ chapbuf[5] = '\x02'; /* Second attribute - username */ ulen = strlen(username); if (ulen > 255) ulen = 255; if (ulen < 1) ulen = 1; chapbuf[6] = ulen; memcpy(chapbuf+7, username, ulen); sk_write(p->sub_socket, chapbuf, ulen + 7); p->chap_num_attributes = 0; p->chap_num_attributes_processed = 0; p->chap_current_attribute = -1; p->chap_current_datalen = 0; |
︙ | ︙ |
Changes to dialog.c.
︙ | ︙ | |||
9 10 11 12 13 14 15 | #include <stdlib.h> #define DEFINE_INTORPTR_FNS #include "putty.h" #include "dialog.h" | < < < < | 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | #include <stdlib.h> #define DEFINE_INTORPTR_FNS #include "putty.h" #include "dialog.h" int ctrl_path_elements(char *path) { int i = 1; while (*path) { if (*path == '/') i++; path++; } |
︙ | ︙ | |||
47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 | { struct controlbox *ret = snew(struct controlbox); ret->nctrlsets = ret->ctrlsetsize = 0; ret->ctrlsets = NULL; ret->nfrees = ret->freesize = 0; ret->frees = NULL; return ret; } void ctrl_free_box(struct controlbox *b) { int i; for (i = 0; i < b->nctrlsets; i++) { ctrl_free_set(b->ctrlsets[i]); } for (i = 0; i < b->nfrees; i++) | > | > | 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 | { struct controlbox *ret = snew(struct controlbox); ret->nctrlsets = ret->ctrlsetsize = 0; ret->ctrlsets = NULL; ret->nfrees = ret->freesize = 0; ret->frees = NULL; ret->freefuncs = NULL; return ret; } void ctrl_free_box(struct controlbox *b) { int i; for (i = 0; i < b->nctrlsets; i++) { ctrl_free_set(b->ctrlsets[i]); } for (i = 0; i < b->nfrees; i++) b->freefuncs[i](b->frees[i]); sfree(b->ctrlsets); sfree(b->frees); sfree(b->freefuncs); sfree(b); } void ctrl_free_set(struct controlset *s) { int i; |
︙ | ︙ | |||
181 182 183 184 185 186 187 | (b->nctrlsets-index) * sizeof(*b->ctrlsets)); b->ctrlsets[index] = s; b->nctrlsets++; return s; } /* Allocate some private data in a controlbox. */ | | > > | > > > > > > > > > > > > | 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 | (b->nctrlsets-index) * sizeof(*b->ctrlsets)); b->ctrlsets[index] = s; b->nctrlsets++; return s; } /* Allocate some private data in a controlbox. */ void *ctrl_alloc_with_free(struct controlbox *b, size_t size, ctrl_freefn_t freefunc) { void *p; /* * This is an internal allocation routine, so it's allowed to * use smalloc directly. */ p = smalloc(size); if (b->nfrees >= b->freesize) { b->freesize = b->nfrees + 32; b->frees = sresize(b->frees, b->freesize, void *); b->freefuncs = sresize(b->freefuncs, b->freesize, ctrl_freefn_t); } b->frees[b->nfrees] = p; b->freefuncs[b->nfrees] = freefunc; b->nfrees++; return p; } static void ctrl_default_free(void *p) { sfree(p); } void *ctrl_alloc(struct controlbox *b, size_t size) { return ctrl_alloc_with_free(b, size, ctrl_default_free); } static union control *ctrl_new(struct controlset *s, int type, intorptr helpctx, handler_fn handler, intorptr context) { union control *c = snew(union control); if (s->ncontrols >= s->ctrlsize) { |
︙ | ︙ | |||
456 457 458 459 460 461 462 | break; case CTRL_FILESELECT: sfree(ctrl->fileselect.title); break; } sfree(ctrl); } | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 468 469 470 471 472 473 474 | break; case CTRL_FILESELECT: sfree(ctrl->fileselect.title); break; } sfree(ctrl); } |
Changes to dialog.h.
︙ | ︙ | |||
158 159 160 161 162 163 164 | /* * Most controls need to provide a function which gets * called when that control's setting is changed, or when * the control's setting needs initialising. * * The `data' parameter points to the writable data being * modified as a result of the configuration activity; for | | | 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 | /* * Most controls need to provide a function which gets * called when that control's setting is changed, or when * the control's setting needs initialising. * * The `data' parameter points to the writable data being * modified as a result of the configuration activity; for * example, the PuTTY `Conf' structure, although not * necessarily. * * The `dlg' parameter is passed back to the platform- * specific routines to read and write the actual control * state. */ handler_fn handler; |
︙ | ︙ | |||
423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 | char *boxtitle; /* title of container box */ int ncolumns; /* current no. of columns at bottom */ int ncontrols; /* number of `union control' in array */ int ctrlsize; /* allocated size of array */ union control **ctrls; /* actual array */ }; /* * This is the container structure which holds a complete set of * controls. */ struct controlbox { int nctrlsets; /* number of ctrlsets */ int ctrlsetsize; /* ctrlset size */ struct controlset **ctrlsets; /* actual array of ctrlsets */ int nfrees; int freesize; void **frees; /* array of aux data areas to free */ }; struct controlbox *ctrl_new_box(void); void ctrl_free_box(struct controlbox *); /* * Standard functions used for populating a controlbox structure. | > > > | 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 | char *boxtitle; /* title of container box */ int ncolumns; /* current no. of columns at bottom */ int ncontrols; /* number of `union control' in array */ int ctrlsize; /* allocated size of array */ union control **ctrls; /* actual array */ }; typedef void (*ctrl_freefn_t)(void *); /* used by ctrl_alloc_with_free */ /* * This is the container structure which holds a complete set of * controls. */ struct controlbox { int nctrlsets; /* number of ctrlsets */ int ctrlsetsize; /* ctrlset size */ struct controlset **ctrlsets; /* actual array of ctrlsets */ int nfrees; int freesize; void **frees; /* array of aux data areas to free */ ctrl_freefn_t *freefuncs; /* parallel array of free functions */ }; struct controlbox *ctrl_new_box(void); void ctrl_free_box(struct controlbox *); /* * Standard functions used for populating a controlbox structure. |
︙ | ︙ | |||
460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 | /* * This function works like `malloc', but the memory it returns * will be automatically freed when the controlbox is freed. Note * that a controlbox is a dialog-box _template_, not an instance, * and so data allocated through this function is better not used * to hold modifiable per-instance things. It's mostly here for * allocating structures to be passed as control handler params. */ void *ctrl_alloc(struct controlbox *b, size_t size); /* * Individual routines to create `union control' structures in a controlset. * * Most of these routines allow the most common fields to be set * directly, and put default values in the rest. Each one returns a * pointer to the `union control' it created, so that final tweaks | > > > > > > | 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 | /* * This function works like `malloc', but the memory it returns * will be automatically freed when the controlbox is freed. Note * that a controlbox is a dialog-box _template_, not an instance, * and so data allocated through this function is better not used * to hold modifiable per-instance things. It's mostly here for * allocating structures to be passed as control handler params. * * ctrl_alloc_with_free also allows you to provide a function to free * the structure, in case there are other dynamically allocated bits * and pieces dangling off it. */ void *ctrl_alloc(struct controlbox *b, size_t size); void *ctrl_alloc_with_free(struct controlbox *b, size_t size, ctrl_freefn_t freefunc); /* * Individual routines to create `union control' structures in a controlset. * * Most of these routines allow the most common fields to be set * directly, and put default values in the rest. Each one returns a * pointer to the `union control' it created, so that final tweaks |
︙ | ︙ | |||
517 518 519 520 521 522 523 | handler_fn handler, intorptr context); union control *ctrl_text(struct controlset *, char *text, intorptr helpctx); union control *ctrl_checkbox(struct controlset *, char *label, char shortcut, intorptr helpctx, handler_fn handler, intorptr context); union control *ctrl_tabdelay(struct controlset *, union control *); | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | | | | | | 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 | handler_fn handler, intorptr context); union control *ctrl_text(struct controlset *, char *text, intorptr helpctx); union control *ctrl_checkbox(struct controlset *, char *label, char shortcut, intorptr helpctx, handler_fn handler, intorptr context); union control *ctrl_tabdelay(struct controlset *, union control *); /* * Routines the platform-independent dialog code can call to read * and write the values of controls. */ void dlg_radiobutton_set(union control *ctrl, void *dlg, int whichbutton); int dlg_radiobutton_get(union control *ctrl, void *dlg); void dlg_checkbox_set(union control *ctrl, void *dlg, int checked); int dlg_checkbox_get(union control *ctrl, void *dlg); void dlg_editbox_set(union control *ctrl, void *dlg, char const *text); char *dlg_editbox_get(union control *ctrl, void *dlg); /* result must be freed by caller */ /* The `listbox' functions can also apply to combo boxes. */ void dlg_listbox_clear(union control *ctrl, void *dlg); void dlg_listbox_del(union control *ctrl, void *dlg, int index); void dlg_listbox_add(union control *ctrl, void *dlg, char const *text); /* * Each listbox entry may have a numeric id associated with it. * Note that some front ends only permit a string to be stored at * each position, which means that _if_ you put two identical * strings in any listbox then you MUST not assign them different * IDs and expect to get meaningful results back. */ void dlg_listbox_addwithid(union control *ctrl, void *dlg, char const *text, int id); int dlg_listbox_getid(union control *ctrl, void *dlg, int index); /* dlg_listbox_index returns <0 if no single element is selected. */ int dlg_listbox_index(union control *ctrl, void *dlg); int dlg_listbox_issel(union control *ctrl, void *dlg, int index); void dlg_listbox_select(union control *ctrl, void *dlg, int index); void dlg_text_set(union control *ctrl, void *dlg, char const *text); void dlg_filesel_set(union control *ctrl, void *dlg, Filename *fn); Filename *dlg_filesel_get(union control *ctrl, void *dlg); void dlg_fontsel_set(union control *ctrl, void *dlg, FontSpec *fn); FontSpec *dlg_fontsel_get(union control *ctrl, void *dlg); /* * Bracketing a large set of updates in these two functions will * cause the front end (if possible) to delay updating the screen * until it's all complete, thus avoiding flicker. */ void dlg_update_start(union control *ctrl, void *dlg); void dlg_update_done(union control *ctrl, void *dlg); |
︙ | ︙ |
Changes to doc/blurb.but.
|
| | > > | 1 2 3 4 5 6 7 8 9 10 | \define{versionidblurb} \versionid $Id: blurb.but 9993 2013-08-05 15:15:17Z jacob $ \define{dash} \u2013{-} \title PuTTY User Manual \cfg{xhtml-leaf-level}{1} \cfg{xhtml-leaf-smallest-contents}{2} \cfg{xhtml-leaf-contains-contents}{true} \cfg{xhtml-body-end}{<p>If you want to provide feedback on this manual |
︙ | ︙ | |||
27 28 29 30 31 32 33 | Windows versions of the PuTTY utilities. Some options are therefore mentioned that are absent from the \i{Unix version}; the Unix version has features not described here; and the \i\cw{pterm} and command-line \cw{puttygen} utilities are not described at all. The only Unix-specific documentation that currently exists is the \I{man pages for PuTTY tools}man pages. | | | 29 30 31 32 33 34 35 36 37 38 | Windows versions of the PuTTY utilities. Some options are therefore mentioned that are absent from the \i{Unix version}; the Unix version has features not described here; and the \i\cw{pterm} and command-line \cw{puttygen} utilities are not described at all. The only Unix-specific documentation that currently exists is the \I{man pages for PuTTY tools}man pages. \copyright This manual is copyright 2001-2013 Simon Tatham. All rights reserved. You may distribute this documentation under the MIT licence. See \k{licence} for the licence text in full. |
Changes to doc/config.but.
|
| | | 1 2 3 4 5 6 7 8 | \define{versionidconfig} \versionid $Id: config.but 10088 2013-11-18 22:34:57Z jacob $ \C{config} Configuring PuTTY This chapter describes all the \i{configuration options} in PuTTY. PuTTY is configured using the control panel that comes up before you start a session. Some options can also be changed in the middle of a |
︙ | ︙ | |||
1250 1251 1252 1253 1254 1255 1256 | needs to know what character set to interpret them in. Similarly, PuTTY needs to know how to translate your keystrokes into the encoding the server expects. Unfortunately, there is no satisfactory mechanism for PuTTY and the server to communicate this information, so it must usually be manually configured. There are a lot of character sets to choose from. The \q{Remote | | | > > > > | | | < < < < < < | 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 | needs to know what character set to interpret them in. Similarly, PuTTY needs to know how to translate your keystrokes into the encoding the server expects. Unfortunately, there is no satisfactory mechanism for PuTTY and the server to communicate this information, so it must usually be manually configured. There are a lot of character sets to choose from. The \q{Remote character set} option lets you select one. By default PuTTY will use the \i{UTF-8} encoding of \i{Unicode}, which can represent pretty much any character; data coming from the server is interpreted as UTF-8, and keystrokes are sent UTF-8 encoded. This is what most modern distributions of Linux will expect by default. However, if this is wrong for your server, you can select a different character set using this control. A few other notable character sets are: \b The \i{ISO-8859} series are all standard character sets that include various accented characters appropriate for different sets of languages. \b The \i{Win125x} series are defined by Microsoft, for similar purposes. In particular Win1252 is almost equivalent to ISO-8859-1, but contains a few extra characters such as matched quotes and the Euro symbol. \b If you want the old IBM PC character set with block graphics and line-drawing characters, you can select \q{\i{CP437}}. If you need support for a numeric \i{code page} which is not listed in the drop-down list, such as code page 866, then you can try entering its name manually (\c{\i{CP866}} for example) in the list box. If the underlying version of Windows has the appropriate translation table installed, PuTTY will use it. \S{config-cjk-ambig-wide} \q{Treat \i{CJK} ambiguous characters as wide} |
︙ | ︙ | |||
1537 1538 1539 1540 1541 1542 1543 | \c colors#256, cols#80, it#8, lines#24, pairs#256, \e bbbbbbbbbb If you do not see \cq{colors#256} in the output, you may need to change your terminal setting. On modern Linux machines, you could try \cq{xterm-256color}. | | | | > | | | | | | > | 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 | \c colors#256, cols#80, it#8, lines#24, pairs#256, \e bbbbbbbbbb If you do not see \cq{colors#256} in the output, you may need to change your terminal setting. On modern Linux machines, you could try \cq{xterm-256color}. \S{config-boldcolour} \q{Indicate bolded text by changing} \cfg{winhelp-topic}{colours.bold} When the server sends a \i{control sequence} indicating that some text should be displayed in \i{bold}, PuTTY can handle this in several ways. It can either change the \i{font} for a bold version, or use the same font in a brighter colour, or it can do both (brighten the colour \e{and} embolden the font). This control lets you choose which. By default bold is indicated by colour, so non-bold text is displayed in light grey and bold text is displayed in bright white (and similarly in other colours). If you change the setting to \q{The font} box, bold and non-bold text will be displayed in the same colour, and instead the font will change to indicate the difference. If you select \q{Both}, the font and the colour will both change. \S{config-logpalette} \q{Attempt to use \i{logical palettes}} \cfg{winhelp-topic}{colours.logpal} Logical palettes are a mechanism by which a Windows application running on an \i{8-bit colour} display can select precisely the colours |
︙ | ︙ | |||
2270 2271 2272 2273 2274 2275 2276 | PuTTY will attempt to use protocol 1 if the server you connect to does not offer protocol 2, and vice versa. If you select \q{1 only} or \q{2 only} here, PuTTY will only connect if the server you connect to offers the SSH protocol version you have specified. | | | < | < < | < < < > > > > > > | | | | | | | | > > | > | > > < | | < > | | | < < | > > | > > > > | | < < > > > < < > | < < < > > > > | 2270 2271 2272 2273 2274 2275 2276 2277 2278 2279 2280 2281 2282 2283 2284 2285 2286 2287 2288 2289 2290 2291 2292 2293 2294 2295 2296 2297 2298 2299 2300 2301 2302 2303 2304 2305 2306 2307 2308 2309 2310 2311 2312 2313 2314 2315 2316 2317 2318 2319 2320 2321 2322 2323 2324 2325 2326 2327 2328 2329 2330 2331 2332 2333 2334 2335 2336 2337 2338 2339 2340 2341 2342 2343 | PuTTY will attempt to use protocol 1 if the server you connect to does not offer protocol 2, and vice versa. If you select \q{1 only} or \q{2 only} here, PuTTY will only connect if the server you connect to offers the SSH protocol version you have specified. \S{config-ssh-sharing} Sharing an SSH connection between PuTTY tools \cfg{winhelp-topic}{ssh.sharing} The controls in this box allow you to configure PuTTY to reuse an existing SSH connection, where possible. The SSH-2 protocol permits you to run multiple data channels over the same SSH connection, so that you can log in just once (and do the expensive encryption setup just once) and then have more than one terminal window open. Each instance of PuTTY can still run at most one terminal session, but using the controls in this box, you can configure PuTTY to check if another instance of itself has already connected to the target host, and if so, share that instance's SSH connection instead of starting a separate new one. To enable this feature, just tick the box \q{Share SSH connections if possible}. Then, whenever you start up a PuTTY session connecting to a particular host, it will try to reuse an existing SSH connection if one is available. For example, selecting \q{Duplicate Session} from the system menu will launch another session on the same host, and if sharing is enabled then it will reuse the existing SSH connection. When this mode is in use, the first PuTTY that connected to a given server becomes the \q{upstream}, which means that it is the one managing the real SSH connection. All subsequent PuTTYs which reuse the connection are referred to as \q{downstreams}: they do not connect to the real server at all, but instead connect to the upstream PuTTY via local inter-process communication methods. For this system to be activated, \e{both} the upstream and downstream instances of PuTTY must have the sharing option enabled. The upstream PuTTY can therefore not terminate until all its downstreams have closed. This is similar to the effect you get with port forwarding or X11 forwarding, in which a PuTTY whose terminal session has already finished will still remain open so as to keep serving forwarded connections. In case you need to configure this system in more detail, there are two additional checkboxes which allow you to specify whether a particular PuTTY can act as an upstream or a downstream or both. (These boxes only take effect if the main \q{Share SSH connections if possible} box is also ticked.) By default both of these boxes are ticked, so that multiple PuTTYs started from the same configuration will designate one of themselves as the upstream and share a single connection; but if for some reason you need a particular PuTTY configuration \e{not} to be an upstream (e.g. because you definitely need it to close promptly) or not to be a downstream (e.g. because it needs to do its own authentication using a special private key) then you can untick one or the other of these boxes. I have referred to \q{PuTTY} throughout the above discussion, but all the other PuTTY tools which make SSH connections can use this mechanism too. For example, if PSCP or PSFTP loads a configuration with sharing enabled, then it can act as a downstream and use an existing SSH connection set up by an instance of GUI PuTTY. The one special case is that PSCP and PSFTP will \e{never} act as upstreams. \H{config-ssh-kex} The Kex panel \# FIXME: This whole section is draft. Feel free to revise. The Kex panel (short for \q{\i{key exchange}}) allows you to configure options related to SSH-2 key exchange. |
︙ | ︙ | |||
2448 2449 2450 2451 2452 2453 2454 2455 2456 2457 2458 2459 2460 2461 | Disabling data-based rekeys entirely is a bad idea. The \i{integrity}, and to a lesser extent, \i{confidentiality} of the SSH-2 protocol depend in part on rekeys occuring before a 32-bit packet sequence number wraps around. Unlike time-based rekeys, data-based rekeys won't occur when the SSH connection is idle, so they shouldn't cause the same problems. The SSH-1 protocol, incidentally, has even weaker integrity protection than SSH-2 without rekeys. \H{config-ssh-auth} The Auth panel The Auth panel allows you to configure \i{authentication} options for SSH sessions. \S{config-ssh-noauth} \q{Bypass authentication entirely} | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 2457 2458 2459 2460 2461 2462 2463 2464 2465 2466 2467 2468 2469 2470 2471 2472 2473 2474 2475 2476 2477 2478 2479 2480 2481 2482 2483 2484 2485 2486 2487 2488 2489 2490 2491 2492 2493 2494 2495 2496 2497 2498 2499 2500 2501 2502 2503 2504 2505 2506 2507 2508 2509 2510 2511 2512 2513 2514 2515 2516 2517 2518 2519 2520 2521 2522 | Disabling data-based rekeys entirely is a bad idea. The \i{integrity}, and to a lesser extent, \i{confidentiality} of the SSH-2 protocol depend in part on rekeys occuring before a 32-bit packet sequence number wraps around. Unlike time-based rekeys, data-based rekeys won't occur when the SSH connection is idle, so they shouldn't cause the same problems. The SSH-1 protocol, incidentally, has even weaker integrity protection than SSH-2 without rekeys. \H{config-ssh-encryption} The Cipher panel \cfg{winhelp-topic}{ssh.ciphers} PuTTY supports a variety of different \i{encryption algorithm}s, and allows you to choose which one you prefer to use. You can do this by dragging the algorithms up and down in the list box (or moving them using the Up and Down buttons) to specify a preference order. When you make an SSH connection, PuTTY will search down the list from the top until it finds an algorithm supported by the server, and then use that. PuTTY currently supports the following algorithms: \b \i{AES} (Rijndael) - 256, 192, or 128-bit SDCTR or CBC (SSH-2 only) \b \i{Arcfour} (RC4) - 256 or 128-bit stream cipher (SSH-2 only) \b \i{Blowfish} - 256-bit SDCTR (SSH-2 only) or 128-bit CBC \b \ii{Triple-DES} - 168-bit SDCTR (SSH-2 only) or CBC \b \ii{Single-DES} - 56-bit CBC (see below for SSH-2) If the algorithm PuTTY finds is below the \q{warn below here} line, you will see a warning box when you make the connection: \c The first cipher supported by the server \c is single-DES, which is below the configured \c warning threshold. \c Do you want to continue with this connection? This warns you that the first available encryption is not a very secure one. Typically you would put the \q{warn below here} line between the encryptions you consider secure and the ones you consider substandard. By default, PuTTY supplies a preference order intended to reflect a reasonable preference in terms of security and speed. In SSH-2, the encryption algorithm is negotiated independently for each direction of the connection, although PuTTY does not support separate configuration of the preference orders. As a result you may get two warnings similar to the one above, possibly with different encryptions. Single-DES is not recommended in the SSH-2 protocol standards, but one or two server implementations do support it. PuTTY can use single-DES to interoperate with these servers if you enable the \q{Enable legacy use of single-DES in SSH-2} option; by default this is disabled and PuTTY will stick to recommended ciphers. \H{config-ssh-auth} The Auth panel The Auth panel allows you to configure \i{authentication} options for SSH sessions. \S{config-ssh-noauth} \q{Bypass authentication entirely} |
︙ | ︙ | |||
3201 3202 3203 3204 3205 3206 3207 3208 3209 3210 3211 3212 3213 3214 | 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 when PuTTY is connecting to a local \I{serial port}\i{serial line}. \S{config-serial-line} Selecting a serial line to connect to | > > > > > > > > > > > > > > > > > > > > > > > | 3262 3263 3264 3265 3266 3267 3268 3269 3270 3271 3272 3273 3274 3275 3276 3277 3278 3279 3280 3281 3282 3283 3284 3285 3286 3287 3288 3289 3290 3291 3292 3293 3294 3295 3296 3297 3298 | 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. \S{config-ssh-bug-winadj} \q{Chokes on PuTTY's SSH-2 \cq{winadj} requests} \cfg{winhelp-topic}{ssh.bugs.winadj} PuTTY sometimes sends a special request to SSH servers in the middle of channel data, with the name \cw{winadj@putty.projects.tartarus.org} (see \k{sshnames-channel}). The purpose of this request is to measure the round-trip time to the server, which PuTTY uses to tune its flow control. The server does not actually have to \e{understand} the message; it is expected to send back a \cw{SSH_MSG_CHANNEL_FAILURE} message indicating that it didn't understand it. (All PuTTY needs for its timing calculations is \e{some} kind of response.) It has been known for some SSH servers to get confused by this message in one way or another \dash because it has a long name, or because they can't cope with unrecognised request names even to the extent of sending back the correct failure response, or because they handle it sensibly but fill up the server's log file with pointless spam, or whatever. PuTTY therefore supports this bug-compatibility flag: if it believes the server has this bug, it will never send its \cq{winadj@putty.projects.tartarus.org} request, and will make do without its timing data. \H{config-serial} The Serial panel The \i{Serial} panel allows you to configure options that only apply when PuTTY is connecting to a local \I{serial port}\i{serial line}. \S{config-serial-line} Selecting a serial line to connect to |
︙ | ︙ |
Changes to doc/errors.but.
|
| | | 1 2 3 4 5 6 7 8 | \define{versioniderrors} \versionid $Id: errors.but 9627 2012-08-26 09:50:57Z jacob $ \C{errors} Common \i{error messages} This chapter lists a number of common error messages which PuTTY and its associated tools can produce, and explains what they mean in more detail. |
︙ | ︙ | |||
54 55 56 57 58 59 60 | You should contact your server's administrator and see whether they expect the host key to have changed. If so, verify the new host key in the same way as you would if it was new. See \k{gs-hostkey} for more information on host keys. | < < < < < < < < < < < < < < | 54 55 56 57 58 59 60 61 62 63 64 65 66 67 | You should contact your server's administrator and see whether they expect the host key to have changed. If so, verify the new host key in the same way as you would if it was new. See \k{gs-hostkey} for more information on host keys. \H{errors-cipher-warning} \q{The first cipher supported by the server is ... below the configured warning threshold} This occurs when the SSH server does not offer any ciphers which you have configured PuTTY to consider strong enough. By default, PuTTY puts up this warning only for \ii{single-DES} and \i{Arcfour} encryption. |
︙ | ︙ |
Changes to doc/faq.but.
|
| | | 1 2 3 4 5 6 7 8 | \define{versionidfaq} \versionid $Id: faq.but 9391 2012-01-30 00:29:32Z jacob $ \A{faq} PuTTY \i{FAQ} This FAQ is published on the PuTTY web site, and also provided as an appendix in the manual. \H{faq-intro} Introduction |
︙ | ︙ | |||
1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 | \q{the application configuration is incorrect}. This is caused by a bug in certain versions of \i{Windows XP} which is triggered by PuTTY 0.58. This was fixed in 0.59. The \W{http://www.chiark.greenend.org.uk/~sgtatham/putty/wishlist/xp-wont-run}{\q{xp-wont-run}} entry in PuTTY's wishlist has more details. \H{faq-secure} Security questions \S{faq-publicpc}{Question} Is it safe for me to download PuTTY and use it on a public PC? It depends on whether you trust that PC. If you don't trust the public PC, don't use PuTTY on it, and don't use any other software | > > > > > > > > > > > > > > > > > | 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 | \q{the application configuration is incorrect}. This is caused by a bug in certain versions of \i{Windows XP} which is triggered by PuTTY 0.58. This was fixed in 0.59. The \W{http://www.chiark.greenend.org.uk/~sgtatham/putty/wishlist/xp-wont-run}{\q{xp-wont-run}} entry in PuTTY's wishlist has more details. \S{faq-system32}{Question} When I put PuTTY in \cw{C:\\WINDOWS\\\i{SYSTEM32}} on my \i{64-bit Windows} system, \i{\q{Duplicate Session}} doesn't work. The short answer is not to put the PuTTY executables in that location. On 64-bit systems, \cw{C:\\WINDOWS\\SYSTEM32} is intended to contain only 64-bit binaries; Windows' 32-bit binaries live in \cw{C:\\WINDOWS\\SYSWOW64}. When a 32-bit program such as PuTTY runs on a 64-bit system, it cannot by default see the \q{real} \cw{C:\\WINDOWS\\SYSTEM32} at all, because the \W{http://msdn.microsoft.com/en-us/library/aa384187(v=vs.85).aspx}{File System Redirector} arranges that the running program sees the appropriate kind of binaries in \cw{SYSTEM32}. Thus, operations in the PuTTY suite that involve it accessing its own executables, such as \i{\q{New Session}} and \q{Duplicate Session}, will not work. \H{faq-secure} Security questions \S{faq-publicpc}{Question} Is it safe for me to download PuTTY and use it on a public PC? It depends on whether you trust that PC. If you don't trust the public PC, don't use PuTTY on it, and don't use any other software |
︙ | ︙ |
Changes to doc/index.but.
|
| | | 1 2 3 4 5 6 7 8 | \define{versionidindex} \versionid $Id: index.but 9391 2012-01-30 00:29:32Z jacob $ \IM{Unix version} Unix version of PuTTY tools \IM{Unix version} Linux version of PuTTY tools \IM{Unix} Unix \IM{Unix} Linux |
︙ | ︙ | |||
845 846 847 848 849 850 851 | \IM{logical host name} host key, caching policy \IM{web browsers} web browser \IM{GSSAPI credential delegation} GSSAPI credential delegation \IM{GSSAPI credential delegation} credential delegation, GSSAPI \IM{GSSAPI credential delegation} delegation, of GSSAPI credentials | > > > > > | 845 846 847 848 849 850 851 852 853 854 855 856 | \IM{logical host name} host key, caching policy \IM{web browsers} web browser \IM{GSSAPI credential delegation} GSSAPI credential delegation \IM{GSSAPI credential delegation} credential delegation, GSSAPI \IM{GSSAPI credential delegation} delegation, of GSSAPI credentials \IM{SYSTEM32} \cw{SYSTEM32} directory, on Windows \IM{64-bit Windows} 64-bit Windows \IM{64-bit Windows} Windows, 64-bit |
Changes to doc/licence.but.
|
| | | | 1 2 3 4 5 6 7 8 9 10 11 12 | \define{versionidlicence} \versionid $Id: licence.but 9993 2013-08-05 15:15:17Z jacob $ \A{licence} PuTTY \ii{Licence} PuTTY is \i{copyright} 1997-2013 Simon Tatham. Portions copyright Robert de Bath, Joris van Rantwijk, Delian Delchev, Andreas Schultz, Jeroen Massar, Wez Furlong, Nicolas Barry, Justin Bradford, Ben Harris, Malcolm Smith, Ahmad Khalifa, Markus Kuhn, Colin Watson, and CORE SDI S.A. Permission is hereby granted, free of charge, to any person |
︙ | ︙ |
Changes to doc/man-ptel.but.
︙ | ︙ | |||
33 34 35 36 37 38 39 | \dd Specify the font to use for normal text displayed in the terminal. \dt \cw{\-fb} \e{font-name} \dd Specify the font to use for bold text displayed in the terminal. If the \cw{BoldAsColour} resource is set to 1 (the default), bold text will be displayed in different colours instead of a different font, | | | | 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 | \dd Specify the font to use for normal text displayed in the terminal. \dt \cw{\-fb} \e{font-name} \dd Specify the font to use for bold text displayed in the terminal. If the \cw{BoldAsColour} resource is set to 1 (the default), bold text will be displayed in different colours instead of a different font, so this option will be ignored. If \cw{BoldAsColour} is set to 0 or 2 and you do not specify a bold font, \cw{puttytel} will overprint the normal font to make it look bolder. \dt \cw{\-fw} \e{font-name} \dd Specify the font to use for double-width characters (typically Chinese, Japanese and Korean text) displayed in the terminal. \dt \cw{\-fwb} \e{font-name} \dd Specify the font to use for bold double-width characters (typically Chinese, Japanese and Korean text). Like \cw{-fb}, this will be ignored unless the \cw{BoldAsColour} resource is set to 0 or 2. \dt \cw{\-geometry} \e{geometry} \dd Specify the size of the terminal, in rows and columns of text. See \e{X(7)} for more information on the syntax of geometry specifications. |
︙ | ︙ | |||
70 71 72 73 74 75 76 | \dt \cw{\-bg} \e{colour} \dd Specify the background colour to use for normal text. \dt \cw{\-bfg} \e{colour} \dd Specify the foreground colour to use for bold text, if the | | | | 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 | \dt \cw{\-bg} \e{colour} \dd Specify the background colour to use for normal text. \dt \cw{\-bfg} \e{colour} \dd Specify the foreground colour to use for bold text, if the \cw{BoldAsColour} resource is set to 1 (the default) or 2. \dt \cw{\-bbg} \e{colour} \dd Specify the foreground colour to use for bold reverse-video text, if the \cw{BoldAsColour} resource is set to 1 (the default) or 2. (This colour is best thought of as the bold version of the background colour; so it only appears when text is displayed \e{in} the background colour.) \dt \cw{\-cfg} \e{colour} \dd Specify the foreground colour to use for text covered by the cursor. |
︙ | ︙ |
Changes to doc/man-pter.but.
︙ | ︙ | |||
53 54 55 56 57 58 59 | \dd Specify the font to use for normal text displayed in the terminal. \dt \cw{\-fb} \e{font-name} \dd Specify the font to use for bold text displayed in the terminal. If the \cw{BoldAsColour} resource is set to 1 (the default), bold text will be displayed in different colours instead of a different font, | | | | 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 | \dd Specify the font to use for normal text displayed in the terminal. \dt \cw{\-fb} \e{font-name} \dd Specify the font to use for bold text displayed in the terminal. If the \cw{BoldAsColour} resource is set to 1 (the default), bold text will be displayed in different colours instead of a different font, so this option will be ignored. If \cw{BoldAsColour} is set to 0 or 2 and you do not specify a bold font, \cw{pterm} will overprint the normal font to make it look bolder. \dt \cw{\-fw} \e{font-name} \dd Specify the font to use for double-width characters (typically Chinese, Japanese and Korean text) displayed in the terminal. \dt \cw{\-fwb} \e{font-name} \dd Specify the font to use for bold double-width characters (typically Chinese, Japanese and Korean text). Like \cw{-fb}, this will be ignored unless the \cw{BoldAsColour} resource is set to 0 or 2. \dt \cw{\-geometry} \e{geometry} \dd Specify the size of the terminal, in rows and columns of text. See \e{X(7)} for more information on the syntax of geometry specifications. |
︙ | ︙ | |||
90 91 92 93 94 95 96 | \dt \cw{\-bg} \e{colour} \dd Specify the background colour to use for normal text. \dt \cw{\-bfg} \e{colour} \dd Specify the foreground colour to use for bold text, if the | | | | 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 | \dt \cw{\-bg} \e{colour} \dd Specify the background colour to use for normal text. \dt \cw{\-bfg} \e{colour} \dd Specify the foreground colour to use for bold text, if the \cw{BoldAsColour} resource is set to 1 (the default) or 2. \dt \cw{\-bbg} \e{colour} \dd Specify the foreground colour to use for bold reverse-video text, if the \cw{BoldAsColour} resource is set to 1 (the default) or 2. (This colour is best thought of as the bold version of the background colour; so it only appears when text is displayed \e{in} the background colour.) \dt \cw{\-cfg} \e{colour} \dd Specify the foreground colour to use for text covered by the cursor. |
︙ | ︙ | |||
494 495 496 497 498 499 500 | controls the font used to display normal text. The default is \q{\cw{fixed}}. \dt \cw{pterm.BoldFont} \dd This resource is the same as the \cw{\-fb} command-line option: it controls the font used to display bold text when \cw{BoldAsColour} | | | | | | > | 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 | controls the font used to display normal text. The default is \q{\cw{fixed}}. \dt \cw{pterm.BoldFont} \dd This resource is the same as the \cw{\-fb} command-line option: it controls the font used to display bold text when \cw{BoldAsColour} is set to 0 or 2. The default is unset (the font will be bolded by printing it twice at a one-pixel offset). \dt \cw{pterm.WideFont} \dd This resource is the same as the \cw{\-fw} command-line option: it controls the font used to display double-width characters. The default is unset (double-width characters cannot be displayed). \dt \cw{pterm.WideBoldFont} \dd This resource is the same as the \cw{\-fwb} command-line option: it controls the font used to display double-width characters in bold, when \cw{BoldAsColour} is set to 0 or 2. The default is unset (double-width characters are displayed in bold by printing them twice at a one-pixel offset). \dt \cw{pterm.ShadowBoldOffset} \dd This resource can be set to an integer; the default is \-1. It specifies the offset at which text is overprinted when using \q{shadow bold} mode. The default (1) means that the text will be printed in the normal place, and also one character to the right; this seems to work well for most X bitmap fonts, which have a blank line of pixels down the right-hand side. For some fonts, you may need to set this to \-1, so that the text is overprinted one pixel to the left; for really large fonts, you may want to set it higher than 1 (in one direction or the other). \dt \cw{pterm.BoldAsColour} \dd This option should be set to either 0, 1, or 2; the default is 1. It specifies how bold text should be displayed. When set to 1, bold text is shown by displaying it in a brighter colour; when set to 0, bold text is shown by displaying it in a heavier font; when set to 2, both effects happen at once (a heavy font \e{and} a brighter colour). \dt \cw{pterm.Colour0}, \cw{pterm.Colour1}, ..., \cw{pterm.Colour21} \dd These options control the various colours used to display text in the \cw{pterm} window. Each one should be specified as a triple of decimal numbers giving red, green and blue values: so that black is \q{\cw{0,0,0}}, white is \q{\cw{255,255,255}}, red is |
︙ | ︙ |
Changes to doc/man-putt.but.
︙ | ︙ | |||
33 34 35 36 37 38 39 | \dt \cw{\-fb} \e{font-name} \dd Specify the font to use for bold text displayed in the terminal. If the \cw{BoldAsColour} resource is set to 1 (the default), bold text will be displayed in different colours instead of a different font, so this option will be ignored. If \cw{BoldAsColour} is set to | | | | 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 | \dt \cw{\-fb} \e{font-name} \dd Specify the font to use for bold text displayed in the terminal. If the \cw{BoldAsColour} resource is set to 1 (the default), bold text will be displayed in different colours instead of a different font, so this option will be ignored. If \cw{BoldAsColour} is set to 0 or 2 and you do not specify a bold font, \cw{putty} will overprint the normal font to make it look bolder. \dt \cw{\-fw} \e{font-name} \dd Specify the font to use for double-width characters (typically Chinese, Japanese and Korean text) displayed in the terminal. \dt \cw{\-fwb} \e{font-name} \dd Specify the font to use for bold double-width characters (typically Chinese, Japanese and Korean text). Like \cw{-fb}, this will be ignored unless the \cw{BoldAsColour} resource is set to 0 or 2. \dt \cw{\-geometry} \e{geometry} \dd Specify the size of the terminal, in rows and columns of text. See \e{X(7)} for more information on the syntax of geometry specifications. |
︙ | ︙ | |||
69 70 71 72 73 74 75 | \dt \cw{\-bg} \e{colour} \dd Specify the background colour to use for normal text. \dt \cw{\-bfg} \e{colour} \dd Specify the foreground colour to use for bold text, if the | | | | 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 | \dt \cw{\-bg} \e{colour} \dd Specify the background colour to use for normal text. \dt \cw{\-bfg} \e{colour} \dd Specify the foreground colour to use for bold text, if the \cw{BoldAsColour} resource is set to 1 (the default) or 2. \dt \cw{\-bbg} \e{colour} \dd Specify the foreground colour to use for bold reverse-video text, if the \cw{BoldAsColour} resource is set to 1 (the default) or 2. (This colour is best thought of as the bold version of the background colour; so it only appears when text is displayed \e{in} the background colour.) \dt \cw{\-cfg} \e{colour} \dd Specify the foreground colour to use for text covered by the cursor. |
︙ | ︙ |
Changes to doc/pgpkeys.but.
|
| | | 1 2 3 4 5 6 7 8 | \define{versionidpgpkeys} \versionid $Id$ \A{pgpkeys} PuTTY download keys and signatures \cfg{winhelp-topic}{pgpfingerprints} \I{verifying new versions}We create \i{PGP signatures} for all the PuTTY files distributed from our web site, so that users can be confident |
︙ | ︙ |
Changes to doc/plink.but.
|
| | | 1 2 3 4 5 6 7 8 | \define{versionidplink} \versionid $Id: plink.but 9998 2013-08-06 17:09:07Z simon $ \C{plink} Using the command-line connection tool \i{Plink} \i{Plink} (PuTTY Link) is a command-line connection tool similar to UNIX \c{ssh}. It is mostly used for \i{automated operations}, such as making CVS access a repository on a remote server. |
︙ | ︙ | |||
39 40 41 42 43 44 45 | Once you've got a console window to type into, you can just type \c{plink} on its own to bring up a usage message. This tells you the version of Plink you're using, and gives you a brief summary of how to use Plink: \c Z:\sysosd>plink \c PuTTY Link: command-line connection utility | | | 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 | Once you've got a console window to type into, you can just type \c{plink} on its own to bring up a usage message. This tells you the version of Plink you're using, and gives you a brief summary of how to use Plink: \c Z:\sysosd>plink \c PuTTY Link: command-line connection utility \c Release 0.63 \c Usage: plink [options] [user@]host [command] \c ("host" can also be a PuTTY saved session name) \c Options: \c -V print version information and exit \c -pgpfp print PGP key fingerprints and exit \c -v show verbose messages \c -load sessname Load settings from saved session |
︙ | ︙ |
Changes to doc/pscp.but.
|
| | | 1 2 3 4 5 6 7 8 | \define{versionidpscp} \versionid $Id: pscp.but 9998 2013-08-06 17:09:07Z simon $ \#FIXME: Need examples \C{pscp} Using \i{PSCP} to transfer files securely \i{PSCP}, the PuTTY Secure Copy client, is a tool for \i{transferring files} securely between computers using an SSH connection. |
︙ | ︙ | |||
37 38 39 40 41 42 43 | Once you've got a console window to type into, you can just type \c{pscp} on its own to bring up a usage message. This tells you the version of PSCP you're using, and gives you a brief summary of how to use PSCP: \c Z:\owendadmin>pscp \c PuTTY Secure Copy client | | | 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | Once you've got a console window to type into, you can just type \c{pscp} on its own to bring up a usage message. This tells you the version of PSCP you're using, and gives you a brief summary of how to use PSCP: \c Z:\owendadmin>pscp \c PuTTY Secure Copy client \c Release 0.63 \c Usage: pscp [options] [user@]host:source target \c pscp [options] source [source...] [user@]host:target \c pscp [options] -ls [user@]host:filespec \c Options: \c -V print version information and exit \c -pgpfp print PGP key fingerprints and exit \c -p preserve file attributes |
︙ | ︙ |
Changes to doc/pubkey.but.
|
| | | 1 2 3 4 5 6 7 8 | \define{versionidpubkey} \versionid $Id: pubkey.but 9422 2012-03-04 01:01:11Z jacob $ \C{pubkey} Using public keys for SSH authentication \H{pubkey-intro} \ii{Public key authentication} - an introduction Public key authentication is an alternative means of identifying yourself to a login server, instead of typing a password. It is more |
︙ | ︙ | |||
147 148 149 150 151 152 153 | \cfg{winhelp-topic}{puttygen.bits} The \q{Number of bits} input box allows you to choose the strength of the key PuTTYgen will generate. Currently 1024 bits should be sufficient for most purposes. | < < < < < < < < < < < < | 147 148 149 150 151 152 153 154 155 156 157 158 159 160 | \cfg{winhelp-topic}{puttygen.bits} The \q{Number of bits} input box allows you to choose the strength of the key PuTTYgen will generate. Currently 1024 bits should be sufficient for most purposes. \S{puttygen-generate} The \q{Generate} button \cfg{winhelp-topic}{puttygen.generate} Once you have chosen the type of key you want, and the strength of the key, press the \q{Generate} button and PuTTYgen will begin the process of actually generating the key. |
︙ | ︙ |
Added errsock.c.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 | /* * A dummy Socket implementation which just holds an error message. */ #include <stdio.h> #include <assert.h> #define DEFINE_PLUG_METHOD_MACROS #include "tree234.h" #include "putty.h" #include "network.h" typedef struct Socket_error_tag *Error_Socket; struct Socket_error_tag { const struct socket_function_table *fn; /* the above variable absolutely *must* be the first in this structure */ char *error; Plug plug; }; static Plug sk_error_plug(Socket s, Plug p) { Error_Socket ps = (Error_Socket) s; Plug ret = ps->plug; if (p) ps->plug = p; return ret; } static void sk_error_close(Socket s) { Error_Socket ps = (Error_Socket) s; sfree(ps->error); sfree(ps); } static const char *sk_error_socket_error(Socket s) { Error_Socket ps = (Error_Socket) s; return ps->error; } Socket new_error_socket(const char *errmsg, Plug plug) { static const struct socket_function_table socket_fn_table = { sk_error_plug, sk_error_close, NULL /* write */, NULL /* write_oob */, NULL /* write_eof */, NULL /* flush */, NULL /* set_frozen */, sk_error_socket_error }; Error_Socket ret; ret = snew(struct Socket_error_tag); ret->fn = &socket_fn_table; ret->plug = plug; ret->error = dupstr(errmsg); return (Socket) ret; } |
Changes to import.c.
︙ | ︙ | |||
285 286 287 288 289 290 291 | static int ssh2_read_mpint(void *data, int len, struct mpint_pos *ret) { int bytes; unsigned char *d = (unsigned char *) data; if (len < 4) goto error; | | | | 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 | static int ssh2_read_mpint(void *data, int len, struct mpint_pos *ret) { int bytes; unsigned char *d = (unsigned char *) data; if (len < 4) goto error; bytes = toint(GET_32BIT(d)); if (bytes < 0 || len-4 < bytes) goto error; ret->start = d + 4; ret->bytes = bytes; return bytes+4; error: |
︙ | ︙ | |||
317 318 319 320 321 322 323 | int keyblob_len, keyblob_size; }; static struct openssh_key *load_openssh_key(const Filename *filename, const char **errmsg_p) { struct openssh_key *ret; | | | | 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 | int keyblob_len, keyblob_size; }; static struct openssh_key *load_openssh_key(const Filename *filename, const char **errmsg_p) { struct openssh_key *ret; FILE *fp = NULL; char *line = NULL; char *errmsg, *p; int headers_done; char base64_bit[4]; int base64_chars = 0; ret = snew(struct openssh_key); ret->keyblob = NULL; ret->keyblob_len = ret->keyblob_size = 0; ret->encrypted = 0; memset(ret->iv, 0, sizeof(ret->iv)); fp = f_open(filename, "r", FALSE); if (!fp) { errmsg = "unable to open key file"; goto error; } if (!(line = fgetline(fp))) { errmsg = "unexpected end of file"; |
︙ | ︙ | |||
354 355 356 357 358 359 360 | ret->type = OSSH_RSA; else if (!strcmp(line, "-----BEGIN DSA PRIVATE KEY-----")) ret->type = OSSH_DSA; else { errmsg = "unrecognised key type"; goto error; } | | | > > > | 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 | ret->type = OSSH_RSA; else if (!strcmp(line, "-----BEGIN DSA PRIVATE KEY-----")) ret->type = OSSH_DSA; else { errmsg = "unrecognised key type"; goto error; } smemclr(line, strlen(line)); sfree(line); line = NULL; headers_done = 0; while (1) { if (!(line = fgetline(fp))) { errmsg = "unexpected end of file"; goto error; } strip_crlf(line); if (0 == strncmp(line, "-----END ", 9) && 0 == strcmp(line+strlen(line)-16, "PRIVATE KEY-----")) { sfree(line); line = NULL; break; /* done */ } if ((p = strchr(line, ':')) != NULL) { if (headers_done) { errmsg = "header found in body of key data"; goto error; } *p++ = '\0'; while (*p && isspace((unsigned char)*p)) p++; |
︙ | ︙ | |||
438 439 440 441 442 443 444 | ret->keyblob = sresize(ret->keyblob, ret->keyblob_size, unsigned char); } memcpy(ret->keyblob + ret->keyblob_len, out, len); ret->keyblob_len += len; | | | > > > | | | | | > | | | 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 | ret->keyblob = sresize(ret->keyblob, ret->keyblob_size, unsigned char); } memcpy(ret->keyblob + ret->keyblob_len, out, len); ret->keyblob_len += len; smemclr(out, sizeof(out)); } p++; } } smemclr(line, strlen(line)); sfree(line); line = NULL; } fclose(fp); fp = NULL; if (ret->keyblob_len == 0 || !ret->keyblob) { errmsg = "key body not present"; goto error; } if (ret->encrypted && ret->keyblob_len % 8 != 0) { errmsg = "encrypted key blob is not a multiple of cipher block size"; goto error; } smemclr(base64_bit, sizeof(base64_bit)); if (errmsg_p) *errmsg_p = NULL; return ret; error: if (line) { smemclr(line, strlen(line)); sfree(line); line = NULL; } smemclr(base64_bit, sizeof(base64_bit)); if (ret) { if (ret->keyblob) { smemclr(ret->keyblob, ret->keyblob_size); sfree(ret->keyblob); } smemclr(ret, sizeof(*ret)); sfree(ret); } if (errmsg_p) *errmsg_p = errmsg; if (fp) fclose(fp); return NULL; } int openssh_encrypted(const Filename *filename) { struct openssh_key *key = load_openssh_key(filename, NULL); int ret; if (!key) return 0; ret = key->encrypted; smemclr(key->keyblob, key->keyblob_size); sfree(key->keyblob); smemclr(key, sizeof(*key)); sfree(key); return ret; } struct ssh2_userkey *openssh_read(const Filename *filename, char *passphrase, const char **errmsg_p) { |
︙ | ︙ | |||
560 561 562 563 564 565 566 | ctx = aes_make_context(); aes128_key(ctx, keybuf); aes_iv(ctx, (unsigned char *)key->iv); aes_ssh2_decrypt_blk(ctx, key->keyblob, key->keyblob_len); aes_free_context(ctx); } | | | | 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 | ctx = aes_make_context(); aes128_key(ctx, keybuf); aes_iv(ctx, (unsigned char *)key->iv); aes_ssh2_decrypt_blk(ctx, key->keyblob, key->keyblob_len); aes_free_context(ctx); } smemclr(&md5c, sizeof(md5c)); smemclr(keybuf, sizeof(keybuf)); } /* * Now we have a decrypted key blob, which contains an ASN.1 * encoded private key. We must now untangle the ASN.1. * * We expect the whole key blob to be formatted as a SEQUENCE |
︙ | ︙ | |||
584 585 586 587 588 589 590 | * * - For DSA, we expect them to be 0, p, q, g, y, x in that * order. */ p = key->keyblob; | | > | | 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 | * * - For DSA, we expect them to be 0, p, q, g, y, x in that * order. */ p = key->keyblob; /* Expect the SEQUENCE header. Take its absence as a failure to * decrypt, if the key was encrypted. */ ret = ber_read_id_len(p, key->keyblob_len, &id, &len, &flags); p += ret; if (ret < 0 || id != 16) { errmsg = "ASN.1 decoding failure"; retval = key->encrypted ? SSH2_WRONG_PASSPHRASE : NULL; goto error; } /* Expect a load of INTEGERs. */ if (key->type == OSSH_RSA) num_integers = 9; else if (key->type == OSSH_DSA) |
︙ | ︙ | |||
621 622 623 624 625 626 627 | for (i = 0; i < num_integers; i++) { ret = ber_read_id_len(p, key->keyblob+key->keyblob_len-p, &id, &len, &flags); p += ret; if (ret < 0 || id != 2 || key->keyblob+key->keyblob_len-p < len) { errmsg = "ASN.1 decoding failure"; | | | 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 | for (i = 0; i < num_integers; i++) { ret = ber_read_id_len(p, key->keyblob+key->keyblob_len-p, &id, &len, &flags); p += ret; if (ret < 0 || id != 2 || key->keyblob+key->keyblob_len-p < len) { errmsg = "ASN.1 decoding failure"; retval = key->encrypted ? SSH2_WRONG_PASSPHRASE : NULL; goto error; } if (i == 0) { /* * The first integer should be zero always (I think * this is some sort of version indication). |
︙ | ︙ | |||
694 695 696 697 698 699 700 | retkey->comment = dupstr("imported-openssh-key"); errmsg = NULL; /* no error */ retval = retkey; error: if (blob) { | | | | | 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 | retkey->comment = dupstr("imported-openssh-key"); errmsg = NULL; /* no error */ retval = retkey; error: if (blob) { smemclr(blob, blobsize); sfree(blob); } smemclr(key->keyblob, key->keyblob_size); sfree(key->keyblob); smemclr(key, sizeof(*key)); sfree(key); if (errmsg_p) *errmsg_p = errmsg; return retval; } int openssh_write(const Filename *filename, struct ssh2_userkey *key, char *passphrase) |
︙ | ︙ | |||
736 737 738 739 740 741 742 743 744 745 746 747 748 749 | * key blob, and also decide on the header line. */ if (key->alg == &ssh_rsa) { int pos; struct mpint_pos n, e, d, p, q, iqmp, dmp1, dmq1; Bignum bd, bp, bq, bdmp1, bdmq1; pos = 4 + GET_32BIT(pubblob); pos += ssh2_read_mpint(pubblob+pos, publen-pos, &e); pos += ssh2_read_mpint(pubblob+pos, publen-pos, &n); pos = 0; pos += ssh2_read_mpint(privblob+pos, privlen-pos, &d); pos += ssh2_read_mpint(privblob+pos, privlen-pos, &p); pos += ssh2_read_mpint(privblob+pos, privlen-pos, &q); | > > > > | 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 | * key blob, and also decide on the header line. */ if (key->alg == &ssh_rsa) { int pos; struct mpint_pos n, e, d, p, q, iqmp, dmp1, dmq1; Bignum bd, bp, bq, bdmp1, bdmq1; /* * These blobs were generated from inside PuTTY, so we needn't * treat them as untrusted. */ pos = 4 + GET_32BIT(pubblob); pos += ssh2_read_mpint(pubblob+pos, publen-pos, &e); pos += ssh2_read_mpint(pubblob+pos, publen-pos, &n); pos = 0; pos += ssh2_read_mpint(privblob+pos, privlen-pos, &d); pos += ssh2_read_mpint(privblob+pos, privlen-pos, &p); pos += ssh2_read_mpint(privblob+pos, privlen-pos, &q); |
︙ | ︙ | |||
789 790 791 792 793 794 795 796 797 798 799 800 801 802 | nnumbers = 9; header = "-----BEGIN RSA PRIVATE KEY-----\n"; footer = "-----END RSA PRIVATE KEY-----\n"; } else if (key->alg == &ssh_dss) { int pos; struct mpint_pos p, q, g, y, x; pos = 4 + GET_32BIT(pubblob); pos += ssh2_read_mpint(pubblob+pos, publen-pos, &p); pos += ssh2_read_mpint(pubblob+pos, publen-pos, &q); pos += ssh2_read_mpint(pubblob+pos, publen-pos, &g); pos += ssh2_read_mpint(pubblob+pos, publen-pos, &y); pos = 0; pos += ssh2_read_mpint(privblob+pos, privlen-pos, &x); | > > > > | 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 | nnumbers = 9; header = "-----BEGIN RSA PRIVATE KEY-----\n"; footer = "-----END RSA PRIVATE KEY-----\n"; } else if (key->alg == &ssh_dss) { int pos; struct mpint_pos p, q, g, y, x; /* * These blobs were generated from inside PuTTY, so we needn't * treat them as untrusted. */ pos = 4 + GET_32BIT(pubblob); pos += ssh2_read_mpint(pubblob+pos, publen-pos, &p); pos += ssh2_read_mpint(pubblob+pos, publen-pos, &q); pos += ssh2_read_mpint(pubblob+pos, publen-pos, &g); pos += ssh2_read_mpint(pubblob+pos, publen-pos, &y); pos = 0; pos += ssh2_read_mpint(privblob+pos, privlen-pos, &x); |
︙ | ︙ | |||
907 908 909 910 911 912 913 | MD5Final(keybuf+16, &md5c); /* * Now encrypt the key blob. */ des3_encrypt_pubkey_ossh(keybuf, iv, outblob, outlen); | | | | | | | | | 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 | MD5Final(keybuf+16, &md5c); /* * Now encrypt the key blob. */ des3_encrypt_pubkey_ossh(keybuf, iv, outblob, outlen); smemclr(&md5c, sizeof(md5c)); smemclr(keybuf, sizeof(keybuf)); } /* * And save it. We'll use Unix line endings just in case it's * subsequently transferred in binary mode. */ fp = f_open(filename, "wb", TRUE); /* ensure Unix line endings */ if (!fp) goto error; fputs(header, fp); if (passphrase) { fprintf(fp, "Proc-Type: 4,ENCRYPTED\nDEK-Info: DES-EDE3-CBC,"); for (i = 0; i < 8; i++) fprintf(fp, "%02X", iv[i]); fprintf(fp, "\n\n"); } base64_encode(fp, outblob, outlen, 64); fputs(footer, fp); fclose(fp); ret = 1; error: if (outblob) { smemclr(outblob, outlen); sfree(outblob); } if (spareblob) { smemclr(spareblob, sparelen); sfree(spareblob); } if (privblob) { smemclr(privblob, privlen); sfree(privblob); } if (pubblob) { smemclr(pubblob, publen); sfree(pubblob); } return ret; } /* ---------------------------------------------------------------------- * Code to read ssh.com private keys. |
︙ | ︙ | |||
1049 1050 1051 1052 1053 1054 1055 | int base64_chars = 0; ret = snew(struct sshcom_key); ret->comment[0] = '\0'; ret->keyblob = NULL; ret->keyblob_len = ret->keyblob_size = 0; | | | | > > > | 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 | int base64_chars = 0; ret = snew(struct sshcom_key); ret->comment[0] = '\0'; ret->keyblob = NULL; ret->keyblob_len = ret->keyblob_size = 0; fp = f_open(filename, "r", FALSE); if (!fp) { errmsg = "unable to open key file"; goto error; } if (!(line = fgetline(fp))) { errmsg = "unexpected end of file"; goto error; } strip_crlf(line); if (0 != strcmp(line, "---- BEGIN SSH2 ENCRYPTED PRIVATE KEY ----")) { errmsg = "file does not begin with ssh.com key header"; goto error; } smemclr(line, strlen(line)); sfree(line); line = NULL; headers_done = 0; while (1) { if (!(line = fgetline(fp))) { errmsg = "unexpected end of file"; goto error; } strip_crlf(line); if (!strcmp(line, "---- END SSH2 ENCRYPTED PRIVATE KEY ----")) { sfree(line); line = NULL; break; /* done */ } if ((p = strchr(line, ':')) != NULL) { if (headers_done) { errmsg = "header found in body of key data"; goto error; } *p++ = '\0'; while (*p && isspace((unsigned char)*p)) p++; |
︙ | ︙ | |||
1108 1109 1110 1111 1112 1113 1114 | line2len = strlen(line2); line = sresize(line, len + line2len + 1, char); strcpy(line + len - 1, line2); len += line2len - 1; assert(!line[len]); | | | 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 | line2len = strlen(line2); line = sresize(line, len + line2len + 1, char); strcpy(line + len - 1, line2); len += line2len - 1; assert(!line[len]); smemclr(line2, strlen(line2)); sfree(line2); line2 = NULL; } p = line + hdrstart; strip_crlf(p); if (!strcmp(line, "Comment")) { /* Strip quotes in comment if present. */ |
︙ | ︙ | |||
1154 1155 1156 1157 1158 1159 1160 | memcpy(ret->keyblob + ret->keyblob_len, out, len); ret->keyblob_len += len; } p++; } } | | > > > > | | | > > | | | > < | | > | | > | | | | | > > > < | | 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 | memcpy(ret->keyblob + ret->keyblob_len, out, len); ret->keyblob_len += len; } p++; } } smemclr(line, strlen(line)); sfree(line); line = NULL; } if (ret->keyblob_len == 0 || !ret->keyblob) { errmsg = "key body not present"; goto error; } fclose(fp); if (errmsg_p) *errmsg_p = NULL; return ret; error: if (fp) fclose(fp); if (line) { smemclr(line, strlen(line)); sfree(line); line = NULL; } if (ret) { if (ret->keyblob) { smemclr(ret->keyblob, ret->keyblob_size); sfree(ret->keyblob); } smemclr(ret, sizeof(*ret)); sfree(ret); } if (errmsg_p) *errmsg_p = errmsg; return NULL; } int sshcom_encrypted(const Filename *filename, char **comment) { struct sshcom_key *key = load_sshcom_key(filename, NULL); int pos, len, answer; answer = 0; *comment = NULL; if (!key) goto done; /* * Check magic number. */ if (GET_32BIT(key->keyblob) != 0x3f6ff9eb) { goto done; /* key is invalid */ } /* * Find the cipher-type string. */ pos = 8; if (key->keyblob_len < pos+4) goto done; /* key is far too short */ len = toint(GET_32BIT(key->keyblob + pos)); if (len < 0 || len > key->keyblob_len - pos - 4) goto done; /* key is far too short */ pos += 4 + len; /* skip key type */ len = toint(GET_32BIT(key->keyblob + pos)); /* find cipher-type length */ if (len < 0 || len > key->keyblob_len - pos - 4) goto done; /* cipher type string is incomplete */ if (len != 4 || 0 != memcmp(key->keyblob + pos + 4, "none", 4)) answer = 1; done: if (key) { *comment = dupstr(key->comment); smemclr(key->keyblob, key->keyblob_size); sfree(key->keyblob); smemclr(key, sizeof(*key)); sfree(key); } else { *comment = dupstr(""); } return answer; } static int sshcom_read_mpint(void *data, int len, struct mpint_pos *ret) { unsigned bits, bytes; unsigned char *d = (unsigned char *) data; if (len < 4) goto error; bits = GET_32BIT(d); bytes = (bits + 7) / 8; |
︙ | ︙ | |||
1300 1301 1302 1303 1304 1305 1306 | } /* * Determine the key type. */ pos = 8; if (key->keyblob_len < pos+4 || | > | > | > | | 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 | } /* * Determine the key type. */ pos = 8; if (key->keyblob_len < pos+4 || (len = toint(GET_32BIT(key->keyblob + pos))) < 0 || len > key->keyblob_len - pos - 4) { errmsg = "key blob does not contain a key type string"; goto error; } if (len > sizeof(prefix_rsa) - 1 && !memcmp(key->keyblob+pos+4, prefix_rsa, sizeof(prefix_rsa) - 1)) { type = RSA; } else if (len > sizeof(prefix_dsa) - 1 && !memcmp(key->keyblob+pos+4, prefix_dsa, sizeof(prefix_dsa) - 1)) { type = DSA; } else { errmsg = "key is of unknown type"; goto error; } pos += 4+len; /* * Determine the cipher type. */ if (key->keyblob_len < pos+4 || (len = toint(GET_32BIT(key->keyblob + pos))) < 0 || len > key->keyblob_len - pos - 4) { errmsg = "key blob does not contain a cipher type string"; goto error; } if (len == 4 && !memcmp(key->keyblob+pos+4, "none", 4)) encrypted = 0; else if (len == 8 && !memcmp(key->keyblob+pos+4, "3des-cbc", 8)) encrypted = 1; else { errmsg = "key encryption is of unknown type"; goto error; } pos += 4+len; /* * Get hold of the encrypted part of the key. */ if (key->keyblob_len < pos+4 || (len = toint(GET_32BIT(key->keyblob + pos))) < 0 || len > key->keyblob_len - pos - 4) { errmsg = "key blob does not contain actual key data"; goto error; } ciphertext = (char *)key->keyblob + pos + 4; cipherlen = len; if (cipherlen == 0) { errmsg = "length of key data is zero"; |
︙ | ︙ | |||
1386 1387 1388 1389 1390 1391 1392 | /* * Now decrypt the key blob. */ memset(iv, 0, sizeof(iv)); des3_decrypt_pubkey_ossh(keybuf, iv, (unsigned char *)ciphertext, cipherlen); | | | | | 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 | /* * Now decrypt the key blob. */ memset(iv, 0, sizeof(iv)); des3_decrypt_pubkey_ossh(keybuf, iv, (unsigned char *)ciphertext, cipherlen); smemclr(&md5c, sizeof(md5c)); smemclr(keybuf, sizeof(keybuf)); /* * Hereafter we return WRONG_PASSPHRASE for any parsing * error. (But only if we've just tried to decrypt it! * Returning WRONG_PASSPHRASE for an unencrypted key is * automatic doom.) */ if (encrypted) ret = SSH2_WRONG_PASSPHRASE; } /* * Strip away the containing string to get to the real meat. */ len = toint(GET_32BIT(ciphertext)); if (len < 0 || len > cipherlen-4) { errmsg = "containing string was ill-formed"; goto error; } ciphertext += 4; cipherlen = len; |
︙ | ︙ | |||
1443 1444 1445 1446 1447 1448 1449 | pos += put_mp(blob+pos, n.start, n.bytes); publen = pos; pos += put_string(blob+pos, d.start, d.bytes); pos += put_mp(blob+pos, q.start, q.bytes); pos += put_mp(blob+pos, p.start, p.bytes); pos += put_mp(blob+pos, u.start, u.bytes); privlen = pos - publen; | | > > > | 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 | pos += put_mp(blob+pos, n.start, n.bytes); publen = pos; pos += put_string(blob+pos, d.start, d.bytes); pos += put_mp(blob+pos, q.start, q.bytes); pos += put_mp(blob+pos, p.start, p.bytes); pos += put_mp(blob+pos, u.start, u.bytes); privlen = pos - publen; } else { struct mpint_pos p, q, g, x, y; int pos = 4; assert(type == DSA); /* the only other option from the if above */ if (GET_32BIT(ciphertext) != 0) { errmsg = "predefined DSA parameters not supported"; goto error; } pos += sshcom_read_mpint(ciphertext+pos, cipherlen-pos, &p); pos += sshcom_read_mpint(ciphertext+pos, cipherlen-pos, &g); pos += sshcom_read_mpint(ciphertext+pos, cipherlen-pos, &q); |
︙ | ︙ | |||
1470 1471 1472 1473 1474 1475 1476 | pos += put_mp(blob+pos, p.start, p.bytes); pos += put_mp(blob+pos, q.start, q.bytes); pos += put_mp(blob+pos, g.start, g.bytes); pos += put_mp(blob+pos, y.start, y.bytes); publen = pos; pos += put_mp(blob+pos, x.start, x.bytes); privlen = pos - publen; | < < > | | | | 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 | pos += put_mp(blob+pos, p.start, p.bytes); pos += put_mp(blob+pos, q.start, q.bytes); pos += put_mp(blob+pos, g.start, g.bytes); pos += put_mp(blob+pos, y.start, y.bytes); publen = pos; pos += put_mp(blob+pos, x.start, x.bytes); privlen = pos - publen; } assert(privlen > 0); /* should have bombed by now if not */ retkey = snew(struct ssh2_userkey); retkey->alg = alg; retkey->data = alg->createkey(blob, publen, blob+publen, privlen); if (!retkey->data) { sfree(retkey); errmsg = "unable to create key data structure"; goto error; } retkey->comment = dupstr(key->comment); errmsg = NULL; /* no error */ ret = retkey; error: if (blob) { smemclr(blob, blobsize); sfree(blob); } smemclr(key->keyblob, key->keyblob_size); sfree(key->keyblob); smemclr(key, sizeof(*key)); sfree(key); if (errmsg_p) *errmsg_p = errmsg; return ret; } int sshcom_write(const Filename *filename, struct ssh2_userkey *key, char *passphrase) |
︙ | ︙ | |||
1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 | * Find the sequence of integers to be encoded into the OpenSSH * key blob, and also decide on the header line. */ if (key->alg == &ssh_rsa) { int pos; struct mpint_pos n, e, d, p, q, iqmp; pos = 4 + GET_32BIT(pubblob); pos += ssh2_read_mpint(pubblob+pos, publen-pos, &e); pos += ssh2_read_mpint(pubblob+pos, publen-pos, &n); pos = 0; pos += ssh2_read_mpint(privblob+pos, privlen-pos, &d); pos += ssh2_read_mpint(privblob+pos, privlen-pos, &p); pos += ssh2_read_mpint(privblob+pos, privlen-pos, &q); | > > > > | 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 | * Find the sequence of integers to be encoded into the OpenSSH * key blob, and also decide on the header line. */ if (key->alg == &ssh_rsa) { int pos; struct mpint_pos n, e, d, p, q, iqmp; /* * These blobs were generated from inside PuTTY, so we needn't * treat them as untrusted. */ pos = 4 + GET_32BIT(pubblob); pos += ssh2_read_mpint(pubblob+pos, publen-pos, &e); pos += ssh2_read_mpint(pubblob+pos, publen-pos, &n); pos = 0; pos += ssh2_read_mpint(privblob+pos, privlen-pos, &d); pos += ssh2_read_mpint(privblob+pos, privlen-pos, &p); pos += ssh2_read_mpint(privblob+pos, privlen-pos, &q); |
︙ | ︙ | |||
1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 | nnumbers = 6; initial_zero = 0; type = "if-modn{sign{rsa-pkcs1-sha1},encrypt{rsa-pkcs1v2-oaep}}"; } else if (key->alg == &ssh_dss) { int pos; struct mpint_pos p, q, g, y, x; pos = 4 + GET_32BIT(pubblob); pos += ssh2_read_mpint(pubblob+pos, publen-pos, &p); pos += ssh2_read_mpint(pubblob+pos, publen-pos, &q); pos += ssh2_read_mpint(pubblob+pos, publen-pos, &g); pos += ssh2_read_mpint(pubblob+pos, publen-pos, &y); pos = 0; pos += ssh2_read_mpint(privblob+pos, privlen-pos, &x); | > > > > | 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 | nnumbers = 6; initial_zero = 0; type = "if-modn{sign{rsa-pkcs1-sha1},encrypt{rsa-pkcs1v2-oaep}}"; } else if (key->alg == &ssh_dss) { int pos; struct mpint_pos p, q, g, y, x; /* * These blobs were generated from inside PuTTY, so we needn't * treat them as untrusted. */ pos = 4 + GET_32BIT(pubblob); pos += ssh2_read_mpint(pubblob+pos, publen-pos, &p); pos += ssh2_read_mpint(pubblob+pos, publen-pos, &q); pos += ssh2_read_mpint(pubblob+pos, publen-pos, &g); pos += ssh2_read_mpint(pubblob+pos, publen-pos, &y); pos = 0; pos += ssh2_read_mpint(privblob+pos, privlen-pos, &x); |
︙ | ︙ | |||
1660 1661 1662 1663 1664 1665 1666 | /* * Now decrypt the key blob. */ memset(iv, 0, sizeof(iv)); des3_encrypt_pubkey_ossh(keybuf, iv, (unsigned char *)ciphertext, cipherlen); | | | | | 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 | /* * Now decrypt the key blob. */ memset(iv, 0, sizeof(iv)); des3_encrypt_pubkey_ossh(keybuf, iv, (unsigned char *)ciphertext, cipherlen); smemclr(&md5c, sizeof(md5c)); smemclr(keybuf, sizeof(keybuf)); } /* * And save it. We'll use Unix line endings just in case it's * subsequently transferred in binary mode. */ fp = f_open(filename, "wb", TRUE); /* ensure Unix line endings */ if (!fp) goto error; fputs("---- BEGIN SSH2 ENCRYPTED PRIVATE KEY ----\n", fp); fprintf(fp, "Comment: \""); /* * Comment header is broken with backslash-newline if it goes * over 70 chars. Although it's surrounded by quotes, it |
︙ | ︙ | |||
1696 1697 1698 1699 1700 1701 1702 | base64_encode(fp, outblob, pos, 70); fputs("---- END SSH2 ENCRYPTED PRIVATE KEY ----\n", fp); fclose(fp); ret = 1; error: if (outblob) { | | | | | 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 | base64_encode(fp, outblob, pos, 70); fputs("---- END SSH2 ENCRYPTED PRIVATE KEY ----\n", fp); fclose(fp); ret = 1; error: if (outblob) { smemclr(outblob, outlen); sfree(outblob); } if (privblob) { smemclr(privblob, privlen); sfree(privblob); } if (pubblob) { smemclr(pubblob, publen); sfree(pubblob); } return ret; } |
Changes to ldisc.c.
︙ | ︙ | |||
8 9 10 11 12 13 14 | #include <stdio.h> #include <ctype.h> #include "putty.h" #include "terminal.h" #include "ldisc.h" | | | | | | 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | #include <stdio.h> #include <ctype.h> #include "putty.h" #include "terminal.h" #include "ldisc.h" #define ECHOING (ldisc->localecho == FORCE_ON || \ (ldisc->localecho == AUTO && \ (ldisc->back->ldisc(ldisc->backhandle, LD_ECHO) || \ term_ldisc(ldisc->term, LD_ECHO)))) #define EDITING (ldisc->localedit == FORCE_ON || \ (ldisc->localedit == AUTO && \ (ldisc->back->ldisc(ldisc->backhandle, LD_EDIT) || \ term_ldisc(ldisc->term, LD_EDIT)))) static void c_write(Ldisc ldisc, char *buf, int len) { from_backend(ldisc->frontend, 0, buf, len); } |
︙ | ︙ | |||
72 73 74 75 76 77 78 | while (n--) c_write(ldisc, "\010 \010", 3); } #define CTRL(x) (x^'@') #define KCTRL(x) ((x^'@') | 0x100) | | < > > > > > > > > > > > > > | 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 | while (n--) c_write(ldisc, "\010 \010", 3); } #define CTRL(x) (x^'@') #define KCTRL(x) ((x^'@') | 0x100) void *ldisc_create(Conf *conf, Terminal *term, Backend *back, void *backhandle, void *frontend) { Ldisc ldisc = snew(struct ldisc_tag); ldisc->buf = NULL; ldisc->buflen = 0; ldisc->bufsiz = 0; ldisc->quotenext = 0; ldisc->back = back; ldisc->backhandle = backhandle; ldisc->term = term; ldisc->frontend = frontend; ldisc_configure(ldisc, conf); /* Link ourselves into the backend and the terminal */ if (term) term->ldisc = ldisc; if (back) back->provide_ldisc(backhandle, ldisc); return ldisc; } void ldisc_configure(void *handle, Conf *conf) { Ldisc ldisc = (Ldisc) handle; ldisc->telnet_keyboard = conf_get_int(conf, CONF_telnet_keyboard); ldisc->telnet_newline = conf_get_int(conf, CONF_telnet_newline); ldisc->protocol = conf_get_int(conf, CONF_protocol); ldisc->localecho = conf_get_int(conf, CONF_localecho); ldisc->localedit = conf_get_int(conf, CONF_localedit); } void ldisc_free(void *handle) { Ldisc ldisc = (Ldisc) handle; if (ldisc->term) ldisc->term->ldisc = NULL; |
︙ | ︙ | |||
129 130 131 132 133 134 135 136 137 138 139 140 141 142 | } /* * Notify the front end that something was pressed, in case * it's depending on finding out (e.g. keypress termination for * Close On Exit). */ frontend_keypress(ldisc->frontend); /* * Less than zero means null terminated special string. */ if (len < 0) { len = strlen(buf); keyflag = KCTRL('@'); | > > > > > > > > > > > > | 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 | } /* * Notify the front end that something was pressed, in case * it's depending on finding out (e.g. keypress termination for * Close On Exit). */ frontend_keypress(ldisc->frontend); if (interactive && ldisc->term) { /* * Interrupt a paste from the clipboard, if one was in * progress when the user pressed a key. This is easier than * buffering the current piece of data and saving it until the * terminal has finished pasting, and has the potential side * benefit of permitting a user to cancel an accidental huge * paste. */ term_nopaste(ldisc->term); } /* * Less than zero means null terminated special string. */ if (len < 0) { len = strlen(buf); keyflag = KCTRL('@'); |
︙ | ︙ | |||
199 200 201 202 203 204 205 | } ldisc->back->special(ldisc->backhandle, TS_EL); /* * We don't send IP, SUSP or ABORT if the user has * configured telnet specials off! This breaks * talkers otherwise. */ | | | 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 | } ldisc->back->special(ldisc->backhandle, TS_EL); /* * We don't send IP, SUSP or ABORT if the user has * configured telnet specials off! This breaks * talkers otherwise. */ if (!ldisc->telnet_keyboard) goto default_case; if (c == CTRL('C')) ldisc->back->special(ldisc->backhandle, TS_IP); if (c == CTRL('Z')) ldisc->back->special(ldisc->backhandle, TS_SUSP); if (c == CTRL('\\')) ldisc->back->special(ldisc->backhandle, TS_ABORT); |
︙ | ︙ | |||
251 252 253 254 255 256 257 | * - receiving a magic-^M empties the line buffer, * signals end-of-line in one of the various * entertaining ways, and _doesn't_ fall out of * the bottom of the if and through to the * default clause because of the break. */ case CTRL('J'): | | | | | 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 | * - receiving a magic-^M empties the line buffer, * signals end-of-line in one of the various * entertaining ways, and _doesn't_ fall out of * the bottom of the if and through to the * default clause because of the break. */ case CTRL('J'): if (ldisc->protocol == PROT_RAW && ldisc->buflen > 0 && ldisc->buf[ldisc->buflen - 1] == '\r') { if (ECHOING) bsb(ldisc, plen(ldisc, ldisc->buf[ldisc->buflen - 1])); ldisc->buflen--; /* FALLTHROUGH */ case KCTRL('M'): /* send with newline */ if (ldisc->buflen > 0) ldisc->back->send(ldisc->backhandle, ldisc->buf, ldisc->buflen); if (ldisc->protocol == PROT_RAW) ldisc->back->send(ldisc->backhandle, "\r\n", 2); else if (ldisc->protocol == PROT_TELNET && ldisc->telnet_newline) ldisc->back->special(ldisc->backhandle, TS_EOL); else ldisc->back->send(ldisc->backhandle, "\r", 1); if (ECHOING) c_write(ldisc, "\r\n", 2); ldisc->buflen = 0; break; |
︙ | ︙ | |||
296 297 298 299 300 301 302 | bsb(ldisc, plen(ldisc, ldisc->buf[ldisc->buflen - 1])); ldisc->buflen--; } } if (len > 0) { if (ECHOING) c_write(ldisc, buf, len); | | | | | | | 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 | bsb(ldisc, plen(ldisc, ldisc->buf[ldisc->buflen - 1])); ldisc->buflen--; } } if (len > 0) { if (ECHOING) c_write(ldisc, buf, len); if (keyflag && ldisc->protocol == PROT_TELNET && len == 1) { switch (buf[0]) { case CTRL('M'): if (ldisc->protocol == PROT_TELNET && ldisc->telnet_newline) ldisc->back->special(ldisc->backhandle, TS_EOL); else ldisc->back->send(ldisc->backhandle, "\r", 1); break; case CTRL('?'): case CTRL('H'): if (ldisc->telnet_keyboard) { ldisc->back->special(ldisc->backhandle, TS_EC); break; } case CTRL('C'): if (ldisc->telnet_keyboard) { ldisc->back->special(ldisc->backhandle, TS_IP); break; } case CTRL('Z'): if (ldisc->telnet_keyboard) { ldisc->back->special(ldisc->backhandle, TS_SUSP); break; } default: ldisc->back->send(ldisc->backhandle, buf, len); break; } } else ldisc->back->send(ldisc->backhandle, buf, len); } } } |
Changes to ldisc.h.
1 2 3 4 5 6 7 8 9 10 11 12 13 | /* * ldisc.h: defines the Ldisc data structure used by ldisc.c and * ldiscucs.c. (Unfortunately it was necessary to split the ldisc * module in two, to avoid unnecessarily linking in the Unicode * stuff in tools that don't require it.) */ #ifndef PUTTY_LDISC_H #define PUTTY_LDISC_H typedef struct ldisc_tag { Terminal *term; Backend *back; | < > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | /* * ldisc.h: defines the Ldisc data structure used by ldisc.c and * ldiscucs.c. (Unfortunately it was necessary to split the ldisc * module in two, to avoid unnecessarily linking in the Unicode * stuff in tools that don't require it.) */ #ifndef PUTTY_LDISC_H #define PUTTY_LDISC_H typedef struct ldisc_tag { Terminal *term; Backend *back; void *backhandle; void *frontend; /* * Values cached out of conf. */ int telnet_keyboard, telnet_newline, protocol, localecho, localedit; char *buf; int buflen, bufsiz, quotenext; } *Ldisc; #endif /* PUTTY_LDISC_H */ |
Changes to ldiscucs.c.
︙ | ︙ | |||
47 48 49 50 51 52 53 | linebuffer = snewn(linesize, char); if (in_utf(ldisc->term)) { /* UTF is a simple algorithm */ for (p = linebuffer, i = 0; i < len; i++) { unsigned long ch = widebuf[i]; | | | < | | 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 | linebuffer = snewn(linesize, char); if (in_utf(ldisc->term)) { /* UTF is a simple algorithm */ for (p = linebuffer, i = 0; i < len; i++) { unsigned long ch = widebuf[i]; if (IS_SURROGATE(ch)) { #ifdef PLATFORM_IS_UTF16 if (i+1 < len) { unsigned long ch2 = widebuf[i+1]; if (IS_SURROGATE_PAIR(ch, ch2)) { ch = FROM_SURROGATES(ch, ch2); i++; } } else #endif { /* Unrecognised UTF-16 sequence */ ch = '.'; |
︙ | ︙ |
Changes to logging.c.
︙ | ︙ | |||
12 13 14 15 16 17 18 | #include "putty.h" /* log session to file stuff ... */ struct LogContext { FILE *lgfp; enum { L_CLOSED, L_OPENING, L_OPEN, L_ERROR } state; bufchain queue; | | | > | | 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | #include "putty.h" /* log session to file stuff ... */ struct LogContext { FILE *lgfp; enum { L_CLOSED, L_OPENING, L_OPEN, L_ERROR } state; bufchain queue; Filename *currlogfilename; void *frontend; Conf *conf; int logtype; /* cached out of conf */ }; static Filename *xlatlognam(Filename *s, char *hostname, struct tm *tm); /* * Internal wrapper function which must be called for _all_ output * to the log file. It takes care of opening the log file if it * isn't open, buffering data if it's in the process of being * opened asynchronously, etc. */ |
︙ | ︙ | |||
71 72 73 74 75 76 77 | } /* * Flush any open log file. */ void logflush(void *handle) { struct LogContext *ctx = (struct LogContext *)handle; | | | 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 | } /* * Flush any open log file. */ void logflush(void *handle) { struct LogContext *ctx = (struct LogContext *)handle; if (ctx->logtype > 0) if (ctx->state == L_OPEN) fflush(ctx->lgfp); } static void logfopen_callback(void *handle, int mode) { struct LogContext *ctx = (struct LogContext *)handle; |
︙ | ︙ | |||
106 107 108 109 110 111 112 | " =~=~=~=~=~=~=~=~=~=~=~=\r\n", buf); } event = dupprintf("%s session log (%s mode) to file: %s", ctx->state == L_ERROR ? (mode == 0 ? "Disabled writing" : "Error writing") : (mode == 1 ? "Appending" : "Writing new"), | | | | | | | 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 | " =~=~=~=~=~=~=~=~=~=~=~=\r\n", buf); } event = dupprintf("%s session log (%s mode) to file: %s", ctx->state == L_ERROR ? (mode == 0 ? "Disabled writing" : "Error writing") : (mode == 1 ? "Appending" : "Writing new"), (ctx->logtype == LGTYP_ASCII ? "ASCII" : ctx->logtype == LGTYP_DEBUG ? "raw" : ctx->logtype == LGTYP_PACKETS ? "SSH packets" : ctx->logtype == LGTYP_SSHRAW ? "SSH raw data" : "unknown"), filename_to_str(ctx->currlogfilename)); logevent(ctx->frontend, event); sfree(event); /* * Having either succeeded or failed in opening the log file, * we should write any queued data out. */ |
︙ | ︙ | |||
144 145 146 147 148 149 150 | struct tm tm; int mode; /* Prevent repeat calls */ if (ctx->state != L_CLOSED) return; | | > > | > > > | | | 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 | struct tm tm; int mode; /* Prevent repeat calls */ if (ctx->state != L_CLOSED) return; if (!ctx->logtype) return; tm = ltime(); /* substitute special codes in file name */ if (ctx->currlogfilename) filename_free(ctx->currlogfilename); ctx->currlogfilename = xlatlognam(conf_get_filename(ctx->conf, CONF_logfilename), conf_get_str(ctx->conf, CONF_host), &tm); ctx->lgfp = f_open(ctx->currlogfilename, "r", FALSE); /* file already present? */ if (ctx->lgfp) { int logxfovr = conf_get_int(ctx->conf, CONF_logxfovr); fclose(ctx->lgfp); if (logxfovr != LGXF_ASK) { mode = ((logxfovr == LGXF_OVR) ? 2 : 1); } else mode = askappend(ctx->frontend, ctx->currlogfilename, logfopen_callback, ctx); } else mode = 2; /* create == overwrite */ if (mode < 0) |
︙ | ︙ | |||
185 186 187 188 189 190 191 | /* * Log session traffic. */ void logtraffic(void *handle, unsigned char c, int logmode) { struct LogContext *ctx = (struct LogContext *)handle; | | | | 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 | /* * Log session traffic. */ void logtraffic(void *handle, unsigned char c, int logmode) { struct LogContext *ctx = (struct LogContext *)handle; if (ctx->logtype > 0) { if (ctx->logtype == logmode) logwrite(ctx, &c, 1); } } /* * Log an Event Log entry. Used in SSH packet logging mode; this is * also as convenient a place as any to put the output of Event Log |
︙ | ︙ | |||
210 211 212 213 214 215 216 | if ((flags & FLAG_STDERR) && (flags & FLAG_VERBOSE)) { fprintf(stderr, "%s\n", event); fflush(stderr); } /* If we don't have a context yet (eg winnet.c init) then skip entirely */ if (!ctx) return; | | | | > | | < | | | | | | | | > > > > > > > > > > > > > > > > > > > | | > | 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 | if ((flags & FLAG_STDERR) && (flags & FLAG_VERBOSE)) { fprintf(stderr, "%s\n", event); fflush(stderr); } /* If we don't have a context yet (eg winnet.c init) then skip entirely */ if (!ctx) return; if (ctx->logtype != LGTYP_PACKETS && ctx->logtype != LGTYP_SSHRAW) return; logprintf(ctx, "Event Log: %s\r\n", event); logflush(ctx); } /* * Log an SSH packet. * If n_blanks != 0, blank or omit some parts. * Set of blanking areas must be in increasing order. */ void log_packet(void *handle, int direction, int type, char *texttype, const void *data, int len, int n_blanks, const struct logblank_t *blanks, const unsigned long *seq, unsigned downstream_id, const char *additional_log_text) { struct LogContext *ctx = (struct LogContext *)handle; char dumpdata[80], smalldata[5]; int p = 0, b = 0, omitted = 0; int output_pos = 0; /* NZ if pending output in dumpdata */ if (!(ctx->logtype == LGTYP_SSHRAW || (ctx->logtype == LGTYP_PACKETS && texttype))) return; /* Packet header. */ if (texttype) { logprintf(ctx, "%s packet ", direction == PKT_INCOMING ? "Incoming" : "Outgoing"); if (seq) logprintf(ctx, "#0x%lx, ", *seq); logprintf(ctx, "type %d / 0x%02x (%s)", type, type, texttype); if (downstream_id) { logprintf(ctx, " on behalf of downstream #%u", downstream_id); if (additional_log_text) logprintf(ctx, " (%s)", additional_log_text); } logprintf(ctx, "\r\n"); } else { /* * Raw data is logged with a timestamp, so that it's possible * to determine whether a mysterious delay occurred at the * client or server end. (Timestamping the raw data avoids * cluttering the normal case of only logging decrypted SSH * messages, and also adds conceptual rigour in the case where * an SSH message arrives in several pieces.) */ char buf[256]; struct tm tm; tm = ltime(); strftime(buf, 24, "%Y-%m-%d %H:%M:%S", &tm); logprintf(ctx, "%s raw data at %s\r\n", direction == PKT_INCOMING ? "Incoming" : "Outgoing", buf); } /* * Output a hex/ASCII dump of the packet body, blanking/omitting * parts as specified. */ while (p < len) { |
︙ | ︙ | |||
322 323 324 325 326 327 328 | /* Tidy up */ if (omitted) logprintf(ctx, " (%d byte%s omitted)\r\n", omitted, (omitted==1?"":"s")); logflush(ctx); } | | | > > > > | | > > | > > | > < | > < < | > > > | > | | 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 | /* Tidy up */ if (omitted) logprintf(ctx, " (%d byte%s omitted)\r\n", omitted, (omitted==1?"":"s")); logflush(ctx); } void *log_init(void *frontend, Conf *conf) { struct LogContext *ctx = snew(struct LogContext); ctx->lgfp = NULL; ctx->state = L_CLOSED; ctx->frontend = frontend; ctx->conf = conf_copy(conf); ctx->logtype = conf_get_int(ctx->conf, CONF_logtype); ctx->currlogfilename = NULL; bufchain_init(&ctx->queue); return ctx; } void log_free(void *handle) { struct LogContext *ctx = (struct LogContext *)handle; logfclose(ctx); bufchain_clear(&ctx->queue); if (ctx->currlogfilename) filename_free(ctx->currlogfilename); sfree(ctx); } void log_reconfig(void *handle, Conf *conf) { struct LogContext *ctx = (struct LogContext *)handle; int reset_logging; if (!filename_equal(conf_get_filename(ctx->conf, CONF_logfilename), conf_get_filename(conf, CONF_logfilename)) || conf_get_int(ctx->conf, CONF_logtype) != conf_get_int(conf, CONF_logtype)) reset_logging = TRUE; else reset_logging = FALSE; if (reset_logging) logfclose(ctx); conf_free(ctx->conf); ctx->conf = conf_copy(conf); ctx->logtype = conf_get_int(ctx->conf, CONF_logtype); if (reset_logging) logfopen(ctx); } /* * translate format codes into time/date strings * and insert them into log file name * * "&Y":YYYY "&m":MM "&d":DD "&T":hhmmss "&h":<hostname> "&&":& */ static Filename *xlatlognam(Filename *src, char *hostname, struct tm *tm) { char buf[10], *bufp; int size; char *buffer; int buflen, bufsize; const char *s; Filename *ret; bufsize = FILENAME_MAX; buffer = snewn(bufsize, char); buflen = 0; s = filename_to_str(src); while (*s) { /* Let (bufp, len) be the string to append. */ bufp = buf; /* don't usually override this */ if (*s == '&') { char c; s++; |
︙ | ︙ | |||
414 415 416 417 418 419 420 | if (c != '&') buf[size++] = c; } } else { buf[0] = *s++; size = 1; } | | | > > | | < | | > > | 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 | if (c != '&') buf[size++] = c; } } else { buf[0] = *s++; size = 1; } if (bufsize <= buflen + size) { bufsize = (buflen + size) * 5 / 4 + 512; buffer = sresize(buffer, bufsize, char); } memcpy(buffer + buflen, bufp, size); buflen += size; } buffer[buflen] = '\0'; ret = filename_from_str(buffer); sfree(buffer); return ret; } |
Changes to macosx/README.OSX.
︙ | ︙ | |||
16 17 18 19 20 21 22 23 24 25 26 27 28 29 | load of saved sessions, you should not be surprised if a future version of the port decides to look somewhere completely different for the data and therefore loses them all. If that happens, don't say you weren't warned! Other ways in which the port is currently unfinished include: Missing terminal window features -------------------------------- - terminal display is horribly slow - fonts aren't configurable | > > > > > > > > > | 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | load of saved sessions, you should not be surprised if a future version of the port decides to look somewhere completely different for the data and therefore loses them all. If that happens, don't say you weren't warned! Other ways in which the port is currently unfinished include: Bit rot ------- - the conversion of the old fixed-size 'Config' structure to the new dynamic 'Conf' was never applied to this directory - probably other things are out of date too; it would need some work to make it compile again Missing terminal window features -------------------------------- - terminal display is horribly slow - fonts aren't configurable |
︙ | ︙ |
Changes to macosx/osxmain.m.
︙ | ︙ | |||
80 81 82 83 84 85 86 87 88 89 90 91 92 93 | alert = [[alert init] autorelease]; [alert addButtonWithTitle:@"Terminate"]; [alert setInformativeText:[NSString stringWithCString:errorbuf]]; [alert runModal]; } exit(1); } void fatalbox(char *p, ...) { va_list ap; va_start(ap, p); commonfatalbox(p, ap); va_end(ap); | > > > > > > > > > > > > > > > > > > | 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 | alert = [[alert init] autorelease]; [alert addButtonWithTitle:@"Terminate"]; [alert setInformativeText:[NSString stringWithCString:errorbuf]]; [alert runModal]; } exit(1); } void nonfatal(void *frontend, char *p, ...) { char *errorbuf; NSAlert *alert; va_list ap; va_start(ap, p); errorbuf = dupvprintf(p, ap); va_end(ap); alert = [[[NSAlert alloc] init] autorelease]; [alert addButtonWithTitle:@"Error"]; [alert setInformativeText:[NSString stringWithCString:errorbuf]]; [alert runModal]; sfree(errorbuf); } void fatalbox(char *p, ...) { va_list ap; va_start(ap, p); commonfatalbox(p, ap); va_end(ap); |
︙ | ︙ |
Changes to macosx/osxwin.m.
︙ | ︙ | |||
105 106 107 108 109 110 111 | nfg = ((attr & ATTR_FGMASK) >> ATTR_FGSHIFT); nbg = ((attr & ATTR_BGMASK) >> ATTR_BGSHIFT); if (attr & ATTR_REVERSE) { int t = nfg; nfg = nbg; nbg = t; } | | | | | 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 | nfg = ((attr & ATTR_FGMASK) >> ATTR_FGSHIFT); nbg = ((attr & ATTR_BGMASK) >> ATTR_BGSHIFT); if (attr & ATTR_REVERSE) { int t = nfg; nfg = nbg; nbg = t; } if ((cfg.bold_style & 2) && (attr & ATTR_BOLD)) { if (nfg < 16) nfg |= 8; else if (nfg >= 256) nfg |= 1; } if ((cfg.bold_style & 2) && (attr & ATTR_BLINK)) { if (nbg < 16) nbg |= 8; else if (nbg >= 256) nbg |= 1; } if (attr & TATTR_ACTCURS) { nfg = 260; nbg = 261; } if (attr & ATTR_WIDE) { widefactor = 2; /* FIXME: what do we actually have to do about wide characters? */ } else { widefactor = 1; } /* FIXME: ATTR_BOLD if cfg.bold_style & 1 */ if ((lattr & LATTR_MODE) != LATTR_NORM) { x *= 2; if (x >= term->cols) return; if (x + len*2*widefactor > term->cols) len = (term->cols-x)/2/widefactor;/* trim to LH half */ |
︙ | ︙ | |||
953 954 955 956 957 958 959 | void palette_set(void *frontend, int n, int r, int g, int b) { SessionWindow *win = (SessionWindow *)frontend; if (n >= 16) n += 256 - 16; | | | 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 | void palette_set(void *frontend, int n, int r, int g, int b) { SessionWindow *win = (SessionWindow *)frontend; if (n >= 16) n += 256 - 16; if (n >= NALLCOLOURS) return; [win setColour:n r:r/255.0 g:g/255.0 b:b/255.0]; /* * FIXME: do we need an OS X equivalent of set_window_background? */ } |
︙ | ︙ |
Changes to minibidi.c.
1 | /************************************************************************ | | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | /************************************************************************ * $Id: minibidi.c 9426 2012-03-05 18:34:40Z simon $ * * ------------ * Description: * ------------ * This is an implemention of Unicode's Bidirectional Algorithm * (known as UAX #9). * * http://www.unicode.org/reports/tr9/ * * Author: Ahmad Khalifa * * ----------------- * Revision Details: (Updated by Revision Control System) * ----------------- * $Date: 2012-03-05 12:34:40 -0600 (Mon, 05 Mar 2012) $ * $Author: simon $ * $Revision: 9426 $ * * (www.arabeyes.org - under MIT license) * ************************************************************************/ /* * TODO: |
︙ | ︙ | |||
54 55 56 57 58 59 60 | #define SINITIAL(xh) ((xh)+2) #define SMEDIAL(ch) ((ch)+3) #define leastGreaterOdd(x) ( ((x)+1) | 1 ) #define leastGreaterEven(x) ( ((x)+2) &~ 1 ) typedef struct bidi_char { | | | | 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 | #define SINITIAL(xh) ((xh)+2) #define SMEDIAL(ch) ((ch)+3) #define leastGreaterOdd(x) ( ((x)+1) | 1 ) #define leastGreaterEven(x) ( ((x)+2) &~ 1 ) typedef struct bidi_char { unsigned int origwc, wc; unsigned short index; } bidi_char; /* function declarations */ void flipThisRun(bidi_char *from, unsigned char* level, int max, int count); int findIndexOfRun(unsigned char* level , int start, int count, int tlevel); unsigned char getType(int ch); unsigned char setOverrideBits(unsigned char level, unsigned char override); int getPreviousLevel(unsigned char* level, int from); int do_shape(bidi_char *line, bidi_char *to, int count); int do_bidi(bidi_char *line, int count); void doMirror(unsigned int *ch); /* character types */ enum { L, LRE, LRO, R, |
︙ | ︙ | |||
1632 1633 1634 1635 1636 1637 1638 | /* * Bad, Horrible function * takes a pointer to a character that is checked for * having a mirror glyph. */ | | | 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 | /* * Bad, Horrible function * takes a pointer to a character that is checked for * having a mirror glyph. */ void doMirror(unsigned int *ch) { if ((*ch & 0xFF00) == 0) { switch (*ch) { case 0x0028: *ch = 0x0029; break; case 0x0029: *ch = 0x0028; break; case 0x003C: *ch = 0x003E; break; case 0x003E: *ch = 0x003C; break; |
︙ | ︙ |
Changes to misc.c.
︙ | ︙ | |||
95 96 97 98 99 100 101 | p->frontend = frontend; p->data = NULL; p->to_server = TRUE; /* to be on the safe side */ p->name = p->instruction = NULL; p->name_reqd = p->instr_reqd = FALSE; return p; } | | < | | > > > > > > > > > > > > > > > > > > > > > > > > > | | 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 | p->frontend = frontend; p->data = NULL; p->to_server = TRUE; /* to be on the safe side */ p->name = p->instruction = NULL; p->name_reqd = p->instr_reqd = FALSE; return p; } void add_prompt(prompts_t *p, char *promptstr, int echo) { prompt_t *pr = snew(prompt_t); pr->prompt = promptstr; pr->echo = echo; pr->result = NULL; pr->resultsize = 0; p->n_prompts++; p->prompts = sresize(p->prompts, p->n_prompts, prompt_t *); p->prompts[p->n_prompts-1] = pr; } void prompt_ensure_result_size(prompt_t *pr, int newlen) { if ((int)pr->resultsize < newlen) { char *newbuf; newlen = newlen * 5 / 4 + 512; /* avoid too many small allocs */ /* * We don't use sresize / realloc here, because we will be * storing sensitive stuff like passwords in here, and we want * to make sure that the data doesn't get copied around in * memory without the old copy being destroyed. */ newbuf = snewn(newlen, char); memcpy(newbuf, pr->result, pr->resultsize); smemclr(pr->result, pr->resultsize); sfree(pr->result); pr->result = newbuf; pr->resultsize = newlen; } } void prompt_set_result(prompt_t *pr, const char *newstr) { prompt_ensure_result_size(pr, strlen(newstr) + 1); strcpy(pr->result, newstr); } void free_prompts(prompts_t *p) { size_t i; for (i=0; i < p->n_prompts; i++) { prompt_t *pr = p->prompts[i]; smemclr(pr->result, pr->resultsize); /* burn the evidence */ sfree(pr->result); sfree(pr->prompt); sfree(pr); } sfree(p->prompts); sfree(p->name); sfree(p->instruction); |
︙ | ︙ | |||
171 172 173 174 175 176 177 178 179 180 181 182 183 184 | strcpy(q, sn); q += strlen(q); } va_end(ap); return p; } /* * Do an sprintf(), but into a custom-allocated buffer. * * Currently I'm doing this via vsnprintf. This has worked so far, * but it's not good, because vsnprintf is not available on all * platforms. There's an ifdef to use `_vsnprintf', which seems | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 | strcpy(q, sn); q += strlen(q); } va_end(ap); return p; } void burnstr(char *string) /* sfree(str), only clear it first */ { if (string) { smemclr(string, strlen(string)); sfree(string); } } int toint(unsigned u) { /* * Convert an unsigned to an int, without running into the * undefined behaviour which happens by the strict C standard if * the value overflows. You'd hope that sensible compilers would * do the sensible thing in response to a cast, but actually I * don't trust modern compilers not to do silly things like * assuming that _obviously_ you wouldn't have caused an overflow * and so they can elide an 'if (i < 0)' test immediately after * the cast. * * Sensible compilers ought of course to optimise this entire * function into 'just return the input value'! */ if (u <= (unsigned)INT_MAX) return (int)u; else if (u >= (unsigned)INT_MIN) /* wrap in cast _to_ unsigned is OK */ return INT_MIN + (int)(u - (unsigned)INT_MIN); else return INT_MIN; /* fallback; should never occur on binary machines */ } /* * Do an sprintf(), but into a custom-allocated buffer. * * Currently I'm doing this via vsnprintf. This has worked so far, * but it's not good, because vsnprintf is not available on all * platforms. There's an ifdef to use `_vsnprintf', which seems |
︙ | ︙ | |||
328 329 330 331 332 333 334 | * - return a (pointer,length) pair giving some initial data in * the list, suitable for passing to a send or write system * call * - retrieve a larger amount of initial data from the list * - return the current size of the buffer chain in bytes */ | | | < | 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 | * - return a (pointer,length) pair giving some initial data in * the list, suitable for passing to a send or write system * call * - retrieve a larger amount of initial data from the list * - return the current size of the buffer chain in bytes */ #define BUFFER_MIN_GRANULE 512 struct bufchain_granule { struct bufchain_granule *next; char *bufpos, *bufend, *bufmax; }; void bufchain_init(bufchain *ch) { ch->head = ch->tail = NULL; ch->buffersize = 0; } |
︙ | ︙ | |||
367 368 369 370 371 372 373 | { const char *buf = (const char *)data; if (len == 0) return; ch->buffersize += len; | > | | | | | | | | | > | | | > | | < < | | | | < | > | | < > | | | | | | 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 | { const char *buf = (const char *)data; if (len == 0) return; ch->buffersize += len; while (len > 0) { if (ch->tail && ch->tail->bufend < ch->tail->bufmax) { int copylen = min(len, ch->tail->bufmax - ch->tail->bufend); memcpy(ch->tail->bufend, buf, copylen); buf += copylen; len -= copylen; ch->tail->bufend += copylen; } if (len > 0) { int grainlen = max(sizeof(struct bufchain_granule) + len, BUFFER_MIN_GRANULE); struct bufchain_granule *newbuf; newbuf = smalloc(grainlen); newbuf->bufpos = newbuf->bufend = (char *)newbuf + sizeof(struct bufchain_granule); newbuf->bufmax = (char *)newbuf + grainlen; newbuf->next = NULL; if (ch->tail) ch->tail->next = newbuf; else ch->head = newbuf; ch->tail = newbuf; } } } void bufchain_consume(bufchain *ch, int len) { struct bufchain_granule *tmp; assert(ch->buffersize >= len); while (len > 0) { int remlen = len; assert(ch->head != NULL); if (remlen >= ch->head->bufend - ch->head->bufpos) { remlen = ch->head->bufend - ch->head->bufpos; tmp = ch->head; ch->head = tmp->next; if (!ch->head) ch->tail = NULL; sfree(tmp); } else ch->head->bufpos += remlen; ch->buffersize -= remlen; len -= remlen; } } void bufchain_prefix(bufchain *ch, void **data, int *len) { *len = ch->head->bufend - ch->head->bufpos; *data = ch->head->bufpos; } void bufchain_fetch(bufchain *ch, void *data, int len) { struct bufchain_granule *tmp; char *data_c = (char *)data; tmp = ch->head; assert(ch->buffersize >= len); while (len > 0) { int remlen = len; assert(tmp != NULL); if (remlen >= tmp->bufend - tmp->bufpos) remlen = tmp->bufend - tmp->bufpos; memcpy(data_c, tmp->bufpos, remlen); tmp = tmp->next; len -= remlen; data_c += remlen; } } |
︙ | ︙ | |||
631 632 633 634 635 636 637 | debug_printf("%*s%s\n", (16 - i) * 3 + 2, "", foo); } } #endif /* def DEBUG */ /* | | | | | | | | | | > > | > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > | 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 | debug_printf("%*s%s\n", (16 - i) * 3 + 2, "", foo); } } #endif /* def DEBUG */ /* * Determine whether or not a Conf represents a session which can * sensibly be launched right now. */ int conf_launchable(Conf *conf) { if (conf_get_int(conf, CONF_protocol) == PROT_SERIAL) return conf_get_str(conf, CONF_serline)[0] != 0; else return conf_get_str(conf, CONF_host)[0] != 0; } char const *conf_dest(Conf *conf) { if (conf_get_int(conf, CONF_protocol) == PROT_SERIAL) return conf_get_str(conf, CONF_serline); else return conf_get_str(conf, CONF_host); } #ifndef PLATFORM_HAS_SMEMCLR /* * Securely wipe memory. * * The actual wiping is no different from what memset would do: the * point of 'securely' is to try to be sure over-clever compilers * won't optimise away memsets on variables that are about to be freed * or go out of scope. See * https://buildsecurityin.us-cert.gov/bsi-rules/home/g1/771-BSI.html * * Some platforms (e.g. Windows) may provide their own version of this * function. */ void smemclr(void *b, size_t n) { volatile char *vp; if (b && n > 0) { /* * Zero out the memory. */ memset(b, 0, n); /* * Perform a volatile access to the object, forcing the * compiler to admit that the previous memset was important. * * This while loop should in practice run for zero iterations * (since we know we just zeroed the object out), but in * theory (as far as the compiler knows) it might range over * the whole object. (If we had just written, say, '*vp = * *vp;', a compiler could in principle have 'helpfully' * optimised the memset into only zeroing out the first byte. * This should be robust.) */ vp = b; while (*vp) vp++; } } #endif |
Changes to misc.h.
︙ | ︙ | |||
22 23 24 25 26 27 28 | typedef struct FontSpec FontSpec; unsigned long parse_blocksize(const char *bs); char ctrlparse(char *s, char **next); char *dupstr(const char *s); char *dupcat(const char *s1, ...); | | > > > > > > > > > | 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 | typedef struct FontSpec FontSpec; unsigned long parse_blocksize(const char *bs); char ctrlparse(char *s, char **next); char *dupstr(const char *s); char *dupcat(const char *s1, ...); char *dupprintf(const char *fmt, ...) #ifdef __GNUC__ __attribute__ ((format (printf, 1, 2))) #endif ; char *dupvprintf(const char *fmt, va_list ap); void burnstr(char *string); int toint(unsigned); char *fgetline(FILE *fp); void base64_encode_atom(unsigned char *data, int n, char *out); struct bufchain_granule; typedef struct bufchain_tag { struct bufchain_granule *head, *tail; int buffersize; /* current amount of buffered data */ } bufchain; void bufchain_init(bufchain *ch); void bufchain_clear(bufchain *ch); int bufchain_size(bufchain *ch); void bufchain_add(bufchain *ch, const void *data, int len); void bufchain_prefix(bufchain *ch, void **data, int *len); void bufchain_consume(bufchain *ch, int len); void bufchain_fetch(bufchain *ch, void *data, int len); struct tm ltime(void); void smemclr(void *b, size_t len); /* * Debugging functions. * * Output goes to debug.log * * debug(()) (note the double brackets) is like printf(). |
︙ | ︙ |
Changes to mkauto.sh.
1 2 3 4 5 6 7 8 9 | #! /bin/sh # This script makes the autoconf mechanism for the Unix port work. # It's separate from mkfiles.pl because it won't work (and isn't needed) # on a non-Unix system. # It's nice to be able to run this from inside the unix subdir as # well as from outside. test -f unix.h && cd .. | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | | | 1 2 3 4 5 6 7 8 9 10 11 | #! /bin/sh # This script makes the autoconf mechanism for the Unix port work. # It's separate from mkfiles.pl because it won't work (and isn't needed) # on a non-Unix system. # It's nice to be able to run this from inside the unix subdir as # well as from outside. test -f unix.h && cd .. # Run autoconf on our real configure.in. (cd unix && autoreconf -i && rm -rf autom4te.cache) |
Changes to mkfiles.pl.
︙ | ︙ | |||
13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | # mainly because I was too scared to go anywhere near it. # - sbcsgen.pl is still run at startup. # # FIXME: no attempt made to handle !forceobj in the project files. use warnings; use FileHandle; use Cwd; open IN, "Recipe" or do { # We want to deal correctly with being run from one of the # subdirs in the source tree. So if we can't find Recipe here, # try one level up. chdir ".."; open IN, "Recipe" or die "unable to open Recipe file\n"; }; # HACK: One of the source files in `charset' is auto-generated by # sbcsgen.pl. We need to generate that _now_, before attempting # dependency analysis. | > > > > > > > > > > > > > > > > | | 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 | # mainly because I was too scared to go anywhere near it. # - sbcsgen.pl is still run at startup. # # FIXME: no attempt made to handle !forceobj in the project files. use warnings; use FileHandle; use File::Basename; use Cwd; if ($#ARGV >= 0 and ($ARGV[0] eq "-u" or $ARGV[0] eq "-U")) { # Convenience for Unix users: -u means that after we finish what # we're doing here, we also run mkauto.sh and then 'configure' in # the Unix subdirectory. So it's a one-stop shop for regenerating # the actual end-product Unix makefile. # # Arguments supplied after -u go to configure. # # -U is identical, but runs 'configure' at the _top_ level, for # people who habitually do that. $do_unix = ($ARGV[0] eq "-U" ? 2 : 1); shift @ARGV; @confargs = @ARGV; } open IN, "Recipe" or do { # We want to deal correctly with being run from one of the # subdirs in the source tree. So if we can't find Recipe here, # try one level up. chdir ".."; open IN, "Recipe" or die "unable to open Recipe file\n"; }; # HACK: One of the source files in `charset' is auto-generated by # sbcsgen.pl. We need to generate that _now_, before attempting # dependency analysis. eval 'chdir "charset"; require "sbcsgen.pl"; chdir ".."; select STDOUT;'; @srcdirs = ("./"); $divert = undef; # ref to scalar in which text is currently being put $help = ""; # list of newline-free lines of help text $project_name = "project"; # this is a good enough default %makefiles = (); # maps makefile types to output makefile pathnames |
︙ | ︙ | |||
60 61 62 63 64 65 66 67 68 | if ($_[0] eq "!begin" and $_[1] eq "help") { $divert = \$help; next; } if ($_[0] eq "!end") { $divert = undef; next; } if ($_[0] eq "!name") { $project_name = $_[1]; next; } if ($_[0] eq "!srcdir") { push @srcdirs, $_[1]; next; } if ($_[0] eq "!makefile" and &mfval($_[1])) { $makefiles{$_[1]}=$_[2]; next;} if ($_[0] eq "!specialobj" and &mfval($_[1])) { $specialobj{$_[1]}->{$_[2]} = 1; next;} if ($_[0] eq "!forceobj") { $forceobj{$_[1]} = 1; next; } if ($_[0] eq "!begin") { | > > > > > > > > | | 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 | if ($_[0] eq "!begin" and $_[1] eq "help") { $divert = \$help; next; } if ($_[0] eq "!end") { $divert = undef; next; } if ($_[0] eq "!name") { $project_name = $_[1]; next; } if ($_[0] eq "!srcdir") { push @srcdirs, $_[1]; next; } if ($_[0] eq "!makefile" and &mfval($_[1])) { $makefiles{$_[1]}=$_[2]; next;} if ($_[0] eq "!specialobj" and &mfval($_[1])) { $specialobj{$_[1]}->{$_[2]} = 1; next;} if ($_[0] eq "!cflags" and &mfval($_[1])) { ($rest = $_) =~ s/^\s*\S+\s+\S+\s+\S+\s*//; # find rest of input line $rest = 1 if $rest eq ""; $cflags{$_[1]}->{$_[2]} = $rest; next; } if ($_[0] eq "!forceobj") { $forceobj{$_[1]} = 1; next; } if ($_[0] eq "!begin") { if ($_[1] =~ /^>(.*)/) { $divert = \$auxfiles{$1}; } elsif (&mfval($_[1])) { $sect = $_[2] ? $_[2] : "end"; $divert = \($makefile_extra{$_[1]}->{$sect}); } else { $dummy = ''; $divert = \$dummy; } next; |
︙ | ︙ | |||
118 119 120 121 122 123 124 125 126 127 128 129 130 131 | if defined $programs{$prog . "," . $type}; $programs{$prog . "," . $type} = $listref; } $lastlistref = $listref; } close IN; # Now retrieve the complete list of objects and resource files, and # construct dependency data for them. While we're here, expand the # object list for each program, and complain if its type isn't set. @prognames = sort keys %programs; %depends = (); @scanlist = (); | > > > > > > | 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 | if defined $programs{$prog . "," . $type}; $programs{$prog . "," . $type} = $listref; } $lastlistref = $listref; } close IN; foreach $aux (sort keys %auxfiles) { open AUX, ">$aux"; print AUX $auxfiles{$aux}; close AUX; } # Now retrieve the complete list of objects and resource files, and # construct dependency data for them. While we're here, expand the # object list for each program, and complain if its type isn't set. @prognames = sort keys %programs; %depends = (); @scanlist = (); |
︙ | ︙ | |||
173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 | # Files included by this method are not added to @scanlist because # they can never include further files. # # In this pass we write out a hash %further which maps a source # file name into a listref containing further source file names. %further = (); while (scalar @scanlist > 0) { $file = shift @scanlist; next if defined $further{$file}; # skip if we've already done it $further{$file} = []; $dirfile = &findfile($file); open IN, "$dirfile" or die "unable to open source file $file\n"; while (<IN>) { chomp; /^\s*#include\s+\"([^\"]+)\"/ and do { push @{$further{$file}}, $1; push @scanlist, $1; next; | > > | 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 | # Files included by this method are not added to @scanlist because # they can never include further files. # # In this pass we write out a hash %further which maps a source # file name into a listref containing further source file names. %further = (); %allsourcefiles = (); # this is wanted by some makefiles while (scalar @scanlist > 0) { $file = shift @scanlist; next if defined $further{$file}; # skip if we've already done it $further{$file} = []; $dirfile = &findfile($file); $allsourcefiles{$dirfile} = 1; open IN, "$dirfile" or die "unable to open source file $file\n"; while (<IN>) { chomp; /^\s*#include\s+\"([^\"]+)\"/ and do { push @{$further{$file}}, $1; push @scanlist, $1; next; |
︙ | ︙ | |||
222 223 224 225 226 227 228 | sub mfval($) { my ($type) = @_; # Returns true if the argument is a known makefile type. Otherwise, # prints a warning and returns false; if (grep { $type eq $_ } ("vc","vcproj","cygwin","borland","lcc","devcppproj","gtk","unix", | | | 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 | sub mfval($) { my ($type) = @_; # Returns true if the argument is a known makefile type. Otherwise, # prints a warning and returns false; if (grep { $type eq $_ } ("vc","vcproj","cygwin","borland","lcc","devcppproj","gtk","unix", "am","osx",)) { return 1; } warn "$.:unknown makefile type '$type'\n"; return 0; } # Utility routines while writing out the Makefiles. |
︙ | ︙ | |||
386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 | # assume that all UNIX programs have a man page if($suffix eq "1" && $types =~ /:X:/) { return map("$_.1", &progrealnames($types)); } return (); } # Now we're ready to output the actual Makefiles. if (defined $makefiles{'cygwin'}) { $dirpfx = &dirpfx($makefiles{'cygwin'}, "/"); ##-- CygWin makefile open OUT, ">$makefiles{'cygwin'}"; select OUT; print | > > | | | 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 | # assume that all UNIX programs have a man page if($suffix eq "1" && $types =~ /:X:/) { return map("$_.1", &progrealnames($types)); } return (); } $orig_dir = cwd; # Now we're ready to output the actual Makefiles. if (defined $makefiles{'cygwin'}) { $dirpfx = &dirpfx($makefiles{'cygwin'}, "/"); ##-- CygWin makefile open OUT, ">$makefiles{'cygwin'}"; select OUT; print "# Makefile for $project_name under Cygwin, MinGW, or Winelib.\n". "#\n# This file was created by `mkfiles.pl' from the `Recipe' file.\n". "# DO NOT EDIT THIS FILE DIRECTLY; edit Recipe or mkfiles.pl instead.\n"; # gcc command line option is -D not /D ($_ = $help) =~ s/([=" ])\/D/$1-D/gs; print $_; print "\n". "# You can define this path to point at your tools if you need to\n". "# TOOLPATH = c:\\cygwin\\bin\\ # or similar, if you're running Windows\n". "# TOOLPATH = /pkg/mingw32msvc/i386-mingw32msvc/bin/\n". "CC = \$(TOOLPATH)gcc\n". "RC = \$(TOOLPATH)windres\n". "# Uncomment the following two lines to compile under Winelib\n". "# CC = winegcc\n". "# RC = wrc\n". "# You may also need to tell windres where to find include files:\n". "# RCINC = --include-dir c:\\cygwin\\include\\\n". "\n". &splitline("CFLAGS = -mno-cygwin -Wall -O2 -D_WINDOWS -DDEBUG -DWIN32S_COMPAT". " -D_NO_OLDNAMES -DNO_MULTIMON -DNO_HTMLHELP -DNO_SECUREZEROMEMORY " . (join " ", map {"-I$dirpfx$_"} @srcdirs)) . "\n". "LDFLAGS = -mno-cygwin -s\n". &splitline("RCFLAGS = \$(RCINC) --define WIN32=1 --define _WIN32=1". " --define WINVER=0x0400")."\n". "\n". $makefile_extra{'cygwin'}->{'vars'} . |
︙ | ︙ | |||
446 447 448 449 450 451 452 | if ($forceobj{$d->{obj_orig}}) { printf ("%s: FORCE\n", $d->{obj}); } else { print &splitline(sprintf("%s: %s", $d->{obj}, join " ", @{$d->{deps}})), "\n"; } if ($d->{obj} =~ /\.res\.o$/) { | | | | 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 | if ($forceobj{$d->{obj_orig}}) { printf ("%s: FORCE\n", $d->{obj}); } else { print &splitline(sprintf("%s: %s", $d->{obj}, join " ", @{$d->{deps}})), "\n"; } if ($d->{obj} =~ /\.res\.o$/) { print "\t\$(RC) \$(RCFL) \$(RCFLAGS) ".$d->{deps}->[0]." -o ".$d->{obj}."\n\n"; } else { print "\t\$(CC) \$(COMPAT) \$(CFLAGS) \$(XFLAGS) -c ".$d->{deps}->[0]."\n\n"; } } print "\n"; print $makefile_extra{'cygwin'}->{'end'}; print "\nclean:\n". "\trm -f *.o *.exe *.res.o *.so *.map\n". "\n". "FORCE:\n"; select STDOUT; close OUT; } ##-- Borland makefile |
︙ | ︙ | |||
665 666 667 668 669 670 671 | "\t-del debug.log\n"; select STDOUT; close OUT; } if (defined $makefiles{'vcproj'}) { $dirpfx = &dirpfx($makefiles{'vcproj'}, "\\"); | < < | 699 700 701 702 703 704 705 706 707 708 709 710 711 712 | "\t-del debug.log\n"; select STDOUT; close OUT; } if (defined $makefiles{'vcproj'}) { $dirpfx = &dirpfx($makefiles{'vcproj'}, "\\"); ##-- MSVC 6 Workspace and projects # # Note: All files created in this section are written in binary # mode, because although MSVC's command-line make can deal with # LF-only line endings, MSVC project files really _need_ to be # CRLF. Hence, in order for mkfiles.pl to generate usable project # files even when run from Unix, I make sure all files are binary |
︙ | ︙ | |||
1080 1081 1082 1083 1084 1085 1086 | print &def($makefile_extra{'unix'}->{'end'}); print "\nclean:\n". "\trm -f *.o". (join "", map { " $_" } &progrealnames("U")) . "\n"; print "\nFORCE:\n"; select STDOUT; close OUT; } | | | | | | | | > > > > > > > | > > > > > > > > > > | > | | < | | < < < < < < < | | | > | < | | | > | > | | > | | > > > | > > > | > | > > > > | < < < < < | | | | > > > > | > | > | | > | > | < < < < < < | 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 | print &def($makefile_extra{'unix'}->{'end'}); print "\nclean:\n". "\trm -f *.o". (join "", map { " $_" } &progrealnames("U")) . "\n"; print "\nFORCE:\n"; select STDOUT; close OUT; } if (defined $makefiles{'am'}) { $dirpfx = "\$(srcdir)/" . &dirpfx($makefiles{'am'}, "/"); ##-- Unix/autoconf Makefile.am open OUT, ">$makefiles{'am'}"; select OUT; print "# Makefile.am for $project_name under Unix with Autoconf/Automake.\n". "#\n# This file was created by `mkfiles.pl' from the `Recipe' file.\n". "# DO NOT EDIT THIS FILE DIRECTLY; edit Recipe or mkfiles.pl instead.\n\n"; # Complete list of source and header files. Not used by the # auto-generated parts of this makefile, but Recipe might like to # have it available as a variable so that mandatory-rebuild things # (version.o) can conveniently be made to depend on it. @sources = ("allsources", "=", map {"${dirpfx}$_"} sort keys %allsourcefiles); print &splitline(join " ", @sources), "\n\n"; @cliprogs = ("bin_PROGRAMS", "="); foreach $p (&prognames("U")) { ($prog, $type) = split ",", $p; push @cliprogs, $prog; } @allprogs = @cliprogs; foreach $p (&prognames("X")) { ($prog, $type) = split ",", $p; push @allprogs, $prog; } print "if HAVE_GTK\n"; print &splitline(join " ", @allprogs), "\n"; print "else\n"; print &splitline(join " ", @cliprogs), "\n"; print "endif\n\n"; %objtosrc = (); foreach $d (&deps("X", undef, $dirpfx, "/", "am")) { $objtosrc{$d->{obj}} = $d->{deps}->[0]; } print &splitline(join " ", "AM_CPPFLAGS", "=", map {"-I$dirpfx$_"} @srcdirs), "\n"; @amcflags = ("\$(COMPAT)", "\$(XFLAGS)", "\$(WARNINGOPTS)"); print "if HAVE_GTK\n"; print &splitline(join " ", "AM_CFLAGS", "=", "\$(GTK_CFLAGS)", @amcflags), "\n"; print "else\n"; print &splitline(join " ", "AM_CFLAGS", "=", @amcflags), "\n"; print "endif\n\n"; %amspeciallibs = (); foreach $obj (sort { $a cmp $b } keys %{$cflags{'am'}}) { print "lib${obj}_a_SOURCES = ", $objtosrc{$obj}, "\n"; print &splitline(join " ", "lib${obj}_a_CFLAGS", "=", @amcflags, $cflags{'am'}->{$obj}), "\n"; $amspeciallibs{$obj} = "lib${obj}.a"; } print &splitline(join " ", "noinst_LIBRARIES", "=", sort { $a cmp $b } values %amspeciallibs), "\n\n"; foreach $p (&prognames("X:U")) { ($prog, $type) = split ",", $p; print "if HAVE_GTK\n" if $type eq "X"; @progsources = ("${prog}_SOURCES", "="); %sourcefiles = (); @ldadd = (); $objstr = &objects($p, "X", undef, undef); foreach $obj (split / /,$objstr) { if ($amspeciallibs{$obj}) { push @ldadd, $amspeciallibs{$obj}; } else { $sourcefiles{$objtosrc{$obj}} = 1; } } push @progsources, sort { $a cmp $b } keys %sourcefiles; print &splitline(join " ", @progsources), "\n"; if ($type eq "X") { push @ldadd, "\$(GTK_LIBS)"; } if (@ldadd) { print &splitline(join " ", "${prog}_LDADD", "=", @ldadd), "\n"; } print "endif\n" if $type eq "X"; print "\n"; } print $makefile_extra{'am'}->{'end'}; select STDOUT; close OUT; } if (defined $makefiles{'lcc'}) { $dirpfx = &dirpfx($makefiles{'lcc'}, "\\"); ##-- lcc makefile |
︙ | ︙ | |||
1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 | } # Create the project files # Get names of all Windows projects (GUI and console) my @prognames = &prognames("G:C"); foreach $progname (@prognames) { create_devcpp_project(\%all_object_deps, $progname); } sub create_devcpp_project { my ($all_object_deps, $progname) = @_; # Construct program's dependency info (Taken from 'vcproj', seems to work right here, too.) %seen_objects = (); %lib_files = (); %source_files = (); | > > | 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 | } # Create the project files # Get names of all Windows projects (GUI and console) my @prognames = &prognames("G:C"); foreach $progname (@prognames) { create_devcpp_project(\%all_object_deps, $progname); } chdir $orig_dir; sub create_devcpp_project { my ($all_object_deps, $progname) = @_; # Construct program's dependency info (Taken from 'vcproj', seems to work right here, too.) %seen_objects = (); %lib_files = (); %source_files = (); |
︙ | ︙ | |||
1487 1488 1489 1490 1491 1492 1493 | "ProductName=$windows_project\r\n". "ProductVersion=0.1\r\n". "AutoIncBuildNr=0\r\n"; select STDOUT; close OUT; chdir ".."; } } | > > > > > > > > > > > > > > | 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 | "ProductName=$windows_project\r\n". "ProductVersion=0.1\r\n". "AutoIncBuildNr=0\r\n"; select STDOUT; close OUT; chdir ".."; } } # All done, so do the Unix postprocessing if asked to. if ($do_unix) { chdir $orig_dir; system "./mkauto.sh"; die "mkfiles.pl: mkauto.sh returned $?\n" if $? > 0; if ($do_unix == 1) { chdir ($targetdir = dirname($makefiles{"am"})) or die "$targetdir: chdir: $!\n"; } system "./configure", @confargs; die "mkfiles.pl: configure returned $?\n" if $? > 0; } |
Changes to mksrcarc.sh.
︙ | ︙ | |||
16 17 18 19 20 21 22 | # /dev/null! Apparently its heuristics are doubtful of UTF-8 text # files. bintext=testdata/*.txt # These are actual binary files which we don't want transforming. bin=`{ ls -1 windows/*.ico windows/putty.iss windows/website.url macosx/*.icns; \ find . -name '*.dsp' -print -o -name '*.dsw' -print; }` zip -k -l putty-src.zip $text > /dev/null | | | 16 17 18 19 20 21 22 23 24 | # /dev/null! Apparently its heuristics are doubtful of UTF-8 text # files. bintext=testdata/*.txt # These are actual binary files which we don't want transforming. bin=`{ ls -1 windows/*.ico windows/putty.iss windows/website.url macosx/*.icns; \ find . -name '*.dsp' -print -o -name '*.dsw' -print; }` zip -k -l putty-src.zip $text > /dev/null zip -k -l putty-src.zip $bintext > /dev/null 2>&1 zip -k putty-src.zip $bin > /dev/null |
Changes to mkunxarc.sh.
|
| | | > > | | > > | < > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 | #!/bin/bash # Build a Unix source distribution from the PuTTY CVS area. # # 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; of the form `r1234' to have it tagged as a # custom build. Otherwise it'll be tagged as unidentified. case "$1" in ????-??-??) case "$1" in *[!-0-9]*) echo "Malformed snapshot ID '$1'" >&2;exit 1;;esac autoconfver="`cat LATEST.VER`-$1" arcsuffix="-$autoconfver" ver="-DSNAPSHOT=$1" docver= ;; r*) autoconfver="$1" arcsuffix="-$autoconfver" ver="-DSVN_REV=${1#r}" docver= ;; '') autoconfver="X.XX" # got to put something in here! arcsuffix= ver= docver= ;; *pre) set -- "${1%pre}" "$2" case "$1" in *[!.0-9a-z~]*) echo "Malformed prerelease ID '$1'">&2;exit 1;;esac case "$2" in *[!.0-9a-z~]*) echo "Malformed prerelease revision '$1'">&2;exit 1;;esac autoconfver="$1~pre$2" arcsuffix="-$autoconfver" ver="-DPRERELEASE=$1 -DSVN_REV=$2" docver="VERSION=\"PuTTY prerelease $1:r$2\"" ;; *) case "$1" in *[!.0-9a-z~]*) echo "Malformed release ID '$1'">&2;exit 1;;esac autoconfver="$1" arcsuffix="-$autoconfver" ver="-DRELEASE=$1" docver="VERSION=\"PuTTY release $1\"" ;; esac perl mkfiles.pl (cd doc && make -s ${docver:+"$docver"}) relver=`cat LATEST.VER` arcname="putty$arcsuffix" mkdir uxarc mkdir uxarc/$arcname find . -name uxarc -prune -o \ -name CVS -prune -o \ -name .svn -prune -o \ -name . -o \ -type d -exec mkdir uxarc/$arcname/{} \; find . -name uxarc -prune -o \ -name CVS -prune -o \ -name .cvsignore -prune -o \ -name .svn -prune -o \ -name configure.ac -prune -o \ -name '*.zip' -prune -o \ -name '*.tar.gz' -prune -o \ -type f -exec ln -s $PWD/{} uxarc/$arcname/{} \; if test "x$ver" != "x"; then (cd uxarc/$arcname; md5sum `find . -name '*.[ch]' -print` > manifest; echo "$ver" > version.def) fi sed "s/^AC_INIT(putty,.*/AC_INIT(putty, $autoconfver)/" unix/configure.ac > uxarc/$arcname/unix/configure.ac (cd uxarc/$arcname && sh mkauto.sh) 2>errors || { cat errors >&2; exit 1; } tar -C uxarc -chzof $arcname.tar.gz $arcname rm -rf uxarc |
Changes to network.h.
︙ | ︙ | |||
11 12 13 14 15 16 17 | */ #ifndef PUTTY_NETWORK_H #define PUTTY_NETWORK_H #ifndef DONE_TYPEDEFS #define DONE_TYPEDEFS | | < < < < > < < > > > | 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 | */ #ifndef PUTTY_NETWORK_H #define PUTTY_NETWORK_H #ifndef DONE_TYPEDEFS #define DONE_TYPEDEFS typedef struct conf_tag Conf; typedef struct backend_tag Backend; typedef struct terminal_tag Terminal; #endif typedef struct SockAddr_tag *SockAddr; /* pay attention to levels of indirection */ typedef struct socket_function_table **Socket; typedef struct plug_function_table **Plug; struct socket_function_table { Plug(*plug) (Socket s, Plug p); /* use a different plug (return the old one) */ /* if p is NULL, it doesn't change the plug */ /* but it does return the one it's using */ void (*close) (Socket s); int (*write) (Socket s, const char *data, int len); int (*write_oob) (Socket s, const char *data, int len); void (*write_eof) (Socket s); void (*flush) (Socket s); void (*set_frozen) (Socket s, int is_frozen); /* ignored by tcp, but vital for ssl */ const char *(*socket_error) (Socket s); }; typedef union { void *p; int i; } accept_ctx_t; typedef Socket (*accept_fn_t)(accept_ctx_t ctx, Plug plug); struct plug_function_table { void (*log)(Plug p, int type, SockAddr addr, int port, const char *error_msg, int error_code); /* * Passes the client progress reports on the process of setting * up the connection. * |
︙ | ︙ | |||
78 79 80 81 82 83 84 | */ void (*sent) (Plug p, int bufsize); /* * The `sent' function is called when the pending send backlog * on a socket is cleared or partially cleared. The new backlog * size is passed in the `bufsize' parameter. */ | | > > > | | | | > > | > | > < < > | < < < < < < < < < < | 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 | */ void (*sent) (Plug p, int bufsize); /* * The `sent' function is called when the pending send backlog * on a socket is cleared or partially cleared. The new backlog * size is passed in the `bufsize' parameter. */ int (*accepting)(Plug p, accept_fn_t constructor, accept_ctx_t ctx); /* * `accepting' is called only on listener-type sockets, and is * passed a constructor function+context that will create a fresh * Socket describing the connection. It returns nonzero if it * doesn't want the connection for some reason, or 0 on success. */ }; /* proxy indirection layer */ /* NB, control of 'addr' is passed via new_connection, which takes * responsibility for freeing it */ Socket new_connection(SockAddr addr, char *hostname, int port, int privport, int oobinline, int nodelay, int keepalive, Plug plug, Conf *conf); Socket new_listener(char *srcaddr, int port, Plug plug, int local_host_only, Conf *conf, int addressfamily); SockAddr name_lookup(char *host, int port, char **canonicalname, Conf *conf, int addressfamily); int proxy_for_destination (SockAddr addr, const char *hostname, int port, Conf *conf); /* platform-dependent callback from new_connection() */ /* (same caveat about addr as new_connection()) */ Socket platform_new_connection(SockAddr addr, char *hostname, int port, int privport, int oobinline, int nodelay, int keepalive, Plug plug, Conf *conf); /* socket functions */ void sk_init(void); /* called once at program startup */ void sk_cleanup(void); /* called just before program exit */ SockAddr sk_namelookup(const char *host, char **canonicalname, int address_family); SockAddr sk_nonamelookup(const char *host); void sk_getaddr(SockAddr addr, char *buf, int buflen); int sk_addr_needs_port(SockAddr addr); int sk_hostname_is_local(const char *name); int sk_address_is_local(SockAddr addr); int sk_address_is_special_local(SockAddr addr); int sk_addrtype(SockAddr addr); void sk_addrcopy(SockAddr addr, char *buf); void sk_addr_free(SockAddr addr); /* sk_addr_dup generates another SockAddr which contains the same data * as the original one and can be freed independently. May not actually * physically _duplicate_ it: incrementing a reference count so that * one more free is required before it disappears is an acceptable * implementation. */ SockAddr sk_addr_dup(SockAddr addr); /* NB, control of 'addr' is passed via sk_new, which takes responsibility * for freeing it, as for new_connection() */ Socket sk_new(SockAddr addr, int port, int privport, int oobinline, int nodelay, int keepalive, Plug p); Socket sk_newlistener(char *srcaddr, int port, Plug plug, int local_host_only, int address_family); #define sk_plug(s,p) (((*s)->plug) (s, p)) #define sk_close(s) (((*s)->close) (s)) #define sk_write(s,buf,len) (((*s)->write) (s, buf, len)) #define sk_write_oob(s,buf,len) (((*s)->write_oob) (s, buf, len)) #define sk_write_eof(s) (((*s)->write_eof) (s)) #define sk_flush(s) (((*s)->flush) (s)) #ifdef DEFINE_PLUG_METHOD_MACROS #define plug_log(p,type,addr,port,msg,code) (((*p)->log) (p, type, addr, port, msg, code)) #define plug_closing(p,msg,code,callback) (((*p)->closing) (p, msg, code, callback)) #define plug_receive(p,urgent,buf,len) (((*p)->receive) (p, urgent, buf, len)) #define plug_sent(p,bufsize) (((*p)->sent) (p, bufsize)) #define plug_accepting(p, constructor, ctx) (((*p)->accepting)(p, constructor, ctx)) #endif /* * Special error values are returned from sk_namelookup and sk_new * if there's a problem. These functions extract an error message, * or return NULL if there's no problem. */ const char *sk_addr_error(SockAddr addr); #define sk_socket_error(s) (((*s)->socket_error) (s)) |
︙ | ︙ | |||
183 184 185 186 187 188 189 | * - Socket buffering: if an SSH channel (or the whole connection) * backs up or presents a zero window, we must freeze the * associated local socket in order to avoid unbounded buffer * growth. */ #define sk_set_frozen(s, is_frozen) (((*s)->set_frozen) (s, is_frozen)) | < < < < < < > > > > > > | 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 | * - Socket buffering: if an SSH channel (or the whole connection) * backs up or presents a zero window, we must freeze the * associated local socket in order to avoid unbounded buffer * growth. */ #define sk_set_frozen(s, is_frozen) (((*s)->set_frozen) (s, is_frozen)) /* * Simple wrapper on getservbyname(), needed by ssh.c. Returns the * port number, in host byte order (suitable for printf and so on). * Returns 0 on failure. Any platform not supporting getservbyname * can just return 0 - this function is not required to handle * numeric port specifications. */ int net_service_lookup(char *service); /* * Look up the local hostname; return value needs freeing. * May return NULL. */ char *get_hostname(void); /* * Trivial socket implementation which just stores an error. Found in * errsock.c. */ Socket new_error_socket(const char *errmsg, Plug plug); /********** SSL stuff **********/ /* * This section is subject to change, but you get the general idea * of what it will eventually look like. */ |
︙ | ︙ |
Added noshare.c.
> > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | /* * Stub implementation of SSH connection-sharing IPC, for any * platform which can't support it at all. */ #include <stdio.h> #include <assert.h> #include <errno.h> #include "tree234.h" #include "putty.h" #include "ssh.h" #include "network.h" int platform_ssh_share(const char *name, Conf *conf, Plug downplug, Plug upplug, Socket *sock, char **logtext) { return SHARE_NONE; } void platform_ssh_share_cleanup(const char *name) { } |
Added noterm.c.
> > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 | /* * Stubs of functions in terminal.c, for use in programs that don't * have a terminal. */ #include "putty.h" #include "terminal.h" void term_nopaste(Terminal *term) { } |
Changes to notiming.c.
1 2 3 4 5 6 7 8 9 10 11 12 13 | /* * notiming.c: stub version of timing API. * * Used in any tool which needs a subsystem linked against the * timing API but doesn't want to actually provide timing. For * example, key generation tools need the random number generator, * but they don't want the hassle of calling noise_regular() at * regular intervals - and they don't _need_ it either, since they * have their own rigorous and different means of noise collection. */ #include "putty.h" | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | /* * notiming.c: stub version of timing API. * * Used in any tool which needs a subsystem linked against the * timing API but doesn't want to actually provide timing. For * example, key generation tools need the random number generator, * but they don't want the hassle of calling noise_regular() at * regular intervals - and they don't _need_ it either, since they * have their own rigorous and different means of noise collection. */ #include "putty.h" unsigned long schedule_timer(int ticks, timer_fn_t fn, void *ctx) { return 0; } void expire_timer_context(void *ctx) { } |
Changes to pinger.c.
1 2 3 4 5 6 7 8 9 10 | /* * pinger.c: centralised module that deals with sending TS_PING * keepalives, to avoid replicating this code in multiple backends. */ #include "putty.h" struct pinger_tag { int interval; int pending; | | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | /* * pinger.c: centralised module that deals with sending TS_PING * keepalives, to avoid replicating this code in multiple backends. */ #include "putty.h" struct pinger_tag { int interval; int pending; unsigned long next; Backend *back; void *backhandle; }; static void pinger_schedule(Pinger pinger); static void pinger_timer(void *ctx, unsigned long now) { Pinger pinger = (Pinger)ctx; if (pinger->pending && now == pinger->next) { pinger->back->special(pinger->backhandle, TS_PING); pinger->pending = FALSE; pinger_schedule(pinger); } } static void pinger_schedule(Pinger pinger) |
︙ | ︙ | |||
39 40 41 42 43 44 45 | pinger_timer, pinger); if (!pinger->pending || next < pinger->next) { pinger->next = next; pinger->pending = TRUE; } } | | | | > | | | 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 | pinger_timer, pinger); if (!pinger->pending || next < pinger->next) { pinger->next = next; pinger->pending = TRUE; } } Pinger pinger_new(Conf *conf, Backend *back, void *backhandle) { Pinger pinger = snew(struct pinger_tag); pinger->interval = conf_get_int(conf, CONF_ping_interval); pinger->pending = FALSE; pinger->back = back; pinger->backhandle = backhandle; pinger_schedule(pinger); return pinger; } void pinger_reconfig(Pinger pinger, Conf *oldconf, Conf *newconf) { int newinterval = conf_get_int(newconf, CONF_ping_interval); if (conf_get_int(oldconf, CONF_ping_interval) != newinterval) { pinger->interval = newinterval; pinger_schedule(pinger); } } void pinger_free(Pinger pinger) { expire_timer_context(pinger); sfree(pinger); } |
Deleted pkcs11.h.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Changes to portfwd.c.
︙ | ︙ | |||
11 12 13 14 15 16 17 | #ifndef FALSE #define FALSE 0 #endif #ifndef TRUE #define TRUE 1 #endif | | | | | | < < < | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > | | < < | | > > > > > > | > | > > > > > > > > > > > > > > > > > > | | < < < < | < < < < | < < > | < < < | | | | | > | | | | > | | | | | | | | | | | | > > | | | | | | | | | | | | | | | > | | | | | | | | | | | | | | | | | | | | | | | | | > | | | > > | | | | | | | | | | | | | | | | | > > > > | | | | > > | < | | | | | | | | | > | > > | > | < | > | | < | | | | | | < < | | | | | | | | | | | | > | < > > > | | | | | | | | | < < | | < | | | | < < | | > | | > | > > | < < < < | < < | < < < < < | > | > > > | > | < | < | | | < | < | | | | | > | < > > | | | | < | | | | | | | | 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 | #ifndef FALSE #define FALSE 0 #endif #ifndef TRUE #define TRUE 1 #endif struct PortForwarding { const struct plug_function_table *fn; /* the above variable absolutely *must* be the first in this structure */ struct ssh_channel *c; /* channel structure held by ssh.c */ void *backhandle; /* instance of SSH backend itself */ /* Note that backhandle need not be filled in if c is non-NULL */ Socket s; int throttled, throttle_override; int ready; /* * `dynamic' does double duty. It's set to 0 for an ordinary * forwarded port, and nonzero for SOCKS-style dynamic port * forwarding; but the nonzero values are also a state machine * tracking where the SOCKS exchange has got to. */ int dynamic; /* * `hostname' and `port' are the real hostname and port, once * we know what we're connecting to. */ char *hostname; int port; /* * `socksbuf' is the buffer we use to accumulate a SOCKS request. */ char *socksbuf; int sockslen, sockssize; /* * When doing dynamic port forwarding, we can receive * connection data before we are actually able to send it; so * we may have to temporarily hold some in a dynamically * allocated buffer here. */ void *buffer; int buflen; }; struct PortListener { const struct plug_function_table *fn; /* the above variable absolutely *must* be the first in this structure */ void *backhandle; /* instance of SSH backend itself */ Socket s; /* * `dynamic' is set to 0 for an ordinary forwarded port, and * nonzero for SOCKS-style dynamic port forwarding. */ int dynamic; /* * `hostname' and `port' are the real hostname and port, for * ordinary forwardings. */ char *hostname; int port; }; static struct PortForwarding *new_portfwd_state(void) { struct PortForwarding *pf = snew(struct PortForwarding); pf->hostname = NULL; pf->socksbuf = NULL; pf->sockslen = pf->sockssize = 0; pf->buffer = NULL; return pf; } static void free_portfwd_state(struct PortForwarding *pf) { if (!pf) return; sfree(pf->hostname); sfree(pf->socksbuf); sfree(pf->buffer); sfree(pf); } static struct PortListener *new_portlistener_state(void) { struct PortListener *pl = snew(struct PortListener); pl->hostname = NULL; return pl; } static void free_portlistener_state(struct PortListener *pl) { if (!pl) return; sfree(pl->hostname); sfree(pl); } static void pfd_log(Plug plug, int type, SockAddr addr, int port, const char *error_msg, int error_code) { /* we have to dump these since we have no interface to logging.c */ } static void pfl_log(Plug plug, int type, SockAddr addr, int port, const char *error_msg, int error_code) { /* we have to dump these since we have no interface to logging.c */ } static int pfd_closing(Plug plug, const char *error_msg, int error_code, int calling_back) { struct PortForwarding *pf = (struct PortForwarding *) plug; if (error_msg) { /* * Socket error. Slam the connection instantly shut. */ if (pf->c) { sshfwd_unclean_close(pf->c, error_msg); } else { /* * We might not have an SSH channel, if a socket error * occurred during SOCKS negotiation. If not, we must * clean ourself up without sshfwd_unclean_close's call * back to pfd_close. */ pfd_close(pf); } } else { /* * Ordinary EOF received on socket. Send an EOF on the SSH * channel. */ if (pf->c) sshfwd_write_eof(pf->c); } return 1; } static int pfl_closing(Plug plug, const char *error_msg, int error_code, int calling_back) { struct PortListener *pl = (struct PortListener *) plug; pfl_terminate(pl); return 1; } static int pfd_receive(Plug plug, int urgent, char *data, int len) { struct PortForwarding *pf = (struct PortForwarding *) plug; if (pf->dynamic) { while (len--) { if (pf->sockslen >= pf->sockssize) { pf->sockssize = pf->sockslen * 5 / 4 + 256; pf->socksbuf = sresize(pf->socksbuf, pf->sockssize, char); } pf->socksbuf[pf->sockslen++] = *data++; /* * Now check what's in the buffer to see if it's a * valid and complete message in the SOCKS exchange. */ if ((pf->dynamic == 1 || (pf->dynamic >> 12) == 4) && pf->socksbuf[0] == 4) { /* * SOCKS 4. */ if (pf->dynamic == 1) pf->dynamic = 0x4000; if (pf->sockslen < 2) continue; /* don't have command code yet */ if (pf->socksbuf[1] != 1) { /* Not CONNECT. */ /* Send back a SOCKS 4 error before closing. */ char data[8]; memset(data, 0, sizeof(data)); data[1] = 91; /* generic `request rejected' */ sk_write(pf->s, data, 8); pfd_close(pf); return 1; } if (pf->sockslen <= 8) continue; /* haven't started user/hostname */ if (pf->socksbuf[pf->sockslen-1] != 0) continue; /* haven't _finished_ user/hostname */ /* * Now we have a full SOCKS 4 request. Check it to * see if it's a SOCKS 4A request. */ if (pf->socksbuf[4] == 0 && pf->socksbuf[5] == 0 && pf->socksbuf[6] == 0 && pf->socksbuf[7] != 0) { /* * It's SOCKS 4A. So if we haven't yet * collected the host name, we should continue * waiting for data in order to do so; if we * have, we can go ahead. */ int len; if (pf->dynamic == 0x4000) { pf->dynamic = 0x4001; pf->sockslen = 8; /* reset buffer to overwrite name */ continue; } pf->socksbuf[0] = 0; /* reply version code */ pf->socksbuf[1] = 90; /* request granted */ sk_write(pf->s, pf->socksbuf, 8); len = pf->sockslen - 8; pf->port = GET_16BIT_MSB_FIRST(pf->socksbuf+2); pf->hostname = snewn(len+1, char); pf->hostname[len] = '\0'; memcpy(pf->hostname, pf->socksbuf + 8, len); goto connect; } else { /* * It's SOCKS 4, which means we should format * the IP address into the hostname string and * then just go. */ pf->socksbuf[0] = 0; /* reply version code */ pf->socksbuf[1] = 90; /* request granted */ sk_write(pf->s, pf->socksbuf, 8); pf->port = GET_16BIT_MSB_FIRST(pf->socksbuf+2); pf->hostname = dupprintf("%d.%d.%d.%d", (unsigned char)pf->socksbuf[4], (unsigned char)pf->socksbuf[5], (unsigned char)pf->socksbuf[6], (unsigned char)pf->socksbuf[7]); goto connect; } } if ((pf->dynamic == 1 || (pf->dynamic >> 12) == 5) && pf->socksbuf[0] == 5) { /* * SOCKS 5. */ if (pf->dynamic == 1) pf->dynamic = 0x5000; if (pf->dynamic == 0x5000) { int i, method; char data[2]; /* * We're receiving a set of method identifiers. */ if (pf->sockslen < 2) continue; /* no method count yet */ if (pf->sockslen < 2 + (unsigned char)pf->socksbuf[1]) continue; /* no methods yet */ method = 0xFF; /* invalid */ for (i = 0; i < (unsigned char)pf->socksbuf[1]; i++) if (pf->socksbuf[2+i] == 0) { method = 0;/* no auth */ break; } data[0] = 5; data[1] = method; sk_write(pf->s, data, 2); pf->dynamic = 0x5001; pf->sockslen = 0; /* re-empty the buffer */ continue; } if (pf->dynamic == 0x5001) { /* * We're receiving a SOCKS request. */ unsigned char reply[10]; /* SOCKS5 atyp=1 reply */ int atype, alen = 0; /* * Pre-fill reply packet. * In all cases, we set BND.{HOST,ADDR} to 0.0.0.0:0 * (atyp=1) in the reply; if we succeed, we don't know * the right answers, and if we fail, they should be * ignored. */ memset(reply, 0, lenof(reply)); reply[0] = 5; /* VER */ reply[3] = 1; /* ATYP = 1 (IPv4, 0.0.0.0:0) */ if (pf->sockslen < 6) continue; atype = (unsigned char)pf->socksbuf[3]; if (atype == 1) /* IPv4 address */ alen = 4; if (atype == 4) /* IPv6 address */ alen = 16; if (atype == 3) /* domain name has leading length */ alen = 1 + (unsigned char)pf->socksbuf[4]; if (pf->sockslen < 6 + alen) continue; if (pf->socksbuf[1] != 1 || pf->socksbuf[2] != 0) { /* Not CONNECT or reserved field nonzero - error */ reply[1] = 1; /* generic failure */ sk_write(pf->s, (char *) reply, lenof(reply)); pfd_close(pf); return 1; } /* * Now we have a viable connect request. Switch * on atype. */ pf->port = GET_16BIT_MSB_FIRST(pf->socksbuf+4+alen); if (atype == 1) { /* REP=0 (success) already */ sk_write(pf->s, (char *) reply, lenof(reply)); pf->hostname = dupprintf("%d.%d.%d.%d", (unsigned char)pf->socksbuf[4], (unsigned char)pf->socksbuf[5], (unsigned char)pf->socksbuf[6], (unsigned char)pf->socksbuf[7]); goto connect; } else if (atype == 3) { /* REP=0 (success) already */ sk_write(pf->s, (char *) reply, lenof(reply)); pf->hostname = snewn(alen, char); pf->hostname[alen-1] = '\0'; memcpy(pf->hostname, pf->socksbuf + 5, alen-1); goto connect; } else { /* * Unknown address type. (FIXME: support IPv6!) */ reply[1] = 8; /* atype not supported */ sk_write(pf->s, (char *) reply, lenof(reply)); pfd_close(pf); return 1; } } } /* * If we get here without either having done `continue' * or `goto connect', it must be because there is no * sensible interpretation of what's in our buffer. So * close the connection rudely. */ pfd_close(pf); return 1; } return 1; /* * We come here when we're ready to make an actual * connection. */ connect: sfree(pf->socksbuf); pf->socksbuf = NULL; /* * Freeze the socket until the SSH server confirms the * connection. */ sk_set_frozen(pf->s, 1); pf->c = new_sock_channel(pf->backhandle, pf); if (pf->c == NULL) { pfd_close(pf); return 1; } else { /* asks to forward to the specified host/port for this */ ssh_send_port_open(pf->c, pf->hostname, pf->port, "forwarding"); } pf->dynamic = 0; /* * If there's any data remaining in our current buffer, * save it to be sent on pfd_confirm(). */ if (len > 0) { pf->buffer = snewn(len, char); memcpy(pf->buffer, data, len); pf->buflen = len; } } if (pf->ready) { if (sshfwd_write(pf->c, data, len) > 0) { pf->throttled = 1; sk_set_frozen(pf->s, 1); } } return 1; } static void pfd_sent(Plug plug, int bufsize) { struct PortForwarding *pf = (struct PortForwarding *) plug; if (pf->c) sshfwd_unthrottle(pf->c, bufsize); } /* * Called when receiving a PORT OPEN from the server to make a * connection to a destination host. * * On success, returns NULL and fills in *pf_ret. On error, returns a * dynamically allocated error message string. */ char *pfd_connect(struct PortForwarding **pf_ret, char *hostname,int port, void *c, Conf *conf, int addressfamily) { static const struct plug_function_table fn_table = { pfd_log, pfd_closing, pfd_receive, pfd_sent, NULL }; SockAddr addr; const char *err; char *dummy_realhost; struct PortForwarding *pf; /* * Try to find host. */ addr = name_lookup(hostname, port, &dummy_realhost, conf, addressfamily); if ((err = sk_addr_error(addr)) != NULL) { char *err_ret = dupstr(err); sk_addr_free(addr); sfree(dummy_realhost); return err_ret; } /* * Open socket. */ pf = *pf_ret = new_portfwd_state(); pf->fn = &fn_table; pf->throttled = pf->throttle_override = 0; pf->ready = 1; pf->c = c; pf->backhandle = NULL; /* we shouldn't need this */ pf->dynamic = 0; pf->s = new_connection(addr, dummy_realhost, port, 0, 1, 0, 0, (Plug) pf, conf); sfree(dummy_realhost); if ((err = sk_socket_error(pf->s)) != NULL) { char *err_ret = dupstr(err); sk_close(pf->s); free_portfwd_state(pf); *pf_ret = NULL; return err_ret; } return NULL; } /* called when someone connects to the local port */ static int pfl_accepting(Plug p, accept_fn_t constructor, accept_ctx_t ctx) { static const struct plug_function_table fn_table = { pfd_log, pfd_closing, pfd_receive, pfd_sent, NULL }; struct PortForwarding *pf; struct PortListener *pl; Socket s; const char *err; pl = (struct PortListener *)p; pf = new_portfwd_state(); pf->fn = &fn_table; pf->c = NULL; pf->backhandle = pl->backhandle; pf->s = s = constructor(ctx, (Plug) pf); if ((err = sk_socket_error(s)) != NULL) { free_portfwd_state(pf); return err != NULL; } pf->throttled = pf->throttle_override = 0; pf->ready = 0; if (pl->dynamic) { pf->dynamic = 1; pf->port = 0; /* "hostname" buffer is so far empty */ sk_set_frozen(s, 0); /* we want to receive SOCKS _now_! */ } else { pf->dynamic = 0; pf->hostname = dupstr(pl->hostname); pf->port = pl->port; pf->c = new_sock_channel(pl->backhandle, pf); if (pf->c == NULL) { free_portfwd_state(pf); return 1; } else { /* asks to forward to the specified host/port for this */ ssh_send_port_open(pf->c, pf->hostname, pf->port, "forwarding"); } } return 0; } /* * Add a new port-forwarding listener from srcaddr:port -> desthost:destport. * * On success, returns NULL and fills in *pl_ret. On error, returns a * dynamically allocated error message string. */ char *pfl_listen(char *desthost, int destport, char *srcaddr, int port, void *backhandle, Conf *conf, struct PortListener **pl_ret, int address_family) { static const struct plug_function_table fn_table = { pfl_log, pfl_closing, NULL, /* recv */ NULL, /* send */ pfl_accepting }; const char *err; struct PortListener *pl; /* * Open socket. */ pl = *pl_ret = new_portlistener_state(); pl->fn = &fn_table; if (desthost) { pl->hostname = dupstr(desthost); pl->port = destport; pl->dynamic = 0; } else pl->dynamic = 1; pl->backhandle = backhandle; pl->s = new_listener(srcaddr, port, (Plug) pl, !conf_get_int(conf, CONF_lport_acceptall), conf, address_family); if ((err = sk_socket_error(pl->s)) != NULL) { char *err_ret = dupstr(err); sk_close(pl->s); free_portlistener_state(pl); *pl_ret = NULL; return err_ret; } return NULL; } void pfd_close(struct PortForwarding *pf) { if (!pf) return; sk_close(pf->s); free_portfwd_state(pf); } /* * Terminate a listener. */ void pfl_terminate(struct PortListener *pl) { if (!pl) return; sk_close(pl->s); free_portlistener_state(pl); } void pfd_unthrottle(struct PortForwarding *pf) { if (!pf) return; pf->throttled = 0; sk_set_frozen(pf->s, pf->throttled || pf->throttle_override); } void pfd_override_throttle(struct PortForwarding *pf, int enable) { if (!pf) return; pf->throttle_override = enable; sk_set_frozen(pf->s, pf->throttled || pf->throttle_override); } /* * Called to send data down the raw connection. */ int pfd_send(struct PortForwarding *pf, char *data, int len) { if (pf == NULL) return 0; return sk_write(pf->s, data, len); } void pfd_send_eof(struct PortForwarding *pf) { sk_write_eof(pf->s); } void pfd_confirm(struct PortForwarding *pf) { if (pf == NULL) return; pf->ready = 1; sk_set_frozen(pf->s, 0); sk_write(pf->s, NULL, 0); if (pf->buffer) { sshfwd_write(pf->c, pf->buffer, pf->buflen); sfree(pf->buffer); pf->buffer = NULL; } } |
Changes to pproxy.c.
1 2 3 4 5 6 7 8 9 10 11 12 13 | /* * pproxy.c: dummy implementation of platform_new_connection(), to * be supplanted on any platform which has its own local proxy * method. */ #include "putty.h" #include "network.h" #include "proxy.h" Socket platform_new_connection(SockAddr addr, char *hostname, int port, int privport, int oobinline, int nodelay, int keepalive, | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | /* * pproxy.c: dummy implementation of platform_new_connection(), to * be supplanted on any platform which has its own local proxy * method. */ #include "putty.h" #include "network.h" #include "proxy.h" Socket platform_new_connection(SockAddr addr, char *hostname, int port, int privport, int oobinline, int nodelay, int keepalive, Plug plug, Conf *conf) { return NULL; } |
Changes to proxy.c.
︙ | ︙ | |||
10 11 12 13 14 15 16 | #include <string.h> #define DEFINE_PLUG_METHOD_MACROS #include "putty.h" #include "network.h" #include "proxy.h" | | | > | | 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | #include <string.h> #define DEFINE_PLUG_METHOD_MACROS #include "putty.h" #include "network.h" #include "proxy.h" #define do_proxy_dns(conf) \ (conf_get_int(conf, CONF_proxy_dns) == FORCE_ON || \ (conf_get_int(conf, CONF_proxy_dns) == AUTO && \ conf_get_int(conf, CONF_proxy_type) != PROXY_SOCKS4)) /* * Call this when proxy negotiation is complete, so that this * socket can begin working normally. */ void proxy_activate (Proxy_Socket p) { |
︙ | ︙ | |||
60 61 62 63 64 65 66 67 68 69 70 71 72 73 | plug_sent(p->plug, output_after); /* if we were asked to flush the output during * the proxy negotiation process, do so now. */ if (p->pending_flush) sk_flush(p->sub_socket); /* if the backend wanted the socket unfrozen, try to unfreeze. * our set_frozen handler will flush buffered receive data before * unfreezing the actual underlying socket. */ if (!p->freeze) sk_set_frozen((Socket)p, 0); } | > > > | 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 | plug_sent(p->plug, output_after); /* if we were asked to flush the output during * the proxy negotiation process, do so now. */ if (p->pending_flush) sk_flush(p->sub_socket); /* if we have a pending EOF to send, send it */ if (p->pending_eof) sk_write_eof(p->sub_socket); /* if the backend wanted the socket unfrozen, try to unfreeze. * our set_frozen handler will flush buffered receive data before * unfreezing the actual underlying socket. */ if (!p->freeze) sk_set_frozen((Socket)p, 0); } |
︙ | ︙ | |||
111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 | bufchain_clear(&ps->pending_output_data); bufchain_clear(&ps->pending_oob_output_data); bufchain_add(&ps->pending_oob_output_data, data, len); return len; } return sk_write_oob(ps->sub_socket, data, len); } static void sk_proxy_flush (Socket s) { Proxy_Socket ps = (Proxy_Socket) s; if (ps->state != PROXY_STATE_ACTIVE) { ps->pending_flush = 1; return; } sk_flush(ps->sub_socket); } | > > > > > > > > > > > < < < < < < < < < < < < | 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 | bufchain_clear(&ps->pending_output_data); bufchain_clear(&ps->pending_oob_output_data); bufchain_add(&ps->pending_oob_output_data, data, len); return len; } return sk_write_oob(ps->sub_socket, data, len); } static void sk_proxy_write_eof (Socket s) { Proxy_Socket ps = (Proxy_Socket) s; if (ps->state != PROXY_STATE_ACTIVE) { ps->pending_eof = 1; return; } sk_write_eof(ps->sub_socket); } static void sk_proxy_flush (Socket s) { Proxy_Socket ps = (Proxy_Socket) s; if (ps->state != PROXY_STATE_ACTIVE) { ps->pending_flush = 1; return; } sk_flush(ps->sub_socket); } static void sk_proxy_set_frozen (Socket s, int is_frozen) { Proxy_Socket ps = (Proxy_Socket) s; if (ps->state != PROXY_STATE_ACTIVE) { ps->freeze = is_frozen; return; |
︙ | ︙ | |||
242 243 244 245 246 247 248 | ps->sent_bufsize = bufsize; ps->negotiate(ps, PROXY_CHANGE_SENT); return; } plug_sent(ps->plug, bufsize); } | | > > | | | | > > > > > > > > > | | | 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 | ps->sent_bufsize = bufsize; ps->negotiate(ps, PROXY_CHANGE_SENT); return; } plug_sent(ps->plug, bufsize); } static int plug_proxy_accepting(Plug p, accept_fn_t constructor, accept_ctx_t ctx) { Proxy_Plug pp = (Proxy_Plug) p; Proxy_Socket ps = pp->proxy_socket; if (ps->state != PROXY_STATE_ACTIVE) { ps->accepting_constructor = constructor; ps->accepting_ctx = ctx; return ps->negotiate(ps, PROXY_CHANGE_ACCEPTING); } return plug_accepting(ps->plug, constructor, ctx); } /* * This function can accept a NULL pointer as `addr', in which case * it will only check the host name. */ int proxy_for_destination (SockAddr addr, const char *hostname, int port, Conf *conf) { int s = 0, e = 0; char hostip[64]; int hostip_len, hostname_len; const char *exclude_list; /* * Special local connections such as Unix-domain sockets * unconditionally cannot be proxied, even in proxy-localhost * mode. There just isn't any way to ask any known proxy type for * them. */ if (addr && sk_address_is_special_local(addr)) return 0; /* do not proxy */ /* * Check the host name and IP against the hard-coded * representations of `localhost'. */ if (!conf_get_int(conf, CONF_even_proxy_localhost) && (sk_hostname_is_local(hostname) || (addr && sk_address_is_local(addr)))) return 0; /* do not proxy */ /* we want a string representation of the IP address for comparisons */ if (addr) { sk_getaddr(addr, hostip, 64); hostip_len = strlen(hostip); } else hostip_len = 0; /* placate gcc; shouldn't be required */ hostname_len = strlen(hostname); exclude_list = conf_get_str(conf, CONF_proxy_exclude_list); /* now parse the exclude list, and see if either our IP * or hostname matches anything in it. */ while (exclude_list[s]) { while (exclude_list[s] && |
︙ | ︙ | |||
345 346 347 348 349 350 351 | } /* no matches in the exclude list, so use the proxy */ return 1; } SockAddr name_lookup(char *host, int port, char **canonicalname, | | | | | | | | < | | > | | > | > | | | | | | > > > | > | | 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 | } /* no matches in the exclude list, so use the proxy */ return 1; } SockAddr name_lookup(char *host, int port, char **canonicalname, Conf *conf, int addressfamily) { if (conf_get_int(conf, CONF_proxy_type) != PROXY_NONE && do_proxy_dns(conf) && proxy_for_destination(NULL, host, port, conf)) { *canonicalname = dupstr(host); return sk_nonamelookup(host); } return sk_namelookup(host, canonicalname, addressfamily); } Socket new_connection(SockAddr addr, char *hostname, int port, int privport, int oobinline, int nodelay, int keepalive, Plug plug, Conf *conf) { static const struct socket_function_table socket_fn_table = { sk_proxy_plug, sk_proxy_close, sk_proxy_write, sk_proxy_write_oob, sk_proxy_write_eof, sk_proxy_flush, sk_proxy_set_frozen, sk_proxy_socket_error }; static const struct plug_function_table plug_fn_table = { plug_proxy_log, plug_proxy_closing, plug_proxy_receive, plug_proxy_sent, plug_proxy_accepting }; if (conf_get_int(conf, CONF_proxy_type) != PROXY_NONE && proxy_for_destination(addr, hostname, port, conf)) { Proxy_Socket ret; Proxy_Plug pplug; SockAddr proxy_addr; char *proxy_canonical_name; Socket sret; int type; if ((sret = platform_new_connection(addr, hostname, port, privport, oobinline, nodelay, keepalive, plug, conf)) != NULL) return sret; ret = snew(struct Socket_proxy_tag); ret->fn = &socket_fn_table; ret->conf = conf_copy(conf); ret->plug = plug; ret->remote_addr = addr; /* will need to be freed on close */ ret->remote_port = port; ret->error = NULL; ret->pending_flush = 0; ret->pending_eof = 0; ret->freeze = 0; bufchain_init(&ret->pending_input_data); bufchain_init(&ret->pending_output_data); bufchain_init(&ret->pending_oob_output_data); ret->sub_socket = NULL; ret->state = PROXY_STATE_NEW; ret->negotiate = NULL; type = conf_get_int(conf, CONF_proxy_type); if (type == PROXY_HTTP) { ret->negotiate = proxy_http_negotiate; } else if (type == PROXY_SOCKS4) { ret->negotiate = proxy_socks4_negotiate; } else if (type == PROXY_SOCKS5) { ret->negotiate = proxy_socks5_negotiate; } else if (type == PROXY_TELNET) { ret->negotiate = proxy_telnet_negotiate; } else { ret->error = "Proxy error: Unknown proxy method"; return (Socket) ret; } /* create the proxy plug to map calls from the actual * socket into our proxy socket layer */ pplug = snew(struct Plug_proxy_tag); pplug->fn = &plug_fn_table; pplug->proxy_socket = ret; /* look-up proxy */ proxy_addr = sk_namelookup(conf_get_str(conf, CONF_proxy_host), &proxy_canonical_name, conf_get_int(conf, CONF_addressfamily)); if (sk_addr_error(proxy_addr) != NULL) { ret->error = "Proxy error: Unable to resolve proxy host name"; sfree(pplug); sk_addr_free(proxy_addr); return (Socket)ret; } sfree(proxy_canonical_name); /* create the actual socket we will be using, * connected to our proxy server and port. */ ret->sub_socket = sk_new(proxy_addr, conf_get_int(conf, CONF_proxy_port), privport, oobinline, nodelay, keepalive, (Plug) pplug); if (sk_socket_error(ret->sub_socket) != NULL) return (Socket) ret; /* start the proxy negotiation process... */ sk_set_frozen(ret->sub_socket, 0); ret->negotiate(ret, PROXY_CHANGE_NEW); return (Socket) ret; } /* no proxy, so just return the direct socket */ return sk_new(addr, port, privport, oobinline, nodelay, keepalive, plug); } Socket new_listener(char *srcaddr, int port, Plug plug, int local_host_only, Conf *conf, int addressfamily) { /* TODO: SOCKS (and potentially others) support inbound * TODO: connections via the proxy. support them. */ return sk_newlistener(srcaddr, port, plug, local_host_only, addressfamily); } |
︙ | ︙ | |||
521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 | if (p->state == PROXY_STATE_NEW) { /* we are just beginning the proxy negotiate process, * so we'll send off the initial bits of the request. * for this proxy method, it's just a simple HTTP * request */ char *buf, dest[512]; sk_getaddr(p->remote_addr, dest, lenof(dest)); buf = dupprintf("CONNECT %s:%i HTTP/1.1\r\nHost: %s:%i\r\n", dest, p->remote_port, dest, p->remote_port); sk_write(p->sub_socket, buf, strlen(buf)); sfree(buf); | > | | > | | > > > | 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 | if (p->state == PROXY_STATE_NEW) { /* we are just beginning the proxy negotiate process, * so we'll send off the initial bits of the request. * for this proxy method, it's just a simple HTTP * request */ char *buf, dest[512]; char *username, *password; sk_getaddr(p->remote_addr, dest, lenof(dest)); buf = dupprintf("CONNECT %s:%i HTTP/1.1\r\nHost: %s:%i\r\n", dest, p->remote_port, dest, p->remote_port); sk_write(p->sub_socket, buf, strlen(buf)); sfree(buf); username = conf_get_str(p->conf, CONF_proxy_username); password = conf_get_str(p->conf, CONF_proxy_password); if (username[0] || password[0]) { char *buf, *buf2; int i, j, len; buf = dupprintf("%s:%s", username, password); len = strlen(buf); buf2 = snewn(len * 4 / 3 + 100, char); sprintf(buf2, "Proxy-Authorization: Basic "); for (i = 0, j = strlen(buf2); i < len; i += 3, j += 4) base64_encode_atom((unsigned char *)(buf+i), (len-i > 3 ? 3 : len-i), buf2+j); strcpy(buf2+j, "\r\n"); sk_write(p->sub_socket, buf2, strlen(buf2)); sfree(buf); sfree(buf2); } sk_write(p->sub_socket, "\r\n", 2); p->state = 1; return 0; } |
︙ | ︙ | |||
576 577 578 579 580 581 582 | if (change == PROXY_CHANGE_ACCEPTING) { /* we should _never_ see this, as we are using our socket to * connect to a proxy, not accepting inbound connections. * what should we do? close the socket with an appropriate * error message? */ | | > | 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 | if (change == PROXY_CHANGE_ACCEPTING) { /* we should _never_ see this, as we are using our socket to * connect to a proxy, not accepting inbound connections. * what should we do? close the socket with an appropriate * error message? */ return plug_accepting(p->plug, p->accepting_constructor, p->accepting_ctx); } if (change == PROXY_CHANGE_RECEIVE) { /* we have received data from the underlying socket, which * we'll need to parse, process, and respond to appropriately. */ |
︙ | ︙ | |||
707 708 709 710 711 712 713 714 715 716 | * dest. port (2 bytes) [network order] * dest. address (4 bytes) * user ID (variable length, null terminated string) */ int length, type, namelen; char *command, addr[4], hostname[512]; type = sk_addrtype(p->remote_addr); if (type == ADDRTYPE_IPV6) { | > | < > | | | > | 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 | * dest. port (2 bytes) [network order] * dest. address (4 bytes) * user ID (variable length, null terminated string) */ int length, type, namelen; char *command, addr[4], hostname[512]; char *username; type = sk_addrtype(p->remote_addr); if (type == ADDRTYPE_IPV6) { p->error = "Proxy error: SOCKS version 4 does not support IPv6"; return 1; } else if (type == ADDRTYPE_IPV4) { namelen = 0; sk_addrcopy(p->remote_addr, addr); } else { /* type == ADDRTYPE_NAME */ assert(type == ADDRTYPE_NAME); sk_getaddr(p->remote_addr, hostname, lenof(hostname)); namelen = strlen(hostname) + 1; /* include the NUL */ addr[0] = addr[1] = addr[2] = 0; addr[3] = 1; } username = conf_get_str(p->conf, CONF_proxy_username); length = strlen(username) + namelen + 9; command = snewn(length, char); strcpy(command + 8, username); command[0] = 4; /* version 4 */ command[1] = 1; /* CONNECT command */ /* port */ command[2] = (char) (p->remote_port >> 8) & 0xff; command[3] = (char) p->remote_port & 0xff; /* address */ memcpy(command + 4, addr, 4); /* hostname */ memcpy(command + 8 + strlen(username) + 1, hostname, namelen); sk_write(p->sub_socket, command, length); sfree(username); sfree(command); p->state = 1; return 0; } if (change == PROXY_CHANGE_CLOSING) { |
︙ | ︙ | |||
776 777 778 779 780 781 782 | if (change == PROXY_CHANGE_ACCEPTING) { /* we should _never_ see this, as we are using our socket to * connect to a proxy, not accepting inbound connections. * what should we do? close the socket with an appropriate * error message? */ | | > | 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 | if (change == PROXY_CHANGE_ACCEPTING) { /* we should _never_ see this, as we are using our socket to * connect to a proxy, not accepting inbound connections. * what should we do? close the socket with an appropriate * error message? */ return plug_accepting(p->plug, p->accepting_constructor, p->accepting_ctx); } if (change == PROXY_CHANGE_RECEIVE) { /* we have received data from the underlying socket, which * we'll need to parse, process, and respond to appropriately. */ |
︙ | ︙ | |||
864 865 866 867 868 869 870 871 872 873 | * 0x00 = no authentication * 0x01 = GSSAPI * 0x02 = username/password * 0x03 = CHAP */ char command[5]; int len; command[0] = 5; /* version 5 */ | > > > | | 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 | * 0x00 = no authentication * 0x01 = GSSAPI * 0x02 = username/password * 0x03 = CHAP */ char command[5]; char *username, *password; int len; command[0] = 5; /* version 5 */ username = conf_get_str(p->conf, CONF_proxy_username); password = conf_get_str(p->conf, CONF_proxy_password); if (username[0] || password[0]) { command[2] = 0x00; /* no authentication */ len = 3; proxy_socks5_offerencryptedauth (command, &len); command[len++] = 0x02; /* username/password */ command[1] = len - 2; /* Number of methods supported */ } else { command[1] = 1; /* one methods supported: */ |
︙ | ︙ | |||
912 913 914 915 916 917 918 | if (change == PROXY_CHANGE_ACCEPTING) { /* we should _never_ see this, as we are using our socket to * connect to a proxy, not accepting inbound connections. * what should we do? close the socket with an appropriate * error message? */ | | > | 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 | if (change == PROXY_CHANGE_ACCEPTING) { /* we should _never_ see this, as we are using our socket to * connect to a proxy, not accepting inbound connections. * what should we do? close the socket with an appropriate * error message? */ return plug_accepting(p->plug, p->accepting_constructor, p->accepting_ctx); } if (change == PROXY_CHANGE_RECEIVE) { /* we have received data from the underlying socket, which * we'll need to parse, process, and respond to appropriately. */ |
︙ | ︙ | |||
1144 1145 1146 1147 1148 1149 1150 | /* TODO: Handle GSSAPI authentication */ plug_closing(p->plug, "Proxy error: We don't support GSSAPI authentication", PROXY_ERROR_GENERAL, 0); return 1; } if (p->state == 5) { | > > | | | | | | | 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 | /* TODO: Handle GSSAPI authentication */ plug_closing(p->plug, "Proxy error: We don't support GSSAPI authentication", PROXY_ERROR_GENERAL, 0); return 1; } if (p->state == 5) { char *username = conf_get_str(p->conf, CONF_proxy_username); char *password = conf_get_str(p->conf, CONF_proxy_password); if (username[0] || password[0]) { char userpwbuf[255 + 255 + 3]; int ulen, plen; ulen = strlen(username); if (ulen > 255) ulen = 255; if (ulen < 1) ulen = 1; plen = strlen(password); if (plen > 255) plen = 255; if (plen < 1) plen = 1; userpwbuf[0] = 1; /* version number of subnegotiation */ userpwbuf[1] = ulen; memcpy(userpwbuf+2, username, ulen); userpwbuf[ulen+2] = plen; memcpy(userpwbuf+ulen+3, password, plen); sk_write(p->sub_socket, userpwbuf, ulen + plen + 3); p->state = 7; } else plug_closing(p->plug, "Proxy error: Server chose " "username/password authentication but we " "didn't offer it!", PROXY_ERROR_GENERAL, 0); |
︙ | ︙ | |||
1188 1189 1190 1191 1192 1193 1194 | * * (This is for ad-hoc proxies where you connect to the proxy's * telnet port and send a command such as `connect host port'. The * command is configurable, since this proxy type is typically not * standardised or at all well-defined.) */ | | > | < | | | | | | | | 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 | * * (This is for ad-hoc proxies where you connect to the proxy's * telnet port and send a command such as `connect host port'. The * command is configurable, since this proxy type is typically not * standardised or at all well-defined.) */ char *format_telnet_command(SockAddr addr, int port, Conf *conf) { char *fmt = conf_get_str(conf, CONF_proxy_telnet_command); char *ret = NULL; int retlen = 0, retsize = 0; int so = 0, eo = 0; #define ENSURE(n) do { \ if (retsize < retlen + n) { \ retsize = retlen + n + 512; \ ret = sresize(ret, retsize, char); \ } \ } while (0) /* we need to escape \\, \%, \r, \n, \t, \x??, \0???, * %%, %host, %port, %user, and %pass */ while (fmt[eo] != 0) { /* scan forward until we hit end-of-line, * or an escape character (\ or %) */ while (fmt[eo] != 0 && fmt[eo] != '%' && fmt[eo] != '\\') eo++; /* if we hit eol, break out of our escaping loop */ if (fmt[eo] == 0) break; /* if there was any unescaped text before the escape * character, send that now */ if (eo != so) { ENSURE(eo - so); memcpy(ret + retlen, fmt + so, eo - so); retlen += eo - so; } so = eo++; /* if the escape character was the last character of * the line, we'll just stop and send it. */ if (fmt[eo] == 0) break; if (fmt[so] == '\\') { /* we recognize \\, \%, \r, \n, \t, \x??. * anything else, we just send unescaped (including the \). */ switch (fmt[eo]) { case '\\': ENSURE(1); ret[retlen++] = '\\'; eo++; break; |
︙ | ︙ | |||
1276 1277 1278 1279 1280 1281 1282 | { /* escaped hexadecimal value (ie. \xff) */ unsigned char v = 0; int i = 0; for (;;) { eo++; | | < | | < | | < | | 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 | { /* escaped hexadecimal value (ie. \xff) */ unsigned char v = 0; int i = 0; for (;;) { eo++; if (fmt[eo] >= '0' && fmt[eo] <= '9') v += fmt[eo] - '0'; else if (fmt[eo] >= 'a' && fmt[eo] <= 'f') v += fmt[eo] - 'a' + 10; else if (fmt[eo] >= 'A' && fmt[eo] <= 'F') v += fmt[eo] - 'A' + 10; else { /* non hex character, so we abort and just * send the whole thing unescaped (including \x) */ ENSURE(1); ret[retlen++] = '\\'; eo = so + 1; |
︙ | ︙ | |||
1311 1312 1313 1314 1315 1316 1317 | v <<= 4; } } break; default: ENSURE(2); | | | < | < | < | > | | < | > | | < | > | | < | > | | 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 | v <<= 4; } } break; default: ENSURE(2); memcpy(ret+retlen, fmt + so, 2); retlen += 2; eo++; break; } } else { /* % escape. we recognize %%, %host, %port, %user, %pass. * %proxyhost, %proxyport. Anything else we just send * unescaped (including the %). */ if (fmt[eo] == '%') { ENSURE(1); ret[retlen++] = '%'; eo++; } else if (strnicmp(fmt + eo, "host", 4) == 0) { char dest[512]; int destlen; sk_getaddr(addr, dest, lenof(dest)); destlen = strlen(dest); ENSURE(destlen); memcpy(ret+retlen, dest, destlen); retlen += destlen; eo += 4; } else if (strnicmp(fmt + eo, "port", 4) == 0) { char portstr[8], portlen; portlen = sprintf(portstr, "%i", port); ENSURE(portlen); memcpy(ret + retlen, portstr, portlen); retlen += portlen; eo += 4; } else if (strnicmp(fmt + eo, "user", 4) == 0) { char *username = conf_get_str(conf, CONF_proxy_username); int userlen = strlen(username); ENSURE(userlen); memcpy(ret+retlen, username, userlen); retlen += userlen; eo += 4; } else if (strnicmp(fmt + eo, "pass", 4) == 0) { char *password = conf_get_str(conf, CONF_proxy_password); int passlen = strlen(password); ENSURE(passlen); memcpy(ret+retlen, password, passlen); retlen += passlen; eo += 4; } else if (strnicmp(fmt + eo, "proxyhost", 9) == 0) { char *host = conf_get_str(conf, CONF_proxy_host); int phlen = strlen(host); ENSURE(phlen); memcpy(ret+retlen, host, phlen); retlen += phlen; eo += 9; } else if (strnicmp(fmt + eo, "proxyport", 9) == 0) { int port = conf_get_int(conf, CONF_proxy_port); char pport[50]; int pplen; sprintf(pport, "%d", port); pplen = strlen(pport); ENSURE(pplen); memcpy(ret+retlen, pport, pplen); retlen += pplen; eo += 9; } else { |
︙ | ︙ | |||
1400 1401 1402 1403 1404 1405 1406 | /* resume scanning for additional escapes after this one. */ so = eo; } /* if there is any unescaped text at the end of the line, send it */ if (eo != so) { ENSURE(eo - so); | | | | 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 | /* resume scanning for additional escapes after this one. */ so = eo; } /* if there is any unescaped text at the end of the line, send it */ if (eo != so) { ENSURE(eo - so); memcpy(ret + retlen, fmt + so, eo - so); retlen += eo - so; } ENSURE(1); ret[retlen] = '\0'; return ret; #undef ENSURE } int proxy_telnet_negotiate (Proxy_Socket p, int change) { if (p->state == PROXY_CHANGE_NEW) { char *formatted_cmd; formatted_cmd = format_telnet_command(p->remote_addr, p->remote_port, p->conf); sk_write(p->sub_socket, formatted_cmd, strlen(formatted_cmd)); sfree(formatted_cmd); p->state = 1; return 0; } |
︙ | ︙ | |||
1453 1454 1455 1456 1457 1458 1459 | if (change == PROXY_CHANGE_ACCEPTING) { /* we should _never_ see this, as we are using our socket to * connect to a proxy, not accepting inbound connections. * what should we do? close the socket with an appropriate * error message? */ | | > | 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 | if (change == PROXY_CHANGE_ACCEPTING) { /* we should _never_ see this, as we are using our socket to * connect to a proxy, not accepting inbound connections. * what should we do? close the socket with an appropriate * error message? */ return plug_accepting(p->plug, p->accepting_constructor, p->accepting_ctx); } if (change == PROXY_CHANGE_RECEIVE) { /* we have received data from the underlying socket, which * we'll need to parse, process, and respond to appropriately. */ |
︙ | ︙ |
Changes to proxy.h.
︙ | ︙ | |||
26 27 28 29 30 31 32 33 34 35 36 37 38 39 | SockAddr remote_addr; int remote_port; bufchain pending_output_data; bufchain pending_oob_output_data; int pending_flush; bufchain pending_input_data; #define PROXY_STATE_NEW -1 #define PROXY_STATE_ACTIVE 0 int state; /* proxy states greater than 0 are implementation * dependent, but represent various stages/states * of the initialization/setup/negotiation with the | > | 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | SockAddr remote_addr; int remote_port; bufchain pending_output_data; bufchain pending_oob_output_data; int pending_flush; bufchain pending_input_data; int pending_eof; #define PROXY_STATE_NEW -1 #define PROXY_STATE_ACTIVE 0 int state; /* proxy states greater than 0 are implementation * dependent, but represent various stages/states * of the initialization/setup/negotiation with the |
︙ | ︙ | |||
73 74 75 76 77 78 79 | char *receive_data; int receive_len; /* sent */ int sent_bufsize; /* accepting */ | > | | | 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 | char *receive_data; int receive_len; /* sent */ int sent_bufsize; /* accepting */ accept_fn_t accepting_constructor; accept_ctx_t accepting_ctx; /* configuration, used to look up proxy settings */ Conf *conf; /* CHAP transient data */ int chap_num_attributes; int chap_num_attributes_processed; int chap_current_attribute; int chap_current_datalen; }; |
︙ | ︙ | |||
106 107 108 109 110 111 112 | extern int proxy_socks4_negotiate (Proxy_Socket, int); extern int proxy_socks5_negotiate (Proxy_Socket, int); /* * This may be reused by local-command proxies on individual * platforms. */ | | | 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 | extern int proxy_socks4_negotiate (Proxy_Socket, int); extern int proxy_socks5_negotiate (Proxy_Socket, int); /* * This may be reused by local-command proxies on individual * platforms. */ char *format_telnet_command(SockAddr addr, int port, Conf *conf); /* * These are implemented in cproxy.c or nocproxy.c, depending on * whether encrypted proxy authentication is available. */ extern void proxy_socks5_offerencryptedauth(char *command, int *len); extern int proxy_socks5_handlechap (Proxy_Socket p); extern int proxy_socks5_selectchap(Proxy_Socket p); #endif |
Changes to pscp.c.
︙ | ︙ | |||
37 38 39 40 41 42 43 44 45 46 | static int scp_unsafe_mode = 0; static int errs = 0; static int try_scp = 1; static int try_sftp = 1; static int main_cmd_is_sftp = 0; static int fallback_cmd_is_sftp = 0; static int using_sftp = 0; static Backend *back; static void *backhandle; | > | > | 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 | static int scp_unsafe_mode = 0; static int errs = 0; static int try_scp = 1; static int try_sftp = 1; static int main_cmd_is_sftp = 0; static int fallback_cmd_is_sftp = 0; static int using_sftp = 0; static int uploading = 0; static Backend *back; static void *backhandle; static Conf *conf; int sent_eof = FALSE; static void source(char *src); static void rsource(char *src); static void sink(char *targ, char *src); const char *const appname = "PSCP"; |
︙ | ︙ | |||
123 124 125 126 127 128 129 130 131 132 133 134 135 136 | sfree(str); va_end(ap); tell_str(stderr, str2); sfree(str2); errs++; cleanup_exit(1); } void connection_fatal(void *frontend, char *fmt, ...) { char *str, *str2; va_list ap; va_start(ap, fmt); str = dupvprintf(fmt, ap); | > > > > > > > > > > > > > | 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 | sfree(str); va_end(ap); tell_str(stderr, str2); sfree(str2); errs++; cleanup_exit(1); } void nonfatal(char *fmt, ...) { char *str, *str2; va_list ap; va_start(ap, fmt); str = dupvprintf(fmt, ap); str2 = dupcat("Error: ", str, "\n", NULL); sfree(str); va_end(ap); tell_str(stderr, str2); sfree(str2); errs++; } void connection_fatal(void *frontend, char *fmt, ...) { char *str, *str2; va_list ap; va_start(ap, fmt); str = dupvprintf(fmt, ap); |
︙ | ︙ | |||
209 210 211 212 213 214 215 216 217 218 219 220 221 222 | { /* * No "untrusted" output should get here (the way the code is * currently, it's all diverted by FLAG_STDERR). */ assert(!"Unexpected call to from_backend_untrusted()"); return 0; /* not reached */ } static int ssh_scp_recv(unsigned char *buf, int len) { outptr = buf; outlen = len; /* | > > > > > > > > > > > > > > | 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 | { /* * No "untrusted" output should get here (the way the code is * currently, it's all diverted by FLAG_STDERR). */ assert(!"Unexpected call to from_backend_untrusted()"); return 0; /* not reached */ } int from_backend_eof(void *frontend) { /* * We usually expect to be the party deciding when to close the * connection, so if we see EOF before we sent it ourselves, we * should panic. The exception is if we're using old-style scp and * downloading rather than uploading. */ if ((using_sftp || uploading) && !sent_eof) { connection_fatal(frontend, "Received unexpected end-of-file from server"); } return FALSE; } static int ssh_scp_recv(unsigned char *buf, int len) { outptr = buf; outlen = len; /* |
︙ | ︙ | |||
294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 | tell_str(stderr, str2); sfree(str2); errs++; if (back != NULL && back->connected(backhandle)) { char ch; back->special(backhandle, TS_EOF); ssh_scp_recv((unsigned char *) &ch, 1); } cleanup_exit(1); } /* * Open an SSH connection to user@host and execute cmd. */ static void do_cmd(char *host, char *user, char *cmd) { const char *err; | > > > > > > > > > > > > > > > > > > > > > > > > | 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 | tell_str(stderr, str2); sfree(str2); errs++; if (back != NULL && back->connected(backhandle)) { char ch; back->special(backhandle, TS_EOF); sent_eof = TRUE; ssh_scp_recv((unsigned char *) &ch, 1); } cleanup_exit(1); } /* * Wait for the reply to a single SFTP request. Parallels the same * function in psftp.c (but isn't centralised into sftp.c because the * latter module handles SFTP only and shouldn't assume that SFTP is * the only thing going on by calling connection_fatal). */ struct sftp_packet *sftp_wait_for_reply(struct sftp_request *req) { struct sftp_packet *pktin; struct sftp_request *rreq; sftp_register(req); pktin = sftp_recv(); if (pktin == NULL) connection_fatal(NULL, "did not receive SFTP response packet " "from server"); rreq = sftp_find_request(pktin); if (rreq != req) connection_fatal(NULL, "unable to understand SFTP response packet " "from server: %s", fxp_error()); return pktin; } /* * Open an SSH connection to user@host and execute cmd. */ static void do_cmd(char *host, char *user, char *cmd) { const char *err; |
︙ | ︙ | |||
329 330 331 332 333 334 335 | /* * If we haven't loaded session details already (e.g., from -load), * try looking for a session called "host". */ if (!loaded_session) { /* Try to load settings for `host' into a temporary config */ | | | | | | | < | < | | | | | | | > | > > > > > | > > | | < | | | < < | | | | | | < > | | | | | | | | > | | < | | < | | < | > > > > > | | | | | | | > | > | | | | | | > > > | | | | 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 | /* * If we haven't loaded session details already (e.g., from -load), * try looking for a session called "host". */ if (!loaded_session) { /* Try to load settings for `host' into a temporary config */ Conf *conf2 = conf_new(); conf_set_str(conf2, CONF_host, ""); do_defaults(host, conf2); if (conf_get_str(conf2, CONF_host)[0] != '\0') { /* Settings present and include hostname */ /* Re-load data into the real config. */ do_defaults(host, conf); } else { /* Session doesn't exist or mention a hostname. */ /* Use `host' as a bare hostname. */ conf_set_str(conf, CONF_host, host); } } else { /* Patch in hostname `host' to session details. */ conf_set_str(conf, CONF_host, host); } /* * Force use of SSH. (If they got the protocol wrong we assume the * port is useless too.) */ if (conf_get_int(conf, CONF_protocol) != PROT_SSH) { conf_set_int(conf, CONF_protocol, PROT_SSH); conf_set_int(conf, CONF_port, 22); } /* * Enact command-line overrides. */ cmdline_run_saved(conf); /* * Muck about with the hostname in various ways. */ { char *hostbuf = dupstr(conf_get_str(conf, CONF_host)); char *host = hostbuf; char *p, *q; /* * Trim leading whitespace. */ host += strspn(host, " \t"); /* * See if host is of the form user@host, and separate out * the username if so. */ if (host[0] != '\0') { char *atsign = strrchr(host, '@'); if (atsign) { *atsign = '\0'; conf_set_str(conf, CONF_username, host); host = atsign + 1; } } /* * Remove any remaining whitespace. */ p = hostbuf; q = host; while (*q) { if (*q != ' ' && *q != '\t') *p++ = *q; q++; } *p = '\0'; conf_set_str(conf, CONF_host, hostbuf); sfree(hostbuf); } /* Set username */ if (user != NULL && user[0] != '\0') { conf_set_str(conf, CONF_username, user); } else if (conf_get_str(conf, CONF_username)[0] == '\0') { user = get_username(); if (!user) bump("Empty user name"); else { if (verbose) tell_user(stderr, "Guessing user name: %s", user); conf_set_str(conf, CONF_username, user); sfree(user); } } /* * Disable scary things which shouldn't be enabled for simple * things like SCP and SFTP: agent forwarding, port forwarding, * X forwarding. */ conf_set_int(conf, CONF_x11_forward, 0); conf_set_int(conf, CONF_agentfwd, 0); conf_set_int(conf, CONF_ssh_simple, TRUE); { char *key; while ((key = conf_get_str_nthstrkey(conf, CONF_portfwd, 0)) != NULL) conf_del_str_str(conf, CONF_portfwd, key); } /* * Set up main and possibly fallback command depending on * options specified by user. * Attempt to start the SFTP subsystem as a first choice, * falling back to the provided scp command if that fails. */ conf_set_str(conf, CONF_remote_cmd2, ""); if (try_sftp) { /* First choice is SFTP subsystem. */ main_cmd_is_sftp = 1; conf_set_str(conf, CONF_remote_cmd, "sftp"); conf_set_int(conf, CONF_ssh_subsys, TRUE); if (try_scp) { /* Fallback is to use the provided scp command. */ fallback_cmd_is_sftp = 0; conf_set_str(conf, CONF_remote_cmd2, cmd); conf_set_int(conf, CONF_ssh_subsys2, FALSE); } else { /* Since we're not going to try SCP, we may as well try * harder to find an SFTP server, since in the current * implementation we have a spare slot. */ fallback_cmd_is_sftp = 1; /* see psftp.c for full explanation of this kludge */ conf_set_str(conf, CONF_remote_cmd2, "test -x /usr/lib/sftp-server &&" " exec /usr/lib/sftp-server\n" "test -x /usr/local/lib/sftp-server &&" " exec /usr/local/lib/sftp-server\n" "exec sftp-server"); conf_set_int(conf, CONF_ssh_subsys2, FALSE); } } else { /* Don't try SFTP at all; just try the scp command. */ main_cmd_is_sftp = 0; conf_set_str(conf, CONF_remote_cmd, cmd); conf_set_int(conf, CONF_ssh_subsys, FALSE); } conf_set_int(conf, CONF_nopty, TRUE); back = &ssh_backend; err = back->init(NULL, &backhandle, conf, conf_get_str(conf, CONF_host), conf_get_int(conf, CONF_port), &realhost, 0, conf_get_int(conf, CONF_tcp_keepalives)); if (err != NULL) bump("ssh_init: %s", err); logctx = log_init(NULL, conf); back->provide_logctx(backhandle, logctx); console_provide_logctx(logctx); ssh_scp_init(); if (verbose && realhost != NULL && errs == 0) tell_user(stderr, "Connected to %s", realhost); sfree(realhost); } /* * Update statistic information about current file. */ static void print_stats(char *name, uint64 size, uint64 done, |
︙ | ︙ | |||
622 623 624 625 626 627 628 | do { if (ssh_scp_recv((unsigned char *) &ch, 1) <= 0) bump("Protocol error: Lost connection"); rbuf[p++] = ch; } while (p < sizeof(rbuf) && ch != '\n'); rbuf[p - 1] = '\0'; if (resp == 1) | | | 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 | do { if (ssh_scp_recv((unsigned char *) &ch, 1) <= 0) bump("Protocol error: Lost connection"); rbuf[p++] = ch; } while (p < sizeof(rbuf) && ch != '\n'); rbuf[p - 1] = '\0'; if (resp == 1) tell_user(stderr, "%s", rbuf); else bump("%s", rbuf); errs++; return (-1); } } |
︙ | ︙ | |||
655 656 657 658 659 660 661 | } void scp_sftp_listdir(char *dirname) { struct fxp_handle *dirh; struct fxp_names *names; struct fxp_name *ournames; struct sftp_packet *pktin; | | | | < | | | < | | 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 | } void scp_sftp_listdir(char *dirname) { struct fxp_handle *dirh; struct fxp_names *names; struct fxp_name *ournames; struct sftp_packet *pktin; struct sftp_request *req; int nnames, namesize; int i; if (!fxp_init()) { tell_user(stderr, "unable to initialise SFTP: %s", fxp_error()); errs++; return; } printf("Listing directory %s\n", dirname); req = fxp_opendir_send(dirname); pktin = sftp_wait_for_reply(req); dirh = fxp_opendir_recv(pktin, req); if (dirh == NULL) { printf("Unable to open %s: %s\n", dirname, fxp_error()); } else { nnames = namesize = 0; ournames = NULL; while (1) { req = fxp_readdir_send(dirh); pktin = sftp_wait_for_reply(req); names = fxp_readdir_recv(pktin, req); if (names == NULL) { if (fxp_error_type() == SSH_FX_EOF) break; printf("Reading directory %s: %s\n", dirname, fxp_error()); break; } |
︙ | ︙ | |||
706 707 708 709 710 711 712 | } for (i = 0; i < names->nnames; i++) ournames[nnames++] = names->names[i]; names->nnames = 0; /* prevent free_names */ fxp_free_names(names); } | | | < | > | > > | 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 | } for (i = 0; i < names->nnames; i++) ournames[nnames++] = names->names[i]; names->nnames = 0; /* prevent free_names */ fxp_free_names(names); } req = fxp_close_send(dirh); pktin = sftp_wait_for_reply(req); fxp_close_recv(pktin, req); /* * Now we have our filenames. Sort them by actual file * name, and then output the longname parts. */ if (nnames > 0) qsort(ournames, nnames, sizeof(*ournames), sftp_ls_compare); /* * And print them. */ for (i = 0; i < nnames; i++) printf("%s\n", ournames[i].longname); sfree(ournames); } } /* ---------------------------------------------------------------------- * Helper routines that contain the actual SCP protocol elements, * implemented both as SCP1 and SFTP. */ |
︙ | ︙ | |||
756 757 758 759 760 761 762 | { if (using_sftp) { /* * Find out whether the target filespec is in fact a * directory. */ struct sftp_packet *pktin; | | | | < | | 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 | { if (using_sftp) { /* * Find out whether the target filespec is in fact a * directory. */ struct sftp_packet *pktin; struct sftp_request *req; struct fxp_attrs attrs; int ret; if (!fxp_init()) { tell_user(stderr, "unable to initialise SFTP: %s", fxp_error()); errs++; return 1; } req = fxp_stat_send(target); pktin = sftp_wait_for_reply(req); ret = fxp_stat_recv(pktin, req, &attrs); if (!ret || !(attrs.flags & SSH_FILEXFER_ATTR_PERMISSIONS)) scp_sftp_targetisdir = 0; else scp_sftp_targetisdir = (attrs.permissions & 0040000) != 0; if (shouldbedir && !scp_sftp_targetisdir) { |
︙ | ︙ | |||
815 816 817 818 819 820 821 | char buf[80]; sprintf(buf, "T%lu 0 %lu 0\n", mtime, atime); back->send(backhandle, buf, strlen(buf)); return response(); } } | | | > > > > | | > | < | > > > | | | > > | 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 | char buf[80]; sprintf(buf, "T%lu 0 %lu 0\n", mtime, atime); back->send(backhandle, buf, strlen(buf)); return response(); } } int scp_send_filename(char *name, uint64 size, int permissions) { if (using_sftp) { char *fullname; struct sftp_packet *pktin; struct sftp_request *req; struct fxp_attrs attrs; if (scp_sftp_targetisdir) { fullname = dupcat(scp_sftp_remotepath, "/", name, NULL); } else { fullname = dupstr(scp_sftp_remotepath); } attrs.flags = 0; PUT_PERMISSIONS(attrs, permissions); req = fxp_open_send(fullname, SSH_FXF_WRITE | SSH_FXF_CREAT | SSH_FXF_TRUNC, &attrs); pktin = sftp_wait_for_reply(req); scp_sftp_filehandle = fxp_open_recv(pktin, req); if (!scp_sftp_filehandle) { tell_user(stderr, "pscp: unable to open %s: %s", fullname, fxp_error()); sfree(fullname); errs++; return 1; } scp_sftp_fileoffset = uint64_make(0, 0); scp_sftp_xfer = xfer_upload_init(scp_sftp_filehandle, scp_sftp_fileoffset); sfree(fullname); return 0; } else { char buf[40]; char sizestr[40]; uint64_decimal(size, sizestr); if (permissions < 0) permissions = 0644; sprintf(buf, "C%04o %s ", (int)(permissions & 07777), sizestr); back->send(backhandle, buf, strlen(buf)); back->send(backhandle, name, strlen(name)); back->send(backhandle, "\n", 1); return response(); } } int scp_send_filedata(char *data, int len) { if (using_sftp) { int ret; struct sftp_packet *pktin; if (!scp_sftp_filehandle) { return 1; } while (!xfer_upload_ready(scp_sftp_xfer)) { pktin = sftp_recv(); ret = xfer_upload_gotpkt(scp_sftp_xfer, pktin); if (ret <= 0) { tell_user(stderr, "error while writing: %s", fxp_error()); if (ret == INT_MIN) /* pktin not even freed */ sfree(pktin); errs++; return 1; } } xfer_upload_data(scp_sftp_xfer, data, len); |
︙ | ︙ | |||
905 906 907 908 909 910 911 | } int scp_send_finish(void) { if (using_sftp) { struct fxp_attrs attrs; struct sftp_packet *pktin; | | | > > > > > > > | | < | | | | < | | 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 | } int scp_send_finish(void) { if (using_sftp) { struct fxp_attrs attrs; struct sftp_packet *pktin; struct sftp_request *req; int ret; while (!xfer_done(scp_sftp_xfer)) { pktin = sftp_recv(); ret = xfer_upload_gotpkt(scp_sftp_xfer, pktin); if (ret <= 0) { tell_user(stderr, "error while writing: %s", fxp_error()); if (ret == INT_MIN) /* pktin not even freed */ sfree(pktin); errs++; return 1; } } xfer_cleanup(scp_sftp_xfer); if (!scp_sftp_filehandle) { return 1; } if (scp_has_times) { attrs.flags = SSH_FILEXFER_ATTR_ACMODTIME; attrs.atime = scp_sftp_atime; attrs.mtime = scp_sftp_mtime; req = fxp_fsetstat_send(scp_sftp_filehandle, attrs); pktin = sftp_wait_for_reply(req); ret = fxp_fsetstat_recv(pktin, req); if (!ret) { tell_user(stderr, "unable to set file times: %s", fxp_error()); errs++; } } req = fxp_close_send(scp_sftp_filehandle); pktin = sftp_wait_for_reply(req); fxp_close_recv(pktin, req); scp_has_times = 0; return 0; } else { back->send(backhandle, "", 1); return response(); } } |
︙ | ︙ | |||
963 964 965 966 967 968 969 | int scp_send_dirname(char *name, int modes) { if (using_sftp) { char *fullname; char const *err; struct fxp_attrs attrs; struct sftp_packet *pktin; | | | | < | | | < | > | 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 | int scp_send_dirname(char *name, int modes) { if (using_sftp) { char *fullname; char const *err; struct fxp_attrs attrs; struct sftp_packet *pktin; struct sftp_request *req; int ret; if (scp_sftp_targetisdir) { fullname = dupcat(scp_sftp_remotepath, "/", name, NULL); } else { fullname = dupstr(scp_sftp_remotepath); } /* * We don't worry about whether we managed to create the * directory, because if it exists already it's OK just to * use it. Instead, we will stat it afterwards, and if it * exists and is a directory we will assume we were either * successful or it didn't matter. */ req = fxp_mkdir_send(fullname); pktin = sftp_wait_for_reply(req); ret = fxp_mkdir_recv(pktin, req); if (!ret) err = fxp_error(); else err = "server reported no error"; req = fxp_stat_send(fullname); pktin = sftp_wait_for_reply(req); ret = fxp_stat_recv(pktin, req, &attrs); if (!ret || !(attrs.flags & SSH_FILEXFER_ATTR_PERMISSIONS) || !(attrs.permissions & 0040000)) { tell_user(stderr, "unable to create directory %s: %s", fullname, err); sfree(fullname); errs++; return 1; } scp_sftp_remotepath = fullname; return 0; |
︙ | ︙ | |||
1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 | * wildcardness comes before the final slash) and arrange * things so that a dirstack entry will be set up. */ newsource = snewn(1+strlen(source), char); if (!wc_unescape(newsource, source)) { /* Yes, here we go; it's a wildcard. Bah. */ char *dupsource, *lastpart, *dirpart, *wildcard; dupsource = dupstr(source); lastpart = stripslashes(dupsource, 0); wildcard = dupstr(lastpart); *lastpart = '\0'; if (*dupsource && dupsource[1]) { /* * The remains of dupsource are at least two | > > > | 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 | * wildcardness comes before the final slash) and arrange * things so that a dirstack entry will be set up. */ newsource = snewn(1+strlen(source), char); if (!wc_unescape(newsource, source)) { /* Yes, here we go; it's a wildcard. Bah. */ char *dupsource, *lastpart, *dirpart, *wildcard; sfree(newsource); dupsource = dupstr(source); lastpart = stripslashes(dupsource, 0); wildcard = dupstr(lastpart); *lastpart = '\0'; if (*dupsource && dupsource[1]) { /* * The remains of dupsource are at least two |
︙ | ︙ | |||
1129 1130 1131 1132 1133 1134 1135 | #define SCP_SINK_DIR 2 #define SCP_SINK_ENDDIR 3 #define SCP_SINK_RETRY 4 /* not an action; just try again */ struct scp_sink_action { int action; /* FILE, DIR, ENDDIR */ char *buf; /* will need freeing after use */ char *name; /* filename or dirname (not ENDDIR) */ | | | | 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 | #define SCP_SINK_DIR 2 #define SCP_SINK_ENDDIR 3 #define SCP_SINK_RETRY 4 /* not an action; just try again */ struct scp_sink_action { int action; /* FILE, DIR, ENDDIR */ char *buf; /* will need freeing after use */ char *name; /* filename or dirname (not ENDDIR) */ long permissions; /* access permissions (not ENDDIR) */ uint64 size; /* file size (not ENDDIR) */ int settime; /* 1 if atime and mtime are filled */ unsigned long atime, mtime; /* access times for the file */ }; int scp_get_sink_action(struct scp_sink_action *act) { if (using_sftp) { char *fname; int must_free_fname; struct fxp_attrs attrs; struct sftp_packet *pktin; struct sftp_request *req; int ret; if (!scp_sftp_dirstack_head) { if (!scp_sftp_donethistarget) { /* * Simple case: we are only dealing with one file. */ |
︙ | ︙ | |||
1211 1212 1213 1214 1215 1216 1217 | } } /* * Now we have a filename. Stat it, and see if it's a file * or a directory. */ | | | < | > | 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 | } } /* * Now we have a filename. Stat it, and see if it's a file * or a directory. */ req = fxp_stat_send(fname); pktin = sftp_wait_for_reply(req); ret = fxp_stat_recv(pktin, req, &attrs); if (!ret || !(attrs.flags & SSH_FILEXFER_ATTR_PERMISSIONS)) { tell_user(stderr, "unable to identify %s: %s", fname, ret ? "file type not supplied" : fxp_error()); if (must_free_fname) sfree(fname); errs++; return 1; } if (attrs.permissions & 0040000) { struct scp_sftp_dirstack *newitem; struct fxp_handle *dirhandle; |
︙ | ︙ | |||
1267 1268 1269 1270 1271 1272 1273 | * matching them against a wildcard if present. * * If targetisdir is _already_ set (meaning we're * already in the middle of going through another such * list), we must push the other (target,namelist) pair * on a stack. */ | | | < | | | | < | | > > > > > | 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 | * matching them against a wildcard if present. * * If targetisdir is _already_ set (meaning we're * already in the middle of going through another such * list), we must push the other (target,namelist) pair * on a stack. */ req = fxp_opendir_send(fname); pktin = sftp_wait_for_reply(req); dirhandle = fxp_opendir_recv(pktin, req); if (!dirhandle) { tell_user(stderr, "pscp: unable to open directory %s: %s", fname, fxp_error()); if (must_free_fname) sfree(fname); errs++; return 1; } nnames = namesize = 0; ournames = NULL; while (1) { int i; req = fxp_readdir_send(dirhandle); pktin = sftp_wait_for_reply(req); names = fxp_readdir_recv(pktin, req); if (names == NULL) { if (fxp_error_type() == SSH_FX_EOF) break; tell_user(stderr, "pscp: reading directory %s: %s", fname, fxp_error()); req = fxp_close_send(dirhandle); pktin = sftp_wait_for_reply(req); fxp_close_recv(pktin, req); if (must_free_fname) sfree(fname); sfree(ournames); errs++; return 1; } if (names->nnames == 0) { fxp_free_names(names); |
︙ | ︙ | |||
1317 1318 1319 1320 1321 1322 1323 | /* * . and .. are normal consequences of * reading a directory, and aren't worth * complaining about. */ } else if (!vet_filename(names->names[i].filename)) { tell_user(stderr, "ignoring potentially dangerous server-" | | | | < | | 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 | /* * . and .. are normal consequences of * reading a directory, and aren't worth * complaining about. */ } else if (!vet_filename(names->names[i].filename)) { tell_user(stderr, "ignoring potentially dangerous server-" "supplied filename '%s'", names->names[i].filename); } else ournames[nnames++] = names->names[i]; } names->nnames = 0; /* prevent free_names */ fxp_free_names(names); } req = fxp_close_send(dirhandle); pktin = sftp_wait_for_reply(req); fxp_close_recv(pktin, req); newitem = snew(struct scp_sftp_dirstack); newitem->next = scp_sftp_dirstack_head; newitem->names = ournames; newitem->namepos = 0; newitem->namelen = nnames; if (must_free_fname) |
︙ | ︙ | |||
1355 1356 1357 1358 1359 1360 1361 | if (newitem->wildcard) { act->action = SCP_SINK_RETRY; } else { act->action = SCP_SINK_DIR; act->buf = dupstr(stripslashes(fname, 0)); act->name = act->buf; act->size = uint64_make(0,0); /* duhh, it's a directory */ | | | 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 | if (newitem->wildcard) { act->action = SCP_SINK_RETRY; } else { act->action = SCP_SINK_DIR; act->buf = dupstr(stripslashes(fname, 0)); act->name = act->buf; act->size = uint64_make(0,0); /* duhh, it's a directory */ act->permissions = 07777 & attrs.permissions; if (scp_sftp_preserve && (attrs.flags & SSH_FILEXFER_ATTR_ACMODTIME)) { act->atime = attrs.atime; act->mtime = attrs.mtime; act->settime = 1; } else act->settime = 0; |
︙ | ︙ | |||
1377 1378 1379 1380 1381 1382 1383 | act->action = SCP_SINK_FILE; act->buf = dupstr(stripslashes(fname, 0)); act->name = act->buf; if (attrs.flags & SSH_FILEXFER_ATTR_SIZE) { act->size = attrs.size; } else act->size = uint64_make(ULONG_MAX,ULONG_MAX); /* no idea */ | | | 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 | act->action = SCP_SINK_FILE; act->buf = dupstr(stripslashes(fname, 0)); act->name = act->buf; if (attrs.flags & SSH_FILEXFER_ATTR_SIZE) { act->size = attrs.size; } else act->size = uint64_make(ULONG_MAX,ULONG_MAX); /* no idea */ act->permissions = 07777 & attrs.permissions; if (scp_sftp_preserve && (attrs.flags & SSH_FILEXFER_ATTR_ACMODTIME)) { act->atime = attrs.atime; act->mtime = attrs.mtime; act->settime = 1; } else act->settime = 0; |
︙ | ︙ | |||
1421 1422 1423 1424 1425 1426 1427 | act->buf = sresize(act->buf, bufsize, char); } act->buf[i++] = ch; } while (ch != '\n'); act->buf[i - 1] = '\0'; switch (action) { case '\01': /* error */ | | | 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 | act->buf = sresize(act->buf, bufsize, char); } act->buf[i++] = ch; } while (ch != '\n'); act->buf[i - 1] = '\0'; switch (action) { case '\01': /* error */ tell_user(stderr, "%s", act->buf); errs++; continue; /* go round again */ case '\02': /* fatal error */ bump("%s", act->buf); case 'E': back->send(backhandle, "", 1); act->action = SCP_SINK_ENDDIR; |
︙ | ︙ | |||
1459 1460 1461 1462 1463 1464 1465 | /* * If we get here, we must have seen SCP_SINK_FILE or * SCP_SINK_DIR. */ { char sizestr[40]; | > | | | | < | | 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 | /* * If we get here, we must have seen SCP_SINK_FILE or * SCP_SINK_DIR. */ { char sizestr[40]; if (sscanf(act->buf, "%lo %s %n", &act->permissions, sizestr, &i) != 2) bump("Protocol error: Illegal file descriptor format"); act->size = uint64_from_decimal(sizestr); act->name = act->buf + i; return 0; } } } int scp_accept_filexfer(void) { if (using_sftp) { struct sftp_packet *pktin; struct sftp_request *req; req = fxp_open_send(scp_sftp_currentname, SSH_FXF_READ, NULL); pktin = sftp_wait_for_reply(req); scp_sftp_filehandle = fxp_open_recv(pktin, req); if (!scp_sftp_filehandle) { tell_user(stderr, "pscp: unable to open %s: %s", scp_sftp_currentname, fxp_error()); errs++; return 1; } |
︙ | ︙ | |||
1506 1507 1508 1509 1510 1511 1512 | struct sftp_packet *pktin; int ret, actuallen; void *vbuf; xfer_download_queue(scp_sftp_xfer); pktin = sftp_recv(); ret = xfer_download_gotpkt(scp_sftp_xfer, pktin); | < | > > | 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 | struct sftp_packet *pktin; int ret, actuallen; void *vbuf; xfer_download_queue(scp_sftp_xfer); pktin = sftp_recv(); ret = xfer_download_gotpkt(scp_sftp_xfer, pktin); if (ret <= 0) { tell_user(stderr, "pscp: error while reading: %s", fxp_error()); if (ret == INT_MIN) /* pktin not even freed */ sfree(pktin); errs++; return -1; } if (xfer_download_data(scp_sftp_xfer, &vbuf, &actuallen)) { /* * This assertion relies on the fact that the natural |
︙ | ︙ | |||
1538 1539 1540 1541 1542 1543 1544 | } } int scp_finish_filerecv(void) { if (using_sftp) { struct sftp_packet *pktin; | | | | > > > > > > > | | < | | > | 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 | } } int scp_finish_filerecv(void) { if (using_sftp) { struct sftp_packet *pktin; struct sftp_request *req; /* * Ensure that xfer_done() will work correctly, so we can * clean up any outstanding requests from the file * transfer. */ xfer_set_error(scp_sftp_xfer); while (!xfer_done(scp_sftp_xfer)) { void *vbuf; int ret, len; pktin = sftp_recv(); ret = xfer_download_gotpkt(scp_sftp_xfer, pktin); if (ret <= 0) { tell_user(stderr, "pscp: error while reading: %s", fxp_error()); if (ret == INT_MIN) /* pktin not even freed */ sfree(pktin); errs++; return -1; } if (xfer_download_data(scp_sftp_xfer, &vbuf, &len)) sfree(vbuf); } xfer_cleanup(scp_sftp_xfer); req = fxp_close_send(scp_sftp_filehandle); pktin = sftp_wait_for_reply(req); fxp_close_recv(pktin, req); return 0; } else { back->send(backhandle, "", 1); return response(); } } /* ---------------------------------------------------------------------- * Send an error message to the other side and to the screen. * Increment error counter. */ static void run_err(const char *fmt, ...) { char *str, *str2; va_list ap; va_start(ap, fmt); errs++; str = dupvprintf(fmt, ap); str2 = dupcat("pscp: ", str, "\n", NULL); sfree(str); scp_send_errmsg(str2); tell_user(stderr, "%s", str2); va_end(ap); sfree(str2); } /* * Execute the source part of the SCP protocol. */ static void source(char *src) { uint64 size; unsigned long mtime, atime; long permissions; char *last; RFile *f; int attr; uint64 i; uint64 stat_bytes; time_t stat_starttime, stat_lasttime; |
︙ | ︙ | |||
1641 1642 1643 1644 1645 1646 1647 | else last++; if (strrchr(last, '\\') != NULL) last = strrchr(last, '\\') + 1; if (last == src && strchr(src, ':') != NULL) last = strchr(src, ':') + 1; | | | > > | > > | 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 | else last++; if (strrchr(last, '\\') != NULL) last = strrchr(last, '\\') + 1; if (last == src && strchr(src, ':') != NULL) last = strchr(src, ':') + 1; f = open_existing_file(src, &size, &mtime, &atime, &permissions); if (f == NULL) { run_err("%s: Cannot open file", src); return; } if (preserve) { if (scp_send_filetimes(mtime, atime)) { close_rfile(f); return; } } if (verbose) { char sizestr[40]; uint64_decimal(size, sizestr); tell_user(stderr, "Sending file %s, size=%s", last, sizestr); } if (scp_send_filename(last, size, permissions)) { close_rfile(f); return; } stat_bytes = uint64_make(0,0); stat_starttime = time(NULL); stat_lasttime = 0; for (i = uint64_make(0,0); uint64_compare(i,size) < 0; |
︙ | ︙ | |||
1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 | } attr = file_type(destfname); exists = (attr != FILE_TYPE_NONEXISTENT); if (act.action == SCP_SINK_DIR) { if (exists && attr != FILE_TYPE_DIRECTORY) { run_err("%s: Not a directory", destfname); continue; } if (!exists) { if (!create_directory(destfname)) { run_err("%s: Cannot create directory", destfname); continue; } } sink(destfname, NULL); /* can we set the timestamp for directories ? */ continue; } | > > > | > | > > > | 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 | } attr = file_type(destfname); exists = (attr != FILE_TYPE_NONEXISTENT); if (act.action == SCP_SINK_DIR) { if (exists && attr != FILE_TYPE_DIRECTORY) { run_err("%s: Not a directory", destfname); sfree(destfname); continue; } if (!exists) { if (!create_directory(destfname)) { run_err("%s: Cannot create directory", destfname); sfree(destfname); continue; } } sink(destfname, NULL); /* can we set the timestamp for directories ? */ sfree(destfname); continue; } f = open_new_file(destfname, act.permissions); if (f == NULL) { run_err("%s: Cannot create file", destfname); sfree(destfname); continue; } if (scp_accept_filexfer()) { sfree(destfname); close_wfile(f); return; } stat_bytes = uint64_make(0, 0); stat_starttime = time(NULL); stat_lasttime = 0; stat_name = stripslashes(destfname, 1); received = uint64_make(0, 0); |
︙ | ︙ | |||
1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 | if (act.settime) { set_file_times(f, act.mtime, act.atime); } close_wfile(f); if (wrerror) { run_err("%s: Write error", destfname); continue; } (void) scp_finish_filerecv(); sfree(destfname); sfree(act.buf); } } /* * We will copy local files to a remote server. */ static void toremote(int argc, char *argv[]) { char *src, *targ, *host, *user; char *cmd; int i, wc_type; targ = argv[argc - 1]; /* Separate host from filename */ host = targ; targ = colon(targ); if (targ == NULL) | > > > | 2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 | if (act.settime) { set_file_times(f, act.mtime, act.atime); } close_wfile(f); if (wrerror) { run_err("%s: Write error", destfname); sfree(destfname); continue; } (void) scp_finish_filerecv(); sfree(destfname); sfree(act.buf); } } /* * We will copy local files to a remote server. */ static void toremote(int argc, char *argv[]) { char *src, *targ, *host, *user; char *cmd; int i, wc_type; uploading = 1; targ = argv[argc - 1]; /* Separate host from filename */ host = targ; targ = colon(targ); if (targ == NULL) |
︙ | ︙ | |||
2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 | /* * We will copy files from a remote server to the local machine. */ static void tolocal(int argc, char *argv[]) { char *src, *targ, *host, *user; char *cmd; if (argc != 2) bump("More than one remote source not supported"); src = argv[0]; targ = argv[1]; | > > | 2135 2136 2137 2138 2139 2140 2141 2142 2143 2144 2145 2146 2147 2148 2149 2150 | /* * We will copy files from a remote server to the local machine. */ static void tolocal(int argc, char *argv[]) { char *src, *targ, *host, *user; char *cmd; uploading = 0; if (argc != 2) bump("More than one remote source not supported"); src = argv[0]; targ = argv[1]; |
︙ | ︙ | |||
2198 2199 2200 2201 2202 2203 2204 2205 2206 2207 2208 2209 2210 2211 2212 2213 2214 2215 2216 2217 2218 2219 2220 2221 2222 2223 | va_start(ap, p); vfprintf(stderr, p, ap); va_end(ap); fprintf(stderr, "\n try typing just \"pscp\" for help\n"); exit(1); } /* * Main program. (Called `psftp_main' because it gets called from * *sftp.c; bit silly, I know, but it had to be called _something_.) */ int psftp_main(int argc, char *argv[]) { int i; default_protocol = PROT_TELNET; flags = FLAG_STDERR #ifdef FLAG_SYNCAGENT | FLAG_SYNCAGENT #endif ; cmdline_tooltype = TOOLTYPE_FILETRANSFER; sk_init(); /* Load Default Settings before doing anything else. */ | > > > > | | | > > | > | 2303 2304 2305 2306 2307 2308 2309 2310 2311 2312 2313 2314 2315 2316 2317 2318 2319 2320 2321 2322 2323 2324 2325 2326 2327 2328 2329 2330 2331 2332 2333 2334 2335 2336 2337 2338 2339 2340 2341 2342 2343 2344 2345 2346 2347 2348 2349 2350 2351 2352 2353 2354 2355 2356 2357 2358 2359 2360 2361 2362 2363 2364 2365 2366 2367 2368 2369 2370 | va_start(ap, p); vfprintf(stderr, p, ap); va_end(ap); fprintf(stderr, "\n try typing just \"pscp\" for help\n"); exit(1); } const int share_can_be_downstream = TRUE; const int share_can_be_upstream = FALSE; /* * Main program. (Called `psftp_main' because it gets called from * *sftp.c; bit silly, I know, but it had to be called _something_.) */ int psftp_main(int argc, char *argv[]) { int i; default_protocol = PROT_TELNET; flags = FLAG_STDERR #ifdef FLAG_SYNCAGENT | FLAG_SYNCAGENT #endif ; cmdline_tooltype = TOOLTYPE_FILETRANSFER; sk_init(); /* Load Default Settings before doing anything else. */ conf = conf_new(); do_defaults(NULL, conf); loaded_session = FALSE; for (i = 1; i < argc; i++) { int ret; if (argv[i][0] != '-') break; ret = cmdline_process_param(argv[i], i+1<argc?argv[i+1]:NULL, 1, conf); if (ret == -2) { cmdline_error("option \"%s\" requires an argument", argv[i]); } else if (ret == 2) { i++; /* skip next argument */ } else if (ret == 1) { /* We have our own verbosity in addition to `flags'. */ if (flags & FLAG_VERBOSE) verbose = 1; } else if (strcmp(argv[i], "-pgpfp") == 0) { pgp_fingerprints(); return 1; } else if (strcmp(argv[i], "-r") == 0) { recursive = 1; } else if (strcmp(argv[i], "-p") == 0) { preserve = 1; } else if (strcmp(argv[i], "-q") == 0) { statistics = 0; } else if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "-?") == 0 || strcmp(argv[i], "--help") == 0) { usage(); } else if (strcmp(argv[i], "-V") == 0 || strcmp(argv[i], "--version") == 0) { version(); } else if (strcmp(argv[i], "-ls") == 0) { list = 1; } else if (strcmp(argv[i], "-batch") == 0) { console_batch_mode = 1; } else if (strcmp(argv[i], "-unsafe") == 0) { scp_unsafe_mode = 1; |
︙ | ︙ | |||
2288 2289 2290 2291 2292 2293 2294 2295 2296 2297 2298 2299 2300 2301 | else tolocal(argc, argv); } if (back != NULL && back->connected(backhandle)) { char ch; back->special(backhandle, TS_EOF); ssh_scp_recv((unsigned char *) &ch, 1); } random_save_seed(); cmdline_cleanup(); console_provide_logctx(NULL); back->free(backhandle); | > | 2400 2401 2402 2403 2404 2405 2406 2407 2408 2409 2410 2411 2412 2413 2414 | else tolocal(argc, argv); } if (back != NULL && back->connected(backhandle)) { char ch; back->special(backhandle, TS_EOF); sent_eof = TRUE; ssh_scp_recv((unsigned char *) &ch, 1); } random_save_seed(); cmdline_cleanup(); console_provide_logctx(NULL); back->free(backhandle); |
︙ | ︙ |
Changes to psftp.c.
︙ | ︙ | |||
32 33 34 35 36 37 38 | /* ---------------------------------------------------------------------- * sftp client state. */ char *pwd, *homedir; static Backend *back; static void *backhandle; | | > > > > > > > > > > > > > > > > > > > > > | | | < | | 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 | /* ---------------------------------------------------------------------- * sftp client state. */ char *pwd, *homedir; static Backend *back; static void *backhandle; static Conf *conf; int sent_eof = FALSE; /* ---------------------------------------------------------------------- * Manage sending requests and waiting for replies. */ struct sftp_packet *sftp_wait_for_reply(struct sftp_request *req) { struct sftp_packet *pktin; struct sftp_request *rreq; sftp_register(req); pktin = sftp_recv(); if (pktin == NULL) connection_fatal(NULL, "did not receive SFTP response packet " "from server"); rreq = sftp_find_request(pktin); if (rreq != req) connection_fatal(NULL, "unable to understand SFTP response packet " "from server: %s", fxp_error()); return pktin; } /* ---------------------------------------------------------------------- * Higher-level helper functions used in commands. */ /* * Attempt to canonify a pathname starting from the pwd. If * canonification fails, at least fall back to returning a _valid_ * pathname (though it may be ugly, eg /home/simon/../foobar). */ char *canonify(char *name) { char *fullname, *canonname; struct sftp_packet *pktin; struct sftp_request *req; if (name[0] == '/') { fullname = dupstr(name); } else { char *slash; if (pwd[strlen(pwd) - 1] == '/') slash = ""; else slash = "/"; fullname = dupcat(pwd, slash, name, NULL); } req = fxp_realpath_send(fullname); pktin = sftp_wait_for_reply(req); canonname = fxp_realpath_recv(pktin, req); if (canonname) { sfree(fullname); return canonname; } else { /* * Attempt number 2. Some FXP_REALPATH implementations |
︙ | ︙ | |||
118 119 120 121 122 123 124 | /* * Now i points at the slash. Deal with the final special * case i==0 (ie the whole path was "/nonexistentfile"). */ fullname[i] = '\0'; /* separate the string */ if (i == 0) { | | | | < | | 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 | /* * Now i points at the slash. Deal with the final special * case i==0 (ie the whole path was "/nonexistentfile"). */ fullname[i] = '\0'; /* separate the string */ if (i == 0) { req = fxp_realpath_send("/"); } else { req = fxp_realpath_send(fullname); } pktin = sftp_wait_for_reply(req); canonname = fxp_realpath_recv(pktin, req); if (!canonname) { /* Even that failed. Restore our best guess at the * constructed filename and give up */ fullname[i] = '/'; /* restore slash and last component */ return fullname; } |
︙ | ︙ | |||
203 204 205 206 207 208 209 | /* ---------------------------------------------------------------------- * The meat of the `get' and `put' commands. */ int sftp_get_file(char *fname, char *outfname, int recurse, int restart) { struct fxp_handle *fh; struct sftp_packet *pktin; | | > < | | < | | 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 | /* ---------------------------------------------------------------------- * The meat of the `get' and `put' commands. */ int sftp_get_file(char *fname, char *outfname, int recurse, int restart) { struct fxp_handle *fh; struct sftp_packet *pktin; struct sftp_request *req; struct fxp_xfer *xfer; uint64 offset; WFile *file; int ret, shown_err = FALSE; struct fxp_attrs attrs; /* * In recursive mode, see if we're dealing with a directory. * (If we're not in recursive mode, we need not even check: the * subsequent FXP_OPEN will return a usable error message.) */ if (recurse) { int result; req = fxp_stat_send(fname); pktin = sftp_wait_for_reply(req); result = fxp_stat_recv(pktin, req, &attrs); if (result && (attrs.flags & SSH_FILEXFER_ATTR_PERMISSIONS) && (attrs.permissions & 0040000)) { struct fxp_handle *dirhandle; int nnames, namesize; |
︙ | ︙ | |||
247 248 249 250 251 252 253 | return 0; } /* * Now get the list of filenames in the remote * directory. */ | | | < | | | < | > > > > > | 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 | return 0; } /* * Now get the list of filenames in the remote * directory. */ req = fxp_opendir_send(fname); pktin = sftp_wait_for_reply(req); dirhandle = fxp_opendir_recv(pktin, req); if (!dirhandle) { printf("%s: unable to open directory: %s\n", fname, fxp_error()); return 0; } nnames = namesize = 0; ournames = NULL; while (1) { int i; req = fxp_readdir_send(dirhandle); pktin = sftp_wait_for_reply(req); names = fxp_readdir_recv(pktin, req); if (names == NULL) { if (fxp_error_type() == SSH_FX_EOF) break; printf("%s: reading directory: %s\n", fname, fxp_error()); req = fxp_close_send(dirhandle); pktin = sftp_wait_for_reply(req); fxp_close_recv(pktin, req); sfree(ournames); return 0; } if (names->nnames == 0) { fxp_free_names(names); break; } |
︙ | ︙ | |||
296 297 298 299 300 301 302 | } else { ournames[nnames++] = fxp_dup_name(&names->names[i]); } } fxp_free_names(names); } | | | < | > | < | | < < | 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 | } else { ournames[nnames++] = fxp_dup_name(&names->names[i]); } } fxp_free_names(names); } req = fxp_close_send(dirhandle); pktin = sftp_wait_for_reply(req); fxp_close_recv(pktin, req); /* * Sort the names into a clear order. This ought to * make things more predictable when we're doing a * reget of the same directory, just in case two * readdirs on the same remote directory return a * different order. */ if (nnames > 0) qsort(ournames, nnames, sizeof(*ournames), sftp_name_compare); /* * If we're in restart mode, find the last filename on * this list that already exists. We may have to do a * reget on _that_ file, but shouldn't have to do * anything on the previous files. * * If none of them exists, of course, we start at 0. */ i = 0; if (restart) { while (i < nnames) { char *nextoutfname; int ret; nextoutfname = dir_file_cat(outfname, ournames[i]->filename); ret = (file_type(nextoutfname) == FILE_TYPE_NONEXISTENT); sfree(nextoutfname); if (ret) break; i++; } if (i > 0) |
︙ | ︙ | |||
349 350 351 352 353 354 355 | * call sftp_get_file again. */ for (; i < nnames; i++) { char *nextfname, *nextoutfname; int ret; nextfname = dupcat(fname, "/", ournames[i]->filename, NULL); | < | < < < | 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 | * call sftp_get_file again. */ for (; i < nnames; i++) { char *nextfname, *nextoutfname; int ret; nextfname = dupcat(fname, "/", ournames[i]->filename, NULL); nextoutfname = dir_file_cat(outfname, ournames[i]->filename); ret = sftp_get_file(nextfname, nextoutfname, recurse, restart); restart = FALSE; /* after first partial file, do full */ sfree(nextoutfname); sfree(nextfname); if (!ret) { for (i = 0; i < nnames; i++) { fxp_free_name(ournames[i]); |
︙ | ︙ | |||
379 380 381 382 383 384 385 | } sfree(ournames); return 1; } } | > > > > > | | < | | | | < | | | < | | 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 | } sfree(ournames); return 1; } } req = fxp_stat_send(fname); pktin = sftp_wait_for_reply(req); if (!fxp_stat_recv(pktin, req, &attrs)) attrs.flags = 0; req = fxp_open_send(fname, SSH_FXF_READ, NULL); pktin = sftp_wait_for_reply(req); fh = fxp_open_recv(pktin, req); if (!fh) { printf("%s: open for read: %s\n", fname, fxp_error()); return 0; } if (restart) { file = open_existing_wfile(outfname, NULL); } else { file = open_new_file(outfname, GET_PERMISSIONS(attrs)); } if (!file) { printf("local: unable to open %s\n", outfname); req = fxp_close_send(fh); pktin = sftp_wait_for_reply(req); fxp_close_recv(pktin, req); return 0; } if (restart) { char decbuf[30]; if (seek_file(file, uint64_make(0,0) , FROM_END) == -1) { close_wfile(file); printf("reget: cannot restart %s - file too large\n", outfname); req = fxp_close_send(fh); pktin = sftp_wait_for_reply(req); fxp_close_recv(pktin, req); return 0; } offset = get_file_posn(file); uint64_decimal(offset, decbuf); printf("reget: restarting at file position %s\n", decbuf); |
︙ | ︙ | |||
443 444 445 446 447 448 449 | void *vbuf; int ret, len; int wpos, wlen; xfer_download_queue(xfer); pktin = sftp_recv(); ret = xfer_download_gotpkt(xfer, pktin); | < | > > | 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 | void *vbuf; int ret, len; int wpos, wlen; xfer_download_queue(xfer); pktin = sftp_recv(); ret = xfer_download_gotpkt(xfer, pktin); if (ret <= 0) { if (!shown_err) { printf("error while reading: %s\n", fxp_error()); shown_err = TRUE; } if (ret == INT_MIN) /* pktin not even freed */ sfree(pktin); ret = 0; } while (xfer_download_data(xfer, &vbuf, &len)) { unsigned char *buf = (unsigned char *)vbuf; wpos = 0; |
︙ | ︙ | |||
479 480 481 482 483 484 485 | } } xfer_cleanup(xfer); close_wfile(file); | | | < | | > > < | | < | | | < | | 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 | } } xfer_cleanup(xfer); close_wfile(file); req = fxp_close_send(fh); pktin = sftp_wait_for_reply(req); fxp_close_recv(pktin, req); return ret; } int sftp_put_file(char *fname, char *outfname, int recurse, int restart) { struct fxp_handle *fh; struct fxp_xfer *xfer; struct sftp_packet *pktin; struct sftp_request *req; uint64 offset; RFile *file; int ret, err, eof; struct fxp_attrs attrs; long permissions; /* * In recursive mode, see if we're dealing with a directory. * (If we're not in recursive mode, we need not even check: the * subsequent fopen will return an error message.) */ if (recurse && file_type(fname) == FILE_TYPE_DIRECTORY) { int result; int nnames, namesize; char *name, **ournames; DirHandle *dh; int i; /* * First, attempt to create the destination directory, * unless it already exists. */ req = fxp_stat_send(outfname); pktin = sftp_wait_for_reply(req); result = fxp_stat_recv(pktin, req, &attrs); if (!result || !(attrs.flags & SSH_FILEXFER_ATTR_PERMISSIONS) || !(attrs.permissions & 0040000)) { req = fxp_mkdir_send(outfname); pktin = sftp_wait_for_reply(req); result = fxp_mkdir_recv(pktin, req); if (!result) { printf("%s: create directory: %s\n", outfname, fxp_error()); return 0; } } |
︙ | ︙ | |||
559 560 561 562 563 564 565 | /* * Sort the names into a clear order. This ought to make * things more predictable when we're doing a reput of the * same directory, just in case two readdirs on the same * local directory return a different order. */ | > | | | < | < | < < | 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 | /* * Sort the names into a clear order. This ought to make * things more predictable when we're doing a reput of the * same directory, just in case two readdirs on the same * local directory return a different order. */ if (nnames > 0) qsort(ournames, nnames, sizeof(*ournames), bare_name_compare); /* * If we're in restart mode, find the last filename on this * list that already exists. We may have to do a reput on * _that_ file, but shouldn't have to do anything on the * previous files. * * If none of them exists, of course, we start at 0. */ i = 0; if (restart) { while (i < nnames) { char *nextoutfname; nextoutfname = dupcat(outfname, "/", ournames[i], NULL); req = fxp_stat_send(nextoutfname); pktin = sftp_wait_for_reply(req); result = fxp_stat_recv(pktin, req, &attrs); sfree(nextoutfname); if (!result) break; i++; } if (i > 0) 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. */ for (; i < nnames; i++) { char *nextfname, *nextoutfname; int ret; nextfname = dir_file_cat(fname, ournames[i]); nextoutfname = dupcat(outfname, "/", ournames[i], NULL); ret = sftp_put_file(nextfname, nextoutfname, recurse, restart); restart = FALSE; /* after first partial file, do full */ sfree(nextoutfname); sfree(nextfname); if (!ret) { for (i = 0; i < nnames; i++) { |
︙ | ︙ | |||
626 627 628 629 630 631 632 | sfree(ournames[i]); } sfree(ournames); return 1; } | | > > | | | > | < | | | < | | 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 | sfree(ournames[i]); } sfree(ournames); return 1; } file = open_existing_file(fname, NULL, NULL, NULL, &permissions); if (!file) { printf("local: unable to open %s\n", fname); return 0; } attrs.flags = 0; PUT_PERMISSIONS(attrs, permissions); if (restart) { req = fxp_open_send(outfname, SSH_FXF_WRITE, &attrs); } else { req = fxp_open_send(outfname, SSH_FXF_WRITE | SSH_FXF_CREAT | SSH_FXF_TRUNC, &attrs); } pktin = sftp_wait_for_reply(req); fh = fxp_open_recv(pktin, req); if (!fh) { close_rfile(file); printf("%s: open for write: %s\n", outfname, fxp_error()); return 0; } if (restart) { char decbuf[30]; struct fxp_attrs attrs; int ret; req = fxp_fstat_send(fh); pktin = sftp_wait_for_reply(req); ret = fxp_fstat_recv(pktin, req, &attrs); if (!ret) { close_rfile(file); printf("read size of %s: %s\n", outfname, fxp_error()); return 0; } if (!(attrs.flags & SSH_FILEXFER_ATTR_SIZE)) { |
︙ | ︙ | |||
705 706 707 708 709 710 711 | xfer_upload_data(xfer, buffer, len); } } if (!xfer_done(xfer)) { pktin = sftp_recv(); ret = xfer_upload_gotpkt(xfer, pktin); | | > > > | | > | | < | | 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 | xfer_upload_data(xfer, buffer, len); } } if (!xfer_done(xfer)) { pktin = sftp_recv(); ret = xfer_upload_gotpkt(xfer, pktin); if (ret <= 0) { if (ret == INT_MIN) /* pktin not even freed */ sfree(pktin); if (!err) { printf("error while writing: %s\n", fxp_error()); err = 1; } } } } xfer_cleanup(xfer); req = fxp_close_send(fh); pktin = sftp_wait_for_reply(req); fxp_close_recv(pktin, req); close_rfile(file); return ret; } /* ---------------------------------------------------------------------- |
︙ | ︙ | |||
739 740 741 742 743 744 745 | int namepos; char *wildcard, *prefix; } SftpWildcardMatcher; SftpWildcardMatcher *sftp_begin_wildcard_matching(char *name) { struct sftp_packet *pktin; | | | 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 | int namepos; char *wildcard, *prefix; } SftpWildcardMatcher; SftpWildcardMatcher *sftp_begin_wildcard_matching(char *name) { struct sftp_packet *pktin; struct sftp_request *req; char *wildcard; char *unwcdir, *tmpdir, *cdir; int len, check; SftpWildcardMatcher *swcm; struct fxp_handle *dirh; /* |
︙ | ︙ | |||
770 771 772 773 774 775 776 | printf("Multiple-level wildcards are not supported\n"); sfree(unwcdir); return NULL; } cdir = canonify(unwcdir); | | | < | | 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 | printf("Multiple-level wildcards are not supported\n"); sfree(unwcdir); return NULL; } cdir = canonify(unwcdir); req = fxp_opendir_send(cdir); pktin = sftp_wait_for_reply(req); dirh = fxp_opendir_recv(pktin, req); if (dirh) { swcm = snew(SftpWildcardMatcher); swcm->dirh = dirh; swcm->names = NULL; swcm->wildcard = dupstr(wildcard); swcm->prefix = unwcdir; |
︙ | ︙ | |||
796 797 798 799 800 801 802 | return swcm; } char *sftp_wildcard_get_filename(SftpWildcardMatcher *swcm) { struct fxp_name *name; struct sftp_packet *pktin; | | | | < | > > > > > > > > > > | | 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 | return swcm; } char *sftp_wildcard_get_filename(SftpWildcardMatcher *swcm) { struct fxp_name *name; struct sftp_packet *pktin; struct sftp_request *req; while (1) { if (swcm->names && swcm->namepos >= swcm->names->nnames) { fxp_free_names(swcm->names); swcm->names = NULL; } if (!swcm->names) { req = fxp_readdir_send(swcm->dirh); pktin = sftp_wait_for_reply(req); swcm->names = fxp_readdir_recv(pktin, req); if (!swcm->names) { if (fxp_error_type() != SSH_FX_EOF) printf("%s: reading directory: %s\n", swcm->prefix, fxp_error()); return NULL; } else if (swcm->names->nnames == 0) { /* * Another failure mode which we treat as EOF is if * the server reports success from FXP_READDIR but * returns no actual names. This is unusual, since * from most servers you'd expect at least "." and * "..", but there's nothing forbidding a server from * omitting those if it wants to. */ return NULL; } swcm->namepos = 0; } assert(swcm->names && swcm->namepos < swcm->names->nnames); name = &swcm->names->names[swcm->namepos++]; |
︙ | ︙ | |||
850 851 852 853 854 855 856 | name->filename); } } void sftp_finish_wildcard_matching(SftpWildcardMatcher *swcm) { struct sftp_packet *pktin; | | | | < | | 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 | name->filename); } } void sftp_finish_wildcard_matching(SftpWildcardMatcher *swcm) { struct sftp_packet *pktin; struct sftp_request *req; req = fxp_close_send(swcm->dirh); pktin = sftp_wait_for_reply(req); fxp_close_recv(pktin, req); if (swcm->names) fxp_free_names(swcm->names); sfree(swcm->prefix); sfree(swcm->wildcard); |
︙ | ︙ | |||
895 896 897 898 899 900 901 902 903 904 905 906 907 908 | while ( (newname = sftp_wildcard_get_filename(swcm)) != NULL ) { cname = canonify(newname); if (!cname) { printf("%s: canonify: %s\n", newname, fxp_error()); ret = 0; } matched = TRUE; ret &= func(ctx, cname); sfree(cname); } if (!matched) { /* Politely warn the user that nothing matched. */ | > | 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 | while ( (newname = sftp_wildcard_get_filename(swcm)) != NULL ) { cname = canonify(newname); if (!cname) { printf("%s: canonify: %s\n", newname, fxp_error()); ret = 0; } sfree(newname); matched = TRUE; ret &= func(ctx, cname); sfree(cname); } if (!matched) { /* Politely warn the user that nothing matched. */ |
︙ | ︙ | |||
966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 | not_connected(); return 0; } if (back != NULL && back->connected(backhandle)) { char ch; back->special(backhandle, TS_EOF); sftp_recvdata(&ch, 1); } do_sftp_cleanup(); return 0; } /* * List a directory. If no arguments are given, list pwd; otherwise * list the directory given in words[1]. */ int sftp_cmd_ls(struct sftp_command *cmd) { struct fxp_handle *dirh; struct fxp_names *names; struct fxp_name **ournames; int nnames, namesize; char *dir, *cdir, *unwcdir, *wildcard; struct sftp_packet *pktin; | > | > | 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 | not_connected(); return 0; } if (back != NULL && back->connected(backhandle)) { char ch; back->special(backhandle, TS_EOF); sent_eof = TRUE; sftp_recvdata(&ch, 1); } do_sftp_cleanup(); return 0; } /* * List a directory. If no arguments are given, list pwd; otherwise * list the directory given in words[1]. */ int sftp_cmd_ls(struct sftp_command *cmd) { struct fxp_handle *dirh; struct fxp_names *names; struct fxp_name **ournames; int nnames, namesize; char *dir, *cdir, *unwcdir, *wildcard; struct sftp_packet *pktin; struct sftp_request *req; int i; if (back == NULL) { not_connected(); return 0; } if (cmd->nwords < 2) dir = "."; else dir = cmd->words[1]; unwcdir = snewn(1 + strlen(dir), char); if (wc_unescape(unwcdir, dir)) { dir = unwcdir; wildcard = NULL; } else { char *tmpdir; int len, check; sfree(unwcdir); wildcard = stripslashes(dir, 0); unwcdir = dupstr(dir); len = wildcard - dir; unwcdir[len] = '\0'; if (len > 0 && unwcdir[len-1] == '/') unwcdir[len-1] = '\0'; tmpdir = snewn(1 + len, char); |
︙ | ︙ | |||
1032 1033 1034 1035 1036 1037 1038 | printf("%s: canonify: %s\n", dir, fxp_error()); sfree(unwcdir); return 0; } printf("Listing directory %s\n", cdir); | | | < | | | < | | 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 | printf("%s: canonify: %s\n", dir, fxp_error()); sfree(unwcdir); return 0; } printf("Listing directory %s\n", cdir); req = fxp_opendir_send(cdir); pktin = sftp_wait_for_reply(req); dirh = fxp_opendir_recv(pktin, req); if (dirh == NULL) { printf("Unable to open %s: %s\n", dir, fxp_error()); } else { nnames = namesize = 0; ournames = NULL; while (1) { req = fxp_readdir_send(dirh); pktin = sftp_wait_for_reply(req); names = fxp_readdir_recv(pktin, req); if (names == NULL) { if (fxp_error_type() == SSH_FX_EOF) break; printf("Reading directory %s: %s\n", dir, fxp_error()); break; } |
︙ | ︙ | |||
1072 1073 1074 1075 1076 1077 1078 | for (i = 0; i < names->nnames; i++) if (!wildcard || wc_match(wildcard, names->names[i].filename)) ournames[nnames++] = fxp_dup_name(&names->names[i]); fxp_free_names(names); } | | | < | > | | 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 | for (i = 0; i < names->nnames; i++) if (!wildcard || wc_match(wildcard, names->names[i].filename)) ournames[nnames++] = fxp_dup_name(&names->names[i]); fxp_free_names(names); } req = fxp_close_send(dirh); pktin = sftp_wait_for_reply(req); fxp_close_recv(pktin, req); /* * Now we have our filenames. Sort them by actual file * name, and then output the longname parts. */ if (nnames > 0) qsort(ournames, nnames, sizeof(*ournames), sftp_name_compare); /* * And print them. */ for (i = 0; i < nnames; i++) { printf("%s\n", ournames[i]->longname); fxp_free_name(ournames[i]); |
︙ | ︙ | |||
1107 1108 1109 1110 1111 1112 1113 | * Change directories. We do this by canonifying the new name, then * trying to OPENDIR it. Only if that succeeds do we set the new pwd. */ int sftp_cmd_cd(struct sftp_command *cmd) { struct fxp_handle *dirh; struct sftp_packet *pktin; | | | | < | | | < | | 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 | * Change directories. We do this by canonifying the new name, then * trying to OPENDIR it. Only if that succeeds do we set the new pwd. */ int sftp_cmd_cd(struct sftp_command *cmd) { struct fxp_handle *dirh; struct sftp_packet *pktin; struct sftp_request *req; char *dir; if (back == NULL) { not_connected(); return 0; } if (cmd->nwords < 2) dir = dupstr(homedir); else dir = canonify(cmd->words[1]); if (!dir) { printf("%s: canonify: %s\n", dir, fxp_error()); return 0; } req = fxp_opendir_send(dir); pktin = sftp_wait_for_reply(req); dirh = fxp_opendir_recv(pktin, req); if (!dirh) { printf("Directory %s: %s\n", dir, fxp_error()); sfree(dir); return 0; } req = fxp_close_send(dirh); pktin = sftp_wait_for_reply(req); fxp_close_recv(pktin, req); sfree(pwd); pwd = dir; printf("Remote directory is now %s\n", pwd); return 1; } |
︙ | ︙ | |||
1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 | swcm = NULL; } while (origwfname) { fname = canonify(origwfname); if (!fname) { printf("%s: canonify: %s\n", origwfname, fxp_error()); sfree(unwcfname); return 0; } if (!multiple && i < cmd->nwords) outfname = cmd->words[i++]; else | > > | 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 | swcm = NULL; } while (origwfname) { fname = canonify(origwfname); if (!fname) { sftp_finish_wildcard_matching(swcm); printf("%s: canonify: %s\n", origwfname, fxp_error()); sfree(origwfname); sfree(unwcfname); return 0; } if (!multiple && i < cmd->nwords) outfname = cmd->words[i++]; else |
︙ | ︙ | |||
1388 1389 1390 1391 1392 1393 1394 | return sftp_general_put(cmd, 1, 0); } int sftp_cmd_mkdir(struct sftp_command *cmd) { char *dir; struct sftp_packet *pktin; | | | 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 | return sftp_general_put(cmd, 1, 0); } int sftp_cmd_mkdir(struct sftp_command *cmd) { char *dir; struct sftp_packet *pktin; struct sftp_request *req; int result; int i, ret; if (back == NULL) { not_connected(); return 0; } |
︙ | ︙ | |||
1410 1411 1412 1413 1414 1415 1416 | for (i = 1; i < cmd->nwords; i++) { dir = canonify(cmd->words[i]); if (!dir) { printf("%s: canonify: %s\n", dir, fxp_error()); return 0; } | | | < | | | | < | | 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 | for (i = 1; i < cmd->nwords; i++) { dir = canonify(cmd->words[i]); if (!dir) { printf("%s: canonify: %s\n", dir, fxp_error()); return 0; } req = fxp_mkdir_send(dir); pktin = sftp_wait_for_reply(req); result = fxp_mkdir_recv(pktin, req); if (!result) { printf("mkdir %s: %s\n", dir, fxp_error()); ret = 0; } else printf("mkdir %s: OK\n", dir); sfree(dir); } return ret; } static int sftp_action_rmdir(void *vctx, char *dir) { struct sftp_packet *pktin; struct sftp_request *req; int result; req = fxp_rmdir_send(dir); pktin = sftp_wait_for_reply(req); result = fxp_rmdir_recv(pktin, req); if (!result) { printf("rmdir %s: %s\n", dir, fxp_error()); return 0; } printf("rmdir %s: OK\n", dir); |
︙ | ︙ | |||
1472 1473 1474 1475 1476 1477 1478 | return ret; } static int sftp_action_rm(void *vctx, char *fname) { struct sftp_packet *pktin; | | | | < | | 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 | return ret; } static int sftp_action_rm(void *vctx, char *fname) { struct sftp_packet *pktin; struct sftp_request *req; int result; req = fxp_remove_send(fname); pktin = sftp_wait_for_reply(req); result = fxp_remove_recv(pktin, req); if (!result) { printf("rm %s: %s\n", fname, fxp_error()); return 0; } printf("rm %s: OK\n", fname); |
︙ | ︙ | |||
1514 1515 1516 1517 1518 1519 1520 | return ret; } static int check_is_dir(char *dstfname) { struct sftp_packet *pktin; | | | | < | | | 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 | return ret; } static int check_is_dir(char *dstfname) { struct sftp_packet *pktin; struct sftp_request *req; struct fxp_attrs attrs; int result; req = fxp_stat_send(dstfname); pktin = sftp_wait_for_reply(req); result = fxp_stat_recv(pktin, req, &attrs); if (result && (attrs.flags & SSH_FILEXFER_ATTR_PERMISSIONS) && (attrs.permissions & 0040000)) return TRUE; else return FALSE; } struct sftp_context_mv { char *dstfname; int dest_is_dir; }; static int sftp_action_mv(void *vctx, char *srcfname) { struct sftp_context_mv *ctx = (struct sftp_context_mv *)vctx; struct sftp_packet *pktin; struct sftp_request *req; const char *error; char *finalfname, *newcanon = NULL; int ret, result; if (ctx->dest_is_dir) { char *p; char *newname; |
︙ | ︙ | |||
1565 1566 1567 1568 1569 1570 1571 | sfree(newname); finalfname = newcanon; } else { finalfname = ctx->dstfname; } | | | < | | 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 | sfree(newname); finalfname = newcanon; } else { finalfname = ctx->dstfname; } req = fxp_rename_send(srcfname, finalfname); pktin = sftp_wait_for_reply(req); result = fxp_rename_recv(pktin, req); error = result ? NULL : fxp_error(); if (error) { printf("mv %s %s: %s\n", srcfname, finalfname, error); ret = 0; } else { |
︙ | ︙ | |||
1637 1638 1639 1640 1641 1642 1643 | unsigned attrs_clr, attrs_xor; }; static int sftp_action_chmod(void *vctx, char *fname) { struct fxp_attrs attrs; struct sftp_packet *pktin; | | | | < | | | < | | 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 | unsigned attrs_clr, attrs_xor; }; static int sftp_action_chmod(void *vctx, char *fname) { struct fxp_attrs attrs; struct sftp_packet *pktin; struct sftp_request *req; int result; unsigned oldperms, newperms; struct sftp_context_chmod *ctx = (struct sftp_context_chmod *)vctx; req = fxp_stat_send(fname); pktin = sftp_wait_for_reply(req); result = fxp_stat_recv(pktin, req, &attrs); if (!result || !(attrs.flags & SSH_FILEXFER_ATTR_PERMISSIONS)) { printf("get attrs for %s: %s\n", fname, result ? "file permissions not provided" : fxp_error()); return 0; } attrs.flags = SSH_FILEXFER_ATTR_PERMISSIONS; /* perms _only_ */ oldperms = attrs.permissions & 07777; attrs.permissions &= ~ctx->attrs_clr; attrs.permissions ^= ctx->attrs_xor; newperms = attrs.permissions & 07777; if (oldperms == newperms) return 1; /* no need to do anything! */ req = fxp_setstat_send(fname, attrs); pktin = sftp_wait_for_reply(req); result = fxp_setstat_recv(pktin, req); if (!result) { printf("set attrs for %s: %s\n", fname, fxp_error()); return 0; } printf("%s: %04o -> %04o\n", fname, oldperms, newperms); |
︙ | ︙ | |||
2215 2216 2217 2218 2219 2220 2221 2222 2223 2224 2225 2226 2227 2228 | line = ssh_sftp_get_cmdline("psftp> ", back == NULL); } if (!line || !*line) { cmd->obey = sftp_cmd_quit; if ((mode == 0) || (modeflags & 1)) printf("quit\n"); return cmd; /* eof */ } line[strcspn(line, "\r\n")] = '\0'; if (modeflags & 1) { printf("%s\n", line); | > | 2232 2233 2234 2235 2236 2237 2238 2239 2240 2241 2242 2243 2244 2245 2246 | line = ssh_sftp_get_cmdline("psftp> ", back == NULL); } if (!line || !*line) { cmd->obey = sftp_cmd_quit; if ((mode == 0) || (modeflags & 1)) printf("quit\n"); sfree(line); return cmd; /* eof */ } line[strcspn(line, "\r\n")] = '\0'; if (modeflags & 1) { printf("%s\n", line); |
︙ | ︙ | |||
2261 2262 2263 2264 2265 2266 2267 | * becomes * * >firstword< * >second word< * >this has "quotes" in< * >and"this"< */ | | > > > | 2279 2280 2281 2282 2283 2284 2285 2286 2287 2288 2289 2290 2291 2292 2293 2294 2295 2296 2297 2298 2299 | * becomes * * >firstword< * >second word< * >this has "quotes" in< * >and"this"< */ while (1) { /* skip whitespace */ while (*p && (*p == ' ' || *p == '\t')) p++; /* terminate loop */ if (!*p) break; /* mark start of word */ q = r = p; /* q sits at start, r writes word */ quoting = 0; while (*p) { if (!quoting && (*p == ' ' || *p == '\t')) break; /* reached end of word */ else if (*p == '"' && p[1] == '"') |
︙ | ︙ | |||
2312 2313 2314 2315 2316 2317 2318 | return cmd; } static int do_sftp_init(void) { struct sftp_packet *pktin; | | | | < | > | 2333 2334 2335 2336 2337 2338 2339 2340 2341 2342 2343 2344 2345 2346 2347 2348 2349 2350 2351 2352 2353 2354 2355 2356 2357 2358 2359 2360 2361 2362 2363 2364 2365 2366 2367 2368 2369 2370 2371 2372 2373 2374 2375 2376 2377 2378 2379 2380 2381 2382 | return cmd; } static int do_sftp_init(void) { struct sftp_packet *pktin; struct sftp_request *req; /* * Do protocol initialisation. */ if (!fxp_init()) { fprintf(stderr, "Fatal: unable to initialise SFTP: %s\n", fxp_error()); return 1; /* failure */ } /* * Find out where our home directory is. */ req = fxp_realpath_send("."); pktin = sftp_wait_for_reply(req); homedir = fxp_realpath_recv(pktin, req); if (!homedir) { fprintf(stderr, "Warning: failed to resolve home directory: %s\n", fxp_error()); homedir = dupstr("."); } else { printf("Remote working directory is %s\n", homedir); } pwd = dupstr(homedir); return 0; } void do_sftp_cleanup() { char ch; if (back) { back->special(backhandle, TS_EOF); sent_eof = TRUE; sftp_recvdata(&ch, 1); back->free(backhandle); sftp_cleanup_request(); back = NULL; backhandle = NULL; } if (pwd) { |
︙ | ︙ | |||
2453 2454 2455 2456 2457 2458 2459 2460 2461 2462 2463 2464 2465 2466 | str2 = dupcat("Fatal: ", str, "\n", NULL); sfree(str); va_end(ap); fputs(str2, stderr); sfree(str2); cleanup_exit(1); } void connection_fatal(void *frontend, char *fmt, ...) { char *str, *str2; va_list ap; va_start(ap, fmt); str = dupvprintf(fmt, ap); | > > > > > > > > > > > > | 2474 2475 2476 2477 2478 2479 2480 2481 2482 2483 2484 2485 2486 2487 2488 2489 2490 2491 2492 2493 2494 2495 2496 2497 2498 2499 | str2 = dupcat("Fatal: ", str, "\n", NULL); sfree(str); va_end(ap); fputs(str2, stderr); sfree(str2); cleanup_exit(1); } void nonfatal(char *fmt, ...) { char *str, *str2; va_list ap; va_start(ap, fmt); str = dupvprintf(fmt, ap); str2 = dupcat("Error: ", str, "\n", NULL); sfree(str); va_end(ap); fputs(str2, stderr); sfree(str2); } void connection_fatal(void *frontend, char *fmt, ...) { char *str, *str2; va_list ap; va_start(ap, fmt); str = dupvprintf(fmt, ap); |
︙ | ︙ | |||
2555 2556 2557 2558 2559 2560 2561 2562 2563 2564 2565 2566 2567 2568 | { /* * No "untrusted" output should get here (the way the code is * currently, it's all diverted by FLAG_STDERR). */ assert(!"Unexpected call to from_backend_untrusted()"); return 0; /* not reached */ } int sftp_recvdata(char *buf, int len) { outptr = (unsigned char *) buf; outlen = len; /* | > > > > > > > > > > > > > | 2588 2589 2590 2591 2592 2593 2594 2595 2596 2597 2598 2599 2600 2601 2602 2603 2604 2605 2606 2607 2608 2609 2610 2611 2612 2613 2614 | { /* * No "untrusted" output should get here (the way the code is * currently, it's all diverted by FLAG_STDERR). */ assert(!"Unexpected call to from_backend_untrusted()"); return 0; /* not reached */ } int from_backend_eof(void *frontend) { /* * We expect to be the party deciding when to close the * connection, so if we see EOF before we sent it ourselves, we * should panic. */ if (!sent_eof) { connection_fatal(frontend, "Received unexpected end-of-file from SFTP server"); } return FALSE; } int sftp_recvdata(char *buf, int len) { outptr = (unsigned char *) buf; outlen = len; /* |
︙ | ︙ | |||
2660 2661 2662 2663 2664 2665 2666 | /* * If we haven't loaded session details already (e.g., from -load), * try looking for a session called "host". */ if (!loaded_session) { /* Try to load settings for `host' into a temporary config */ | | | | | | | < > | < | | | | | | | | | > | > > > > > | > > | | < | | | < < | | | | < < < < < | | < > | | | | | | | | > | | < | | | < | | > > > > > | | | | | > | > | | | > > > | | | 2706 2707 2708 2709 2710 2711 2712 2713 2714 2715 2716 2717 2718 2719 2720 2721 2722 2723 2724 2725 2726 2727 2728 2729 2730 2731 2732 2733 2734 2735 2736 2737 2738 2739 2740 2741 2742 2743 2744 2745 2746 2747 2748 2749 2750 2751 2752 2753 2754 2755 2756 2757 2758 2759 2760 2761 2762 2763 2764 2765 2766 2767 2768 2769 2770 2771 2772 2773 2774 2775 2776 2777 2778 2779 2780 2781 2782 2783 2784 2785 2786 2787 2788 2789 2790 2791 2792 2793 2794 2795 2796 2797 2798 2799 2800 2801 2802 2803 2804 2805 2806 2807 2808 2809 2810 2811 2812 2813 2814 2815 2816 2817 2818 2819 2820 2821 2822 2823 2824 2825 2826 2827 2828 2829 2830 2831 2832 2833 2834 2835 2836 2837 2838 2839 2840 2841 2842 2843 2844 2845 2846 2847 2848 2849 2850 2851 2852 2853 2854 2855 2856 2857 2858 2859 2860 2861 2862 2863 2864 2865 2866 | /* * If we haven't loaded session details already (e.g., from -load), * try looking for a session called "host". */ if (!loaded_session) { /* Try to load settings for `host' into a temporary config */ Conf *conf2 = conf_new(); conf_set_str(conf2, CONF_host, ""); do_defaults(host, conf2); if (conf_get_str(conf2, CONF_host)[0] != '\0') { /* Settings present and include hostname */ /* Re-load data into the real config. */ do_defaults(host, conf); } else { /* Session doesn't exist or mention a hostname. */ /* Use `host' as a bare hostname. */ conf_set_str(conf, CONF_host, host); } conf_free(conf2); } else { /* Patch in hostname `host' to session details. */ conf_set_str(conf, CONF_host, host); } /* * Force use of SSH. (If they got the protocol wrong we assume the * port is useless too.) */ if (conf_get_int(conf, CONF_protocol) != PROT_SSH) { conf_set_int(conf, CONF_protocol, PROT_SSH); conf_set_int(conf, CONF_port, 22); } /* * If saved session / Default Settings says SSH-1 (`1 only' or `1'), * then change it to SSH-2, on the grounds that that's more likely to * work for SFTP. (Can be overridden with `-1' option.) * But if it says `2 only' or `2', respect which. */ if ((conf_get_int(conf, CONF_sshprot) & ~1) != 2) /* is it 2 or 3? */ conf_set_int(conf, CONF_sshprot, 2); /* * Enact command-line overrides. */ cmdline_run_saved(conf); /* * Muck about with the hostname in various ways. */ { char *hostbuf = dupstr(conf_get_str(conf, CONF_host)); char *host = hostbuf; char *p, *q; /* * Trim leading whitespace. */ host += strspn(host, " \t"); /* * See if host is of the form user@host, and separate out * the username if so. */ if (host[0] != '\0') { char *atsign = strrchr(host, '@'); if (atsign) { *atsign = '\0'; conf_set_str(conf, CONF_username, host); host = atsign + 1; } } /* * Remove any remaining whitespace. */ p = hostbuf; q = host; while (*q) { if (*q != ' ' && *q != '\t') *p++ = *q; q++; } *p = '\0'; conf_set_str(conf, CONF_host, hostbuf); sfree(hostbuf); } /* Set username */ if (user != NULL && user[0] != '\0') { conf_set_str(conf, CONF_username, user); } if (portnumber) conf_set_int(conf, CONF_port, portnumber); /* * Disable scary things which shouldn't be enabled for simple * things like SCP and SFTP: agent forwarding, port forwarding, * X forwarding. */ conf_set_int(conf, CONF_x11_forward, 0); conf_set_int(conf, CONF_agentfwd, 0); conf_set_int(conf, CONF_ssh_simple, TRUE); { char *key; while ((key = conf_get_str_nthstrkey(conf, CONF_portfwd, 0)) != NULL) conf_del_str_str(conf, CONF_portfwd, key); } /* Set up subsystem name. */ conf_set_str(conf, CONF_remote_cmd, "sftp"); conf_set_int(conf, CONF_ssh_subsys, TRUE); conf_set_int(conf, CONF_nopty, TRUE); /* * Set up fallback option, for SSH-1 servers or servers with the * sftp subsystem not enabled but the server binary installed * in the usual place. We only support fallback on Unix * systems, and we use a kludgy piece of shellery which should * try to find sftp-server in various places (the obvious * systemwide spots /usr/lib and /usr/local/lib, and then the * user's PATH) and finally give up. * * test -x /usr/lib/sftp-server && exec /usr/lib/sftp-server * test -x /usr/local/lib/sftp-server && exec /usr/local/lib/sftp-server * exec sftp-server * * the idea being that this will attempt to use either of the * obvious pathnames and then give up, and when it does give up * it will print the preferred pathname in the error messages. */ conf_set_str(conf, CONF_remote_cmd2, "test -x /usr/lib/sftp-server &&" " exec /usr/lib/sftp-server\n" "test -x /usr/local/lib/sftp-server &&" " exec /usr/local/lib/sftp-server\n" "exec sftp-server"); conf_set_int(conf, CONF_ssh_subsys2, FALSE); back = &ssh_backend; err = back->init(NULL, &backhandle, conf, conf_get_str(conf, CONF_host), conf_get_int(conf, CONF_port), &realhost, 0, conf_get_int(conf, CONF_tcp_keepalives)); if (err != NULL) { fprintf(stderr, "ssh_init: %s\n", err); return 1; } logctx = log_init(NULL, conf); back->provide_logctx(backhandle, logctx); console_provide_logctx(logctx); while (!back->sendok(backhandle)) { if (back->exitcode(backhandle) >= 0) return 1; if (ssh_sftp_loop_iteration() < 0) { fprintf(stderr, "ssh_init: error during SSH connection setup\n"); |
︙ | ︙ | |||
2827 2828 2829 2830 2831 2832 2833 2834 2835 2836 2837 2838 2839 2840 | va_start(ap, p); vfprintf(stderr, p, ap); va_end(ap); fprintf(stderr, "\n try typing \"psftp -h\" for help\n"); exit(1); } /* * Main program. Parse arguments etc. */ int psftp_main(int argc, char *argv[]) { int i; int portnumber = 0; | > > > | 2881 2882 2883 2884 2885 2886 2887 2888 2889 2890 2891 2892 2893 2894 2895 2896 2897 | va_start(ap, p); vfprintf(stderr, p, ap); va_end(ap); fprintf(stderr, "\n try typing \"psftp -h\" for help\n"); exit(1); } const int share_can_be_downstream = TRUE; const int share_can_be_upstream = FALSE; /* * Main program. Parse arguments etc. */ int psftp_main(int argc, char *argv[]) { int i; int portnumber = 0; |
︙ | ︙ | |||
2850 2851 2852 2853 2854 2855 2856 | ; cmdline_tooltype = TOOLTYPE_FILETRANSFER; sk_init(); userhost = user = NULL; /* Load Default Settings before doing anything else. */ | > | | | > | > | 2907 2908 2909 2910 2911 2912 2913 2914 2915 2916 2917 2918 2919 2920 2921 2922 2923 2924 2925 2926 2927 2928 2929 2930 2931 2932 2933 2934 2935 2936 2937 2938 2939 2940 2941 2942 2943 2944 2945 2946 2947 2948 2949 2950 2951 | ; cmdline_tooltype = TOOLTYPE_FILETRANSFER; sk_init(); userhost = user = NULL; /* Load Default Settings before doing anything else. */ conf = conf_new(); do_defaults(NULL, conf); loaded_session = FALSE; for (i = 1; i < argc; i++) { int ret; if (argv[i][0] != '-') { if (userhost) usage(); else userhost = dupstr(argv[i]); continue; } ret = cmdline_process_param(argv[i], i+1<argc?argv[i+1]:NULL, 1, conf); if (ret == -2) { cmdline_error("option \"%s\" requires an argument", argv[i]); } else if (ret == 2) { i++; /* skip next argument */ } else if (ret == 1) { /* We have our own verbosity in addition to `flags'. */ if (flags & FLAG_VERBOSE) verbose = 1; } else if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "-?") == 0 || strcmp(argv[i], "--help") == 0) { usage(); } else if (strcmp(argv[i], "-pgpfp") == 0) { pgp_fingerprints(); return 1; } else if (strcmp(argv[i], "-V") == 0 || strcmp(argv[i], "--version") == 0) { version(); } else if (strcmp(argv[i], "-batch") == 0) { console_batch_mode = 1; } else if (strcmp(argv[i], "-b") == 0 && i + 1 < argc) { mode = 1; batchfile = argv[++i]; } else if (strcmp(argv[i], "-bc") == 0) { |
︙ | ︙ | |||
2904 2905 2906 2907 2908 2909 2910 | back = NULL; /* * If the loaded session provides a hostname, and a hostname has not * otherwise been specified, pop it in `userhost' so that * `psftp -load sessname' is sufficient to start a session. */ | | | | 2964 2965 2966 2967 2968 2969 2970 2971 2972 2973 2974 2975 2976 2977 2978 2979 | back = NULL; /* * If the loaded session provides a hostname, and a hostname has not * otherwise been specified, pop it in `userhost' so that * `psftp -load sessname' is sufficient to start a session. */ if (!userhost && conf_get_str(conf, CONF_host)[0] != '\0') { userhost = dupstr(conf_get_str(conf, CONF_host)); } /* * If a user@host string has already been provided, connect to * it now. */ if (userhost) { |
︙ | ︙ | |||
2930 2931 2932 2933 2934 2935 2936 2937 2938 2939 2940 2941 2942 2943 2944 2945 2946 | } do_sftp(mode, modeflags, batchfile); if (back != NULL && back->connected(backhandle)) { char ch; back->special(backhandle, TS_EOF); sftp_recvdata(&ch, 1); } do_sftp_cleanup(); random_save_seed(); cmdline_cleanup(); console_provide_logctx(NULL); sk_cleanup(); return 0; } | > | 2990 2991 2992 2993 2994 2995 2996 2997 2998 2999 3000 3001 3002 3003 3004 3005 3006 3007 | } do_sftp(mode, modeflags, batchfile); if (back != NULL && back->connected(backhandle)) { char ch; back->special(backhandle, TS_EOF); sent_eof = TRUE; sftp_recvdata(&ch, 1); } do_sftp_cleanup(); random_save_seed(); cmdline_cleanup(); console_provide_logctx(NULL); sk_cleanup(); return 0; } |
Changes to psftp.h.
︙ | ︙ | |||
81 82 83 84 85 86 87 | * * On the other hand, the abstraction is pretty simple: it supports * only opening a file and reading it, or creating a file and writing * it. None of this read-and-write, seeking-back-and-forth stuff. */ typedef struct RFile RFile; typedef struct WFile WFile; | | > | > | | 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 | * * On the other hand, the abstraction is pretty simple: it supports * only opening a file and reading it, or creating a file and writing * it. None of this read-and-write, seeking-back-and-forth stuff. */ typedef struct RFile RFile; typedef struct WFile WFile; /* Output params size, perms, mtime and atime can all be NULL if * desired. perms will be -1 if the OS does not support POSIX permissions. */ RFile *open_existing_file(char *name, uint64 *size, unsigned long *mtime, unsigned long *atime, long *perms); WFile *open_existing_wfile(char *name, uint64 *size); /* Returns <0 on error, 0 on eof, or number of bytes read, as usual */ int read_from_file(RFile *f, void *buffer, int length); /* Closes and frees the RFile */ void close_rfile(RFile *f); WFile *open_new_file(char *name, long perms); /* Returns <0 on error, 0 on eof, or number of bytes written, as usual */ int write_to_file(WFile *f, void *buffer, int length); void set_file_times(WFile *f, unsigned long mtime, unsigned long atime); /* Closes and frees the WFile */ void close_wfile(WFile *f); /* Seek offset bytes through file */ enum { FROM_START, FROM_CURRENT, FROM_END }; |
︙ | ︙ |
Changes to putty.h.
︙ | ︙ | |||
14 15 16 17 18 19 20 | #else #define GLOBAL extern #endif #endif #ifndef DONE_TYPEDEFS #define DONE_TYPEDEFS | | | 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | #else #define GLOBAL extern #endif #endif #ifndef DONE_TYPEDEFS #define DONE_TYPEDEFS typedef struct conf_tag Conf; typedef struct backend_tag Backend; typedef struct terminal_tag Terminal; #endif #include "puttyps.h" #include "network.h" #include "misc.h" |
︙ | ︙ | |||
300 301 302 303 304 305 306 | enum { /* Actions on remote window title query */ TITLE_NONE, TITLE_EMPTY, TITLE_REAL }; enum { | | | | | | | 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 | enum { /* Actions on remote window title query */ TITLE_NONE, TITLE_EMPTY, TITLE_REAL }; enum { /* Protocol back ends. (CONF_protocol) */ PROT_RAW, PROT_TELNET, PROT_RLOGIN, PROT_SSH, /* PROT_SERIAL is supported on a subset of platforms, but it doesn't * hurt to define it globally. */ PROT_SERIAL }; enum { /* Bell settings (CONF_beep) */ BELL_DISABLED, BELL_DEFAULT, BELL_VISUAL, BELL_WAVEFILE, BELL_PCSPEAKER }; enum { /* Taskbar flashing indication on bell (CONF_beep_ind) */ B_IND_DISABLED, B_IND_FLASH, B_IND_STEADY }; enum { /* Resize actions (CONF_resize_action) */ RESIZE_TERM, RESIZE_DISABLED, RESIZE_FONT, RESIZE_EITHER }; enum { /* Function key types (CONF_funky_type) */ FUNKY_TILDE, FUNKY_LINUX, FUNKY_XTERM, FUNKY_VT400, FUNKY_VT100P, FUNKY_SCO }; |
︙ | ︙ | |||
411 412 413 414 415 416 417 | * the proxy end. */ ADDRTYPE_UNSPEC, ADDRTYPE_IPV4, ADDRTYPE_IPV6, ADDRTYPE_NAME }; struct backend_tag { const char *(*init) (void *frontend_handle, void **backend_handle, | < | | | | 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 | * the proxy end. */ ADDRTYPE_UNSPEC, ADDRTYPE_IPV4, ADDRTYPE_IPV6, ADDRTYPE_NAME }; struct backend_tag { const char *(*init) (void *frontend_handle, void **backend_handle, Conf *conf, char *host, int port, char **realhost, int nodelay, int keepalive); void (*free) (void *handle); /* back->reconfig() passes in a replacement configuration. */ void (*reconfig) (void *handle, Conf *conf); /* back->send() returns the current amount of buffered data. */ int (*send) (void *handle, char *buf, int len); /* back->sendbuffer() does the same thing but without attempting a send */ int (*sendbuffer) (void *handle); void (*size) (void *handle, int width, int height); void (*special) (void *handle, Telnet_Special code); const struct telnet_special *(*get_specials) (void *handle); |
︙ | ︙ | |||
457 458 459 460 461 462 463 | /* * Name of this particular application, for use in the config box * and other pieces of text. */ extern const char *const appname; | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 456 457 458 459 460 461 462 463 464 465 466 467 468 469 | /* * Name of this particular application, for use in the config box * and other pieces of text. */ extern const char *const appname; /* * Some global flags denoting the type of application. * * FLAG_VERBOSE is set when the user requests verbose details. * * FLAG_STDERR is set in command-line applications (which have a * functioning stderr that it makes sense to write to) and not in |
︙ | ︙ | |||
742 743 744 745 746 747 748 | * generally not trust the strings. (But \n is required to behave * vaguely sensibly, at least in `instruction', and ideally in * `prompt[]' too.) */ typedef struct { char *prompt; int echo; | > > > > > > > > > > > | | | 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 | * generally not trust the strings. (But \n is required to behave * vaguely sensibly, at least in `instruction', and ideally in * `prompt[]' too.) */ typedef struct { char *prompt; int echo; /* * 'result' must be a dynamically allocated array of exactly * 'resultsize' chars. The code for actually reading input may * realloc it bigger (and adjust resultsize accordingly) if it has * to. The caller should free it again when finished with it. * * If resultsize==0, then result may be NULL. When setting up a * prompt_t, it's therefore easiest to initialise them this way, * which means all actual allocation is done by the callee. This * is what add_prompt does. */ char *result; size_t resultsize; } prompt_t; typedef struct { /* * Indicates whether the information entered is to be used locally * (for instance a key passphrase prompt), or is destined for the wire. * This is a hint only; the front-end is at liberty not to use this * information (so the caller should ensure that the supplied text is |
︙ | ︙ | |||
766 767 768 769 770 771 772 | * if any, and return success) */ prompt_t **prompts; void *frontend; void *data; /* slot for housekeeping data, managed by * get_userpass_input(); initially NULL */ } prompts_t; prompts_t *new_prompts(void *frontend); | | > > | 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 | * if any, and return success) */ prompt_t **prompts; void *frontend; void *data; /* slot for housekeeping data, managed by * get_userpass_input(); initially NULL */ } prompts_t; prompts_t *new_prompts(void *frontend); void add_prompt(prompts_t *p, char *promptstr, int echo); void prompt_set_result(prompt_t *pr, const char *newstr); void prompt_ensure_result_size(prompt_t *pr, int len); /* Burn the evidence. (Assumes _all_ strings want free()ing.) */ void free_prompts(prompts_t *p); /* * Exports from the front end. */ void request_resize(void *frontend, int, int); |
︙ | ︙ | |||
793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 | void palette_reset(void *frontend); void write_aclip(void *frontend, char *, int, int); void write_clip(void *frontend, wchar_t *, int *, int, int); void get_clip(void *frontend, wchar_t **, int *); void optimised_move(void *frontend, int, int, int); void set_raw_mouse_mode(void *frontend, int); void connection_fatal(void *frontend, char *, ...); void fatalbox(char *, ...); void modalfatalbox(char *, ...); #ifdef macintosh #pragma noreturn(fatalbox) #pragma noreturn(modalfatalbox) #endif void do_beep(void *frontend, int); void begin_session(void *frontend); void sys_cursor(void *frontend, int x, int y); void request_paste(void *frontend); void frontend_keypress(void *frontend); void ldisc_update(void *frontend, int echo, int edit); /* It's the backend's responsibility to invoke this at the start of a * connection, if necessary; it can also invoke it later if the set of * special commands changes. It does not need to invoke it at session * shutdown. */ void update_specials_menu(void *frontend); int from_backend(void *frontend, int is_stderr, const char *data, int len); int from_backend_untrusted(void *frontend, const char *data, int len); void notify_remote_exit(void *frontend); /* Get a sensible value for a tty mode. NULL return = don't set. * Otherwise, returned value should be freed by caller. */ char *get_ttymode(void *frontend, const char *mode); /* * >0 = `got all results, carry on' * 0 = `user cancelled' (FIXME distinguish "give up entirely" and "next auth"?) | > > > > > > | 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 | void palette_reset(void *frontend); void write_aclip(void *frontend, char *, int, int); void write_clip(void *frontend, wchar_t *, int *, int, int); void get_clip(void *frontend, wchar_t **, int *); void optimised_move(void *frontend, int, int, int); void set_raw_mouse_mode(void *frontend, int); void connection_fatal(void *frontend, char *, ...); void nonfatal(char *, ...); void fatalbox(char *, ...); void modalfatalbox(char *, ...); #ifdef macintosh #pragma noreturn(fatalbox) #pragma noreturn(modalfatalbox) #endif void do_beep(void *frontend, int); void begin_session(void *frontend); void sys_cursor(void *frontend, int x, int y); void request_paste(void *frontend); void frontend_keypress(void *frontend); void ldisc_update(void *frontend, int echo, int edit); /* It's the backend's responsibility to invoke this at the start of a * connection, if necessary; it can also invoke it later if the set of * special commands changes. It does not need to invoke it at session * shutdown. */ void update_specials_menu(void *frontend); int from_backend(void *frontend, int is_stderr, const char *data, int len); int from_backend_untrusted(void *frontend, const char *data, int len); /* Called when the back end wants to indicate that EOF has arrived on * the server-to-client stream. Returns FALSE to indicate that we * intend to keep the session open in the other direction, or TRUE to * indicate that if they're closing so are we. */ int from_backend_eof(void *frontend); void notify_remote_exit(void *frontend); /* Get a sensible value for a tty mode. NULL return = don't set. * Otherwise, returned value should be freed by caller. */ char *get_ttymode(void *frontend, const char *mode); /* * >0 = `got all results, carry on' * 0 = `user cancelled' (FIXME distinguish "give up entirely" and "next auth"?) |
︙ | ︙ | |||
846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 | stuff is suspended */ BUSY_CPU /* Locally busy (e.g. crypto); user interaction suspended */ }; void set_busy_status(void *frontend, int status); void cleanup_exit(int); /* * Exports from noise.c. */ void noise_get_heavy(void (*func) (void *, int)); void noise_get_light(void (*func) (void *, int)); void noise_regular(void); void noise_ultralight(unsigned long data); void random_save_seed(void); void random_destroy_seed(void); /* * Exports from settings.c. */ Backend *backend_from_name(const char *name); Backend *backend_from_proto(int proto); | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | | | | > > > > | | | < < | | | | > | 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 | stuff is suspended */ BUSY_CPU /* Locally busy (e.g. crypto); user interaction suspended */ }; void set_busy_status(void *frontend, int status); void cleanup_exit(int); /* * Exports from conf.c, and a big enum (via parametric macro) of * configuration option keys. */ #define CONFIG_OPTIONS(X) \ /* X(value-type, subkey-type, keyword) */ \ X(STR, NONE, host) \ X(INT, NONE, port) \ X(INT, NONE, protocol) \ X(INT, NONE, addressfamily) \ X(INT, NONE, close_on_exit) \ X(INT, NONE, warn_on_close) \ X(INT, NONE, ping_interval) /* in seconds */ \ X(INT, NONE, tcp_nodelay) \ X(INT, NONE, tcp_keepalives) \ X(STR, NONE, loghost) /* logical host being contacted, for host key check */ \ /* Proxy options */ \ X(STR, NONE, proxy_exclude_list) \ X(INT, NONE, proxy_dns) \ X(INT, NONE, even_proxy_localhost) \ X(INT, NONE, proxy_type) \ X(STR, NONE, proxy_host) \ X(INT, NONE, proxy_port) \ X(STR, NONE, proxy_username) \ X(STR, NONE, proxy_password) \ X(STR, NONE, proxy_telnet_command) \ /* SSH options */ \ X(STR, NONE, remote_cmd) \ X(STR, NONE, remote_cmd2) /* fallback if remote_cmd fails; never loaded or saved */ \ X(INT, NONE, nopty) \ X(INT, NONE, compression) \ X(INT, INT, ssh_kexlist) \ X(INT, NONE, ssh_rekey_time) /* in minutes */ \ X(STR, NONE, ssh_rekey_data) /* string encoding e.g. "100K", "2M", "1G" */ \ X(INT, NONE, tryagent) \ X(INT, NONE, agentfwd) \ X(INT, NONE, change_username) /* allow username switching in SSH-2 */ \ X(INT, INT, ssh_cipherlist) \ X(FILENAME, NONE, keyfile) \ X(INT, NONE, sshprot) /* use v1 or v2 when both available */ \ X(INT, NONE, ssh2_des_cbc) /* "des-cbc" unrecommended SSH-2 cipher */ \ X(INT, NONE, ssh_no_userauth) /* bypass "ssh-userauth" (SSH-2 only) */ \ X(INT, NONE, ssh_show_banner) /* show USERAUTH_BANNERs (SSH-2 only) */ \ X(INT, NONE, try_tis_auth) \ X(INT, NONE, try_ki_auth) \ X(INT, NONE, try_gssapi_auth) /* attempt gssapi auth */ \ X(INT, NONE, gssapifwd) /* forward tgt via gss */ \ X(INT, INT, ssh_gsslist) /* preference order for local GSS libs */ \ X(FILENAME, NONE, ssh_gss_custom) \ X(INT, NONE, ssh_subsys) /* run a subsystem rather than a command */ \ X(INT, NONE, ssh_subsys2) /* fallback to go with remote_cmd_ptr2 */ \ X(INT, NONE, ssh_no_shell) /* avoid running a shell */ \ X(STR, NONE, ssh_nc_host) /* host to connect to in `nc' mode */ \ X(INT, NONE, ssh_nc_port) /* port to connect to in `nc' mode */ \ /* Telnet options */ \ X(STR, NONE, termtype) \ X(STR, NONE, termspeed) \ X(STR, STR, ttymodes) /* values are "Vvalue" or "A" */ \ X(STR, STR, environmt) \ X(STR, NONE, username) \ X(INT, NONE, username_from_env) \ X(STR, NONE, localusername) \ X(INT, NONE, rfc_environ) \ X(INT, NONE, passive_telnet) \ /* Serial port options */ \ X(STR, NONE, serline) \ X(INT, NONE, serspeed) \ X(INT, NONE, serdatabits) \ X(INT, NONE, serstopbits) \ X(INT, NONE, serparity) \ X(INT, NONE, serflow) \ /* Keyboard options */ \ X(INT, NONE, bksp_is_delete) \ X(INT, NONE, rxvt_homeend) \ X(INT, NONE, funky_type) \ X(INT, NONE, no_applic_c) /* totally disable app cursor keys */ \ X(INT, NONE, no_applic_k) /* totally disable app keypad */ \ X(INT, NONE, no_mouse_rep) /* totally disable mouse reporting */ \ X(INT, NONE, no_remote_resize) /* disable remote resizing */ \ X(INT, NONE, no_alt_screen) /* disable alternate screen */ \ X(INT, NONE, no_remote_wintitle) /* disable remote retitling */ \ X(INT, NONE, no_dbackspace) /* disable destructive backspace */ \ X(INT, NONE, no_remote_charset) /* disable remote charset config */ \ X(INT, NONE, remote_qtitle_action) /* remote win title query action */ \ X(INT, NONE, app_cursor) \ X(INT, NONE, app_keypad) \ X(INT, NONE, nethack_keypad) \ X(INT, NONE, telnet_keyboard) \ X(INT, NONE, telnet_newline) \ X(INT, NONE, alt_f4) /* is it special? */ \ X(INT, NONE, alt_space) /* is it special? */ \ X(INT, NONE, alt_only) /* is it special? */ \ X(INT, NONE, localecho) \ X(INT, NONE, localedit) \ X(INT, NONE, alwaysontop) \ X(INT, NONE, fullscreenonaltenter) \ X(INT, NONE, scroll_on_key) \ X(INT, NONE, scroll_on_disp) \ X(INT, NONE, erase_to_scrollback) \ X(INT, NONE, compose_key) \ X(INT, NONE, ctrlaltkeys) \ X(STR, NONE, wintitle) /* initial window title */ \ /* Terminal options */ \ X(INT, NONE, savelines) \ X(INT, NONE, dec_om) \ X(INT, NONE, wrap_mode) \ X(INT, NONE, lfhascr) \ X(INT, NONE, cursor_type) /* 0=block 1=underline 2=vertical */ \ X(INT, NONE, blink_cur) \ X(INT, NONE, beep) \ X(INT, NONE, beep_ind) \ X(INT, NONE, bellovl) /* bell overload protection active? */ \ X(INT, NONE, bellovl_n) /* number of bells to cause overload */ \ X(INT, NONE, bellovl_t) /* time interval for overload (seconds) */ \ X(INT, NONE, bellovl_s) /* period of silence to re-enable bell (s) */ \ X(FILENAME, NONE, bell_wavefile) \ X(INT, NONE, scrollbar) \ X(INT, NONE, scrollbar_in_fullscreen) \ X(INT, NONE, resize_action) \ X(INT, NONE, bce) \ X(INT, NONE, blinktext) \ X(INT, NONE, win_name_always) \ X(INT, NONE, width) \ X(INT, NONE, height) \ X(FONT, NONE, font) \ X(INT, NONE, font_quality) \ X(FILENAME, NONE, logfilename) \ X(INT, NONE, logtype) \ X(INT, NONE, logxfovr) \ X(INT, NONE, logflush) \ X(INT, NONE, logomitpass) \ X(INT, NONE, logomitdata) \ X(INT, NONE, hide_mouseptr) \ X(INT, NONE, sunken_edge) \ X(INT, NONE, window_border) \ X(STR, NONE, answerback) \ X(STR, NONE, printer) \ X(INT, NONE, arabicshaping) \ X(INT, NONE, bidi) \ /* Colour options */ \ X(INT, NONE, ansi_colour) \ X(INT, NONE, xterm_256_colour) \ X(INT, NONE, system_colour) \ X(INT, NONE, try_palette) \ X(INT, NONE, bold_style) \ X(INT, INT, colours) \ /* Selection options */ \ X(INT, NONE, mouse_is_xterm) \ X(INT, NONE, rect_select) \ X(INT, NONE, rawcnp) \ X(INT, NONE, rtf_paste) \ X(INT, NONE, mouse_override) \ X(INT, INT, wordness) \ /* translations */ \ X(INT, NONE, vtmode) \ X(STR, NONE, line_codepage) \ X(INT, NONE, cjk_ambig_wide) \ X(INT, NONE, utf8_override) \ X(INT, NONE, xlat_capslockcyr) \ /* X11 forwarding */ \ X(INT, NONE, x11_forward) \ X(STR, NONE, x11_display) \ X(INT, NONE, x11_auth) \ X(FILENAME, NONE, xauthfile) \ /* port forwarding */ \ X(INT, NONE, lport_acceptall) /* accept conns from hosts other than localhost */ \ X(INT, NONE, rport_acceptall) /* same for remote forwarded ports (SSH-2 only) */ \ /* \ * Subkeys for 'portfwd' can have the following forms: \ * \ * [LR]localport \ * [LR]localaddr:localport \ * \ * Dynamic forwardings are indicated by an 'L' key, and the \ * special value "D". For all other forwardings, the value \ * should be of the form 'host:port'. \ */ \ X(STR, STR, portfwd) \ /* SSH bug compatibility modes */ \ X(INT, NONE, sshbug_ignore1) \ X(INT, NONE, sshbug_plainpw1) \ X(INT, NONE, sshbug_rsa1) \ X(INT, NONE, sshbug_hmac2) \ X(INT, NONE, sshbug_derivekey2) \ X(INT, NONE, sshbug_rsapad2) \ X(INT, NONE, sshbug_pksessid2) \ X(INT, NONE, sshbug_rekey2) \ X(INT, NONE, sshbug_maxpkt2) \ X(INT, NONE, sshbug_ignore2) \ X(INT, NONE, sshbug_winadj) \ /* \ * 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. \ */ \ X(INT, NONE, ssh_simple) \ X(INT, NONE, ssh_connection_sharing) \ X(INT, NONE, ssh_connection_sharing_upstream) \ X(INT, NONE, ssh_connection_sharing_downstream) \ /* Options for pterm. Should split out into platform-dependent part. */ \ X(INT, NONE, stamp_utmp) \ X(INT, NONE, login_shell) \ X(INT, NONE, scrollbar_on_left) \ X(INT, NONE, shadowbold) \ X(FONT, NONE, boldfont) \ X(FONT, NONE, widefont) \ X(FONT, NONE, wideboldfont) \ X(INT, NONE, shadowboldoffset) \ X(INT, NONE, crhaslf) \ X(STR, NONE, winclass) \ /* Now define the actual enum of option keywords using that macro. */ #define CONF_ENUM_DEF(valtype, keytype, keyword) CONF_ ## keyword, enum config_primary_key { CONFIG_OPTIONS(CONF_ENUM_DEF) N_CONFIG_OPTIONS }; #undef CONF_ENUM_DEF #define NCFGCOLOURS 22 /* number of colours in CONF_colours above */ /* Functions handling configuration structures. */ Conf *conf_new(void); /* create an empty configuration */ void conf_free(Conf *conf); Conf *conf_copy(Conf *oldconf); void conf_copy_into(Conf *dest, Conf *src); /* Mandatory accessor functions: enforce by assertion that keys exist. */ int conf_get_int(Conf *conf, int key); int conf_get_int_int(Conf *conf, int key, int subkey); char *conf_get_str(Conf *conf, int key); /* result still owned by conf */ char *conf_get_str_str(Conf *conf, int key, const char *subkey); Filename *conf_get_filename(Conf *conf, int key); FontSpec *conf_get_fontspec(Conf *conf, int key); /* still owned by conf */ /* Optional accessor function: return NULL if key does not exist. */ char *conf_get_str_str_opt(Conf *conf, int key, const char *subkey); /* Accessor function to step through a string-subkeyed list. * Returns the next subkey after the provided one, or the first if NULL. * Returns NULL if there are none left. * Both the return value and *subkeyout are still owned by conf. */ char *conf_get_str_strs(Conf *conf, int key, char *subkeyin, char **subkeyout); /* Return the nth string subkey in a list. Owned by conf. NULL if beyond end */ char *conf_get_str_nthstrkey(Conf *conf, int key, int n); /* Functions to set entries in configuration. Always copy their inputs. */ void conf_set_int(Conf *conf, int key, int value); void conf_set_int_int(Conf *conf, int key, int subkey, int value); void conf_set_str(Conf *conf, int key, const char *value); void conf_set_str_str(Conf *conf, int key, const char *subkey, const char *val); void conf_del_str_str(Conf *conf, int key, const char *subkey); void conf_set_filename(Conf *conf, int key, const Filename *val); void conf_set_fontspec(Conf *conf, int key, const FontSpec *val); /* Serialisation functions for Duplicate Session */ int conf_serialised_size(Conf *conf); void conf_serialise(Conf *conf, void *data); int conf_deserialise(Conf *conf, void *data, int maxsize);/*returns size used*/ /* * Functions to copy, free, serialise and deserialise FontSpecs. * Provided per-platform, to go with the platform's idea of a * FontSpec's contents. * * fontspec_serialise returns the number of bytes written, and can * handle data==NULL without crashing. So you can call it once to find * out a size, then again once you've allocated a buffer. */ FontSpec *fontspec_copy(const FontSpec *f); void fontspec_free(FontSpec *f); int fontspec_serialise(FontSpec *f, void *data); FontSpec *fontspec_deserialise(void *data, int maxsize, int *used); /* * Exports from noise.c. */ void noise_get_heavy(void (*func) (void *, int)); void noise_get_light(void (*func) (void *, int)); void noise_regular(void); void noise_ultralight(unsigned long data); void random_save_seed(void); void random_destroy_seed(void); /* * Exports from settings.c. */ Backend *backend_from_name(const char *name); Backend *backend_from_proto(int proto); char *get_remote_username(Conf *conf); /* dynamically allocated */ char *save_settings(char *section, Conf *conf); void save_open_settings(void *sesskey, Conf *conf); void load_settings(char *section, Conf *conf); void load_open_settings(void *sesskey, Conf *conf); void get_sesslist(struct sesslist *, int allocate); void do_defaults(char *, Conf *); void registry_cleanup(void); /* * Functions used by settings.c to provide platform-specific * default settings. * * (The integer one is expected to return `def' if it has no clear * opinion of its own. This is because there's no integer value * which I can reliably set aside to indicate `nil'. The string * function is perfectly all right returning NULL, of course. The * Filename and FontSpec functions are _not allowed_ to fail to * return, since these defaults _must_ be per-platform.) * * The 'Filename *' returned by platform_default_filename, and the * 'FontSpec *' returned by platform_default_fontspec, have ownership * transferred to the caller, and must be freed. */ char *platform_default_s(const char *name); int platform_default_i(const char *name, int def); Filename *platform_default_filename(const char *name); FontSpec *platform_default_fontspec(const char *name); /* * Exports from terminal.c. */ Terminal *term_init(Conf *, struct unicode_data *, void *); void term_free(Terminal *); void term_size(Terminal *, int, int, int); void term_paint(Terminal *, Context, int, int, int, int, int); void term_scroll(Terminal *, int, int); void term_scroll_to_selection(Terminal *, int); void term_pwron(Terminal *, int); void term_clrsb(Terminal *); void term_mouse(Terminal *, Mouse_Button, Mouse_Button, Mouse_Action, int,int,int,int,int); void term_key(Terminal *, Key_Sym, wchar_t *, size_t, unsigned int, unsigned int); void term_deselect(Terminal *); void term_update(Terminal *); void term_invalidate(Terminal *); void term_blink(Terminal *, int set_cursor); void term_do_paste(Terminal *); void term_nopaste(Terminal *); int term_ldisc(Terminal *, int option); void term_copyall(Terminal *); void term_reconfig(Terminal *, Conf *); void term_seen_key_event(Terminal *); int term_data(Terminal *, int is_stderr, const char *data, int len); int term_data_untrusted(Terminal *, const char *data, int len); void term_provide_resize_fn(Terminal *term, void (*resize_fn)(void *, int, int), void *resize_ctx); void term_provide_logctx(Terminal *term, void *logctx); void term_set_focus(Terminal *term, int has_focus); char *term_get_ttymode(Terminal *term, const char *mode); int term_get_userpass_input(Terminal *term, prompts_t *p, unsigned char *in, int inlen); int format_arrow_key(char *buf, Terminal *term, int xkey, int ctrl); /* * Exports from logging.c. */ void *log_init(void *frontend, Conf *conf); void log_free(void *logctx); void log_reconfig(void *logctx, Conf *conf); void logfopen(void *logctx); void logfclose(void *logctx); void logtraffic(void *logctx, unsigned char c, int logmode); void logflush(void *logctx); void log_eventlog(void *logctx, const char *string); enum { PKT_INCOMING, PKT_OUTGOING }; enum { PKTLOG_EMIT, PKTLOG_BLANK, PKTLOG_OMIT }; struct logblank_t { int offset; int len; int type; }; void log_packet(void *logctx, int direction, int type, char *texttype, const void *data, int len, int n_blanks, const struct logblank_t *blanks, const unsigned long *sequence, unsigned downstream_id, const char *additional_log_text); /* * Exports from testback.c */ extern Backend null_backend; extern Backend loop_backend; |
︙ | ︙ | |||
983 984 985 986 987 988 989 | * Exports from ssh.c. */ extern Backend ssh_backend; /* * Exports from ldisc.c. */ | | > | 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 | * Exports from ssh.c. */ extern Backend ssh_backend; /* * Exports from ldisc.c. */ void *ldisc_create(Conf *, Terminal *, Backend *, void *, void *); void ldisc_configure(void *, Conf *); void ldisc_free(void *); void ldisc_send(void *handle, char *buf, int len, int interactive); /* * Exports from ldiscucs.c. */ void lpage_send(void *, int codepage, char *buf, int len, int interactive); |
︙ | ︙ | |||
1011 1012 1013 1014 1015 1016 1017 | void random_ref(void); void random_unref(void); /* * Exports from pinger.c. */ typedef struct pinger_tag *Pinger; | | | | | | | | | | | | 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 | void random_ref(void); void random_unref(void); /* * Exports from pinger.c. */ typedef struct pinger_tag *Pinger; Pinger pinger_new(Conf *conf, Backend *back, void *backhandle); void pinger_reconfig(Pinger, Conf *oldconf, Conf *newconf); void pinger_free(Pinger); /* * Exports from misc.c. */ #include "misc.h" int conf_launchable(Conf *conf); char const *conf_dest(Conf *conf); /* * Exports from sercfg.c. */ void ser_setup_config_box(struct controlbox *b, int midsession, int parity_mask, int flow_mask); /* * Exports from version.c. */ extern char ver[]; /* * Exports from unicode.c. */ #ifndef CP_UTF8 #define CP_UTF8 65001 #endif /* void init_ucs(void); -- this is now in platform-specific headers */ int is_dbcs_leadbyte(int codepage, char byte); int mb_to_wc(int codepage, int flags, const char *mbstr, int mblen, wchar_t *wcstr, int wclen); int wc_to_mb(int codepage, int flags, const wchar_t *wcstr, int wclen, char *mbstr, int mblen, char *defchr, int *defused, struct unicode_data *ucsdata); wchar_t xlat_uskbd2cyrllic(int ch); int check_compose(int first, int second); int decode_codepage(char *cp_name); const char *cp_enumerate (int index); const char *cp_name(int codepage); void get_unitab(int codepage, wchar_t * unitab, int ftype); /* * Exports from wcwidth.c */ int mk_wcwidth(unsigned int ucs); int mk_wcswidth(const unsigned int *pwcs, size_t n); int mk_wcwidth_cjk(unsigned int ucs); int mk_wcswidth_cjk(const unsigned int *pwcs, size_t n); /* * Exports from mscrypto.c */ #ifdef MSCRYPTOAPI int crypto_startup(); void crypto_wrapup(); |
︙ | ︙ | |||
1126 1127 1128 1129 1130 1131 1132 | * askappend can return four values: * * - 2 means overwrite the log file * - 1 means append to the log file * - 0 means cancel logging for this session * - -1 means please wait. */ | | | 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 | * askappend can return four values: * * - 2 means overwrite the log file * - 1 means append to the log file * - 0 means cancel logging for this session * - -1 means please wait. */ int askappend(void *frontend, Filename *filename, void (*callback)(void *ctx, int result), void *ctx); /* * Exports from console frontends (wincons.c, uxcons.c) * that aren't equivalents to things in windlg.c et al. */ extern int console_batch_mode; |
︙ | ︙ | |||
1155 1156 1157 1158 1159 1160 1161 | void printer_finish_job(printer_job *); /* * Exports from cmdline.c (and also cmdline_error(), which is * defined differently in various places and required _by_ * cmdline.c). */ | | | > > > > > > > > > > > > | > > > | | | > > > > | 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 | void printer_finish_job(printer_job *); /* * Exports from cmdline.c (and also cmdline_error(), which is * defined differently in various places and required _by_ * cmdline.c). */ int cmdline_process_param(char *, char *, int, Conf *); void cmdline_run_saved(Conf *); void cmdline_cleanup(void); int cmdline_get_passwd_input(prompts_t *p, unsigned char *in, int inlen); #define TOOLTYPE_FILETRANSFER 1 #define TOOLTYPE_NONNETWORK 2 extern int cmdline_tooltype; void cmdline_error(char *, ...); /* * Exports from config.c. */ struct controlbox; union control; void conf_radiobutton_handler(union control *ctrl, void *dlg, void *data, int event); #define CHECKBOX_INVERT (1<<30) void conf_checkbox_handler(union control *ctrl, void *dlg, void *data, int event); void conf_editbox_handler(union control *ctrl, void *dlg, void *data, int event); void conf_filesel_handler(union control *ctrl, void *dlg, void *data, int event); void conf_fontsel_handler(union control *ctrl, void *dlg, void *data, int event); void setup_config_box(struct controlbox *b, int midsession, int protocol, int protcfginfo); /* * Exports from minibidi.c. */ typedef struct bidi_char { unsigned int origwc, wc; unsigned short index; } bidi_char; int do_bidi(bidi_char *line, int count); int do_shape(bidi_char *line, bidi_char *to, int count); int is_rtl(int c); /* * X11 auth mechanisms we know about. */ enum { X11_NO_AUTH, X11_MIT, /* MIT-MAGIC-COOKIE-1 */ X11_XDM, /* XDM-AUTHORIZATION-1 */ X11_NAUTHS }; extern const char *const x11_authnames[]; /* declared in x11fwd.c */ /* * Miscellaneous exports from the platform-specific code. * * filename_serialise and filename_deserialise have the same semantics * as fontspec_serialise and fontspec_deserialise above. */ Filename *filename_from_str(const char *string); const char *filename_to_str(const Filename *fn); int filename_equal(const Filename *f1, const Filename *f2); int filename_is_null(const Filename *fn); Filename *filename_copy(const Filename *fn); void filename_free(Filename *fn); int filename_serialise(const Filename *f, void *data); Filename *filename_deserialise(void *data, int maxsize, int *used); char *get_username(void); /* return value needs freeing */ char *get_random_data(int bytes); /* used in cmdgen.c */ /* * Exports and imports from timing.c. * * schedule_timer() asks the front end to schedule a callback to a |
︙ | ︙ | |||
1294 1295 1296 1297 1298 1299 1300 | * (It does also mean that the timer action in the above example * will occur 100ms early, but this is not generally critical. And * the hypothetical 1% error in wait() will be partially corrected * for anyway when, _after_ run_timers() returns, you call * GETTICKCOUNT() and compare the result with the returned `next' * value to find out how long you have to make your next wait().) */ | | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 | * (It does also mean that the timer action in the above example * will occur 100ms early, but this is not generally critical. And * the hypothetical 1% error in wait() will be partially corrected * for anyway when, _after_ run_timers() returns, you call * GETTICKCOUNT() and compare the result with the returned `next' * value to find out how long you have to make your next wait().) */ typedef void (*timer_fn_t)(void *ctx, unsigned long now); unsigned long schedule_timer(int ticks, timer_fn_t fn, void *ctx); void expire_timer_context(void *ctx); int run_timers(unsigned long now, unsigned long *next); void timer_change_notify(unsigned long next); /* * Exports from callback.c. * * This provides a method of queuing function calls to be run at the * earliest convenience from the top-level event loop. Use it if * you're deep in a nested chain of calls and want to trigger an * action which will probably lead to your function being re-entered * recursively if you just call the initiating function the normal * way. * * Most front ends run the queued callbacks by simply calling * run_toplevel_callbacks() after handling each event in their * top-level event loop. However, if a front end doesn't have control * over its own event loop (e.g. because it's using GTK) then it can * instead request notifications when a callback is available, so that * it knows to ask its delegate event loop to do the same thing. Also, * if a front end needs to know whether a callback is pending without * actually running it (e.g. so as to put a zero timeout on a select() * call) then it can call toplevel_callback_pending(), which will * return true if at least one callback is in the queue. */ typedef void (*toplevel_callback_fn_t)(void *ctx); void queue_toplevel_callback(toplevel_callback_fn_t fn, void *ctx); void run_toplevel_callbacks(void); int toplevel_callback_pending(void); typedef void (*toplevel_callback_notify_fn_t)(void *frontend); void request_callback_notifications(toplevel_callback_notify_fn_t notify, void *frontend); /* * Define no-op macros for the jump list functions, on platforms that * don't support them. (This is a bit of a hack, and it'd be nicer to * localise even the calls to those functions into the Windows front * end, but it'll do for the moment.) */ #ifndef JUMPLIST_SUPPORTED #define add_session_to_jumplist(x) ((void)0) #define remove_session_from_jumplist(x) ((void)0) #endif /* SURROGATE PAIR */ #ifndef IS_HIGH_SURROGATE #define HIGH_SURROGATE_START 0xd800 #define HIGH_SURROGATE_END 0xdbff #define LOW_SURROGATE_START 0xdc00 #define LOW_SURROGATE_END 0xdfff #define IS_HIGH_SURROGATE(wch) (((wch) >= HIGH_SURROGATE_START) && \ ((wch) <= HIGH_SURROGATE_END)) #define IS_LOW_SURROGATE(wch) (((wch) >= LOW_SURROGATE_START) && \ ((wch) <= LOW_SURROGATE_END)) #define IS_SURROGATE_PAIR(hs, ls) (IS_HIGH_SURROGATE(hs) && \ IS_LOW_SURROGATE(ls)) #endif #define IS_SURROGATE(wch) (((wch) >= HIGH_SURROGATE_START) && \ ((wch) <= LOW_SURROGATE_END)) #define HIGH_SURROGATE_OF(codept) \ (HIGH_SURROGATE_START + (((codept) - 0x10000) >> 10)) #define LOW_SURROGATE_OF(codept) \ (LOW_SURROGATE_START + (((codept) - 0x10000) & 0x3FF)) #define FROM_SURROGATES(wch1, wch2) \ (0x10000 + (((wch1) & 0x3FF) << 10) + ((wch2) & 0x3FF)) #endif |
Changes to puttymem.h.
︙ | ︙ | |||
30 31 32 33 34 35 36 37 38 39 | void safefree(void *); /* * Direct use of smalloc within the code should be avoided where * possible, in favour of these type-casting macros which ensure * you don't mistakenly allocate enough space for one sort of * structure and assign it to a different sort of pointer. */ #define snew(type) ((type *)snmalloc(1, sizeof(type))) #define snewn(n, type) ((type *)snmalloc((n), sizeof(type))) | > > > > > > > > | > > | 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 | void safefree(void *); /* * Direct use of smalloc within the code should be avoided where * possible, in favour of these type-casting macros which ensure * you don't mistakenly allocate enough space for one sort of * structure and assign it to a different sort of pointer. * * The nasty trick in sresize with sizeof arranges for the compiler, * in passing, to type-check the expression ((type *)0 == (ptr)), i.e. * to type-check that the input pointer is a pointer to the correct * type. The construction sizeof(stuff) ? (b) : (b) looks like a * violation of the first principle of safe macros, but in fact it's * OK - although it _expands_ the macro parameter more than once, it * only _evaluates_ it once, so it's still side-effect safe. */ #define snew(type) ((type *)snmalloc(1, sizeof(type))) #define snewn(n, type) ((type *)snmalloc((n), sizeof(type))) #define sresize(ptr, n, type) \ ((type *)snrealloc(sizeof((type *)0 == (ptr)) ? (ptr) : (ptr), \ (n), sizeof(type))) #endif |
Changes to raw.c.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | /* * "Raw" backend. */ #include <stdio.h> #include <stdlib.h> #include "putty.h" #ifndef FALSE #define FALSE 0 #endif #ifndef TRUE #define TRUE 1 #endif #define RAW_MAX_BACKLOG 4096 typedef struct raw_backend_data { const struct plug_function_table *fn; /* the above field _must_ be first in the structure */ Socket s; int bufsize; void *frontend; } *Raw; static void raw_size(void *handle, int width, int height); static void c_write(Raw raw, char *buf, int len) { int backlog = from_backend(raw->frontend, 0, buf, len); | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | /* * "Raw" backend. */ #include <stdio.h> #include <stdlib.h> #include <limits.h> #include "putty.h" #ifndef FALSE #define FALSE 0 #endif #ifndef TRUE #define TRUE 1 #endif #define RAW_MAX_BACKLOG 4096 typedef struct raw_backend_data { const struct plug_function_table *fn; /* the above field _must_ be first in the structure */ Socket s; int closed_on_socket_error; int bufsize; void *frontend; int sent_console_eof, sent_socket_eof; } *Raw; static void raw_size(void *handle, int width, int height); static void c_write(Raw raw, char *buf, int len) { int backlog = from_backend(raw->frontend, 0, buf, len); |
︙ | ︙ | |||
43 44 45 46 47 48 49 50 51 52 53 54 55 56 | if (type == 0) msg = dupprintf("Connecting to %s port %d", addrbuf, port); else msg = dupprintf("Failed to connect to %s: %s", addrbuf, error_msg); logevent(raw->frontend, msg); } static int raw_closing(Plug plug, const char *error_msg, int error_code, int calling_back) { Raw raw = (Raw) plug; | > > > > > > > > > > > > > > > > > > | | | > | | < < | | > | > > > > > > > > > > > > > > | 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 | if (type == 0) msg = dupprintf("Connecting to %s port %d", addrbuf, port); else msg = dupprintf("Failed to connect to %s: %s", addrbuf, error_msg); logevent(raw->frontend, msg); sfree(msg); } static void raw_check_close(Raw raw) { /* * Called after we send EOF on either the socket or the console. * Its job is to wind up the session once we have sent EOF on both. */ if (raw->sent_console_eof && raw->sent_socket_eof) { if (raw->s) { sk_close(raw->s); raw->s = NULL; notify_remote_exit(raw->frontend); } } } static int raw_closing(Plug plug, const char *error_msg, int error_code, int calling_back) { Raw raw = (Raw) plug; if (error_msg) { /* A socket error has occurred. */ if (raw->s) { sk_close(raw->s); raw->s = NULL; raw->closed_on_socket_error = TRUE; notify_remote_exit(raw->frontend); } logevent(raw->frontend, error_msg); connection_fatal(raw->frontend, "%s", error_msg); } else { /* Otherwise, the remote side closed the connection normally. */ if (!raw->sent_console_eof && from_backend_eof(raw->frontend)) { /* * The front end wants us to close the outgoing side of the * connection as soon as we see EOF from the far end. */ if (!raw->sent_socket_eof) { if (raw->s) sk_write_eof(raw->s); raw->sent_socket_eof= TRUE; } } raw->sent_console_eof = TRUE; raw_check_close(raw); } return 0; } static int raw_receive(Plug plug, int urgent, char *data, int len) { Raw raw = (Raw) plug; c_write(raw, data, len); |
︙ | ︙ | |||
85 86 87 88 89 90 91 | * * Returns an error message, or NULL on success. * * Also places the canonical host name into `realhost'. It must be * freed by the caller. */ static const char *raw_init(void *frontend_handle, void **backend_handle, | | > > > > > | | | | > | | | 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 | * * Returns an error message, or NULL on success. * * Also places the canonical host name into `realhost'. It must be * freed by the caller. */ static const char *raw_init(void *frontend_handle, void **backend_handle, Conf *conf, char *host, int port, char **realhost, int nodelay, int keepalive) { static const struct plug_function_table fn_table = { raw_log, raw_closing, raw_receive, raw_sent }; SockAddr addr; const char *err; Raw raw; int addressfamily; char *loghost; raw = snew(struct raw_backend_data); raw->fn = &fn_table; raw->s = NULL; raw->closed_on_socket_error = FALSE; *backend_handle = raw; raw->sent_console_eof = raw->sent_socket_eof = FALSE; raw->frontend = frontend_handle; addressfamily = conf_get_int(conf, CONF_addressfamily); /* * Try to find host. */ { char *buf; buf = dupprintf("Looking up host \"%s\"%s", host, (addressfamily == ADDRTYPE_IPV4 ? " (IPv4)" : (addressfamily == ADDRTYPE_IPV6 ? " (IPv6)" : ""))); logevent(raw->frontend, buf); sfree(buf); } addr = name_lookup(host, port, realhost, conf, addressfamily); if ((err = sk_addr_error(addr)) != NULL) { sk_addr_free(addr); return err; } if (port < 0) port = 23; /* default telnet port */ /* * Open socket. */ raw->s = new_connection(addr, *realhost, port, 0, 1, nodelay, keepalive, (Plug) raw, conf); if ((err = sk_socket_error(raw->s)) != NULL) return err; loghost = conf_get_str(conf, CONF_loghost); if (*loghost) { char *colon; sfree(*realhost); *realhost = dupstr(loghost); colon = strrchr(*realhost, ':'); if (colon) { /* * FIXME: if we ever update this aspect of ssh.c for * IPv6 literal management, this should change in line * with it. */ |
︙ | ︙ | |||
166 167 168 169 170 171 172 | sk_close(raw->s); sfree(raw); } /* * Stub routine (we don't have any need to reconfigure this backend). */ | | | 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 | sk_close(raw->s); sfree(raw); } /* * Stub routine (we don't have any need to reconfigure this backend). */ static void raw_reconfig(void *handle, Conf *conf) { } /* * Called to send data down the raw connection. */ static int raw_send(void *handle, char *buf, int len) |
︙ | ︙ | |||
204 205 206 207 208 209 210 | static void raw_size(void *handle, int width, int height) { /* Do nothing! */ return; } /* | | > > > > > > | | 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 | static void raw_size(void *handle, int width, int height) { /* Do nothing! */ return; } /* * Send raw special codes. We only handle outgoing EOF here. */ static void raw_special(void *handle, Telnet_Special code) { Raw raw = (Raw) handle; if (code == TS_EOF && raw->s) { sk_write_eof(raw->s); raw->sent_socket_eof= TRUE; raw_check_close(raw); } return; } /* * Return a list of the special codes that make sense in this * protocol. */ |
︙ | ︙ | |||
260 261 262 263 264 265 266 267 268 269 270 271 272 273 | } static int raw_exitcode(void *handle) { Raw raw = (Raw) handle; if (raw->s != NULL) return -1; /* still connected */ else /* Exit codes are a meaningless concept in the Raw protocol */ return 0; } /* * cfg_info for Raw does nothing at all. | > > | 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 | } static int raw_exitcode(void *handle) { Raw raw = (Raw) handle; if (raw->s != NULL) return -1; /* still connected */ else if (raw->closed_on_socket_error) return INT_MAX; /* a socket error counts as an unclean exit */ else /* Exit codes are a meaningless concept in the Raw protocol */ return 0; } /* * cfg_info for Raw does nothing at all. |
︙ | ︙ |
Changes to rlogin.c.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | /* * Rlogin backend. */ #include <stdio.h> #include <stdlib.h> #include <ctype.h> #include "putty.h" #ifndef FALSE #define FALSE 0 #endif #ifndef TRUE #define TRUE 1 #endif #define RLOGIN_MAX_BACKLOG 4096 typedef struct rlogin_tag { const struct plug_function_table *fn; /* the above field _must_ be first in the structure */ Socket s; int bufsize; int firstbyte; int cansize; int term_width, term_height; void *frontend; | > > | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | /* * Rlogin backend. */ #include <stdio.h> #include <stdlib.h> #include <limits.h> #include <ctype.h> #include "putty.h" #ifndef FALSE #define FALSE 0 #endif #ifndef TRUE #define TRUE 1 #endif #define RLOGIN_MAX_BACKLOG 4096 typedef struct rlogin_tag { const struct plug_function_table *fn; /* the above field _must_ be first in the structure */ Socket s; int closed_on_socket_error; int bufsize; int firstbyte; int cansize; int term_width, term_height; void *frontend; Conf *conf; /* In case we need to read a username from the terminal before starting */ prompts_t *prompt; } *Rlogin; static void rlogin_size(void *handle, int width, int height); |
︙ | ︙ | |||
52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 | if (type == 0) msg = dupprintf("Connecting to %s port %d", addrbuf, port); else msg = dupprintf("Failed to connect to %s: %s", addrbuf, error_msg); logevent(rlogin->frontend, msg); } static int rlogin_closing(Plug plug, const char *error_msg, int error_code, int calling_back) { Rlogin rlogin = (Rlogin) plug; if (rlogin->s) { sk_close(rlogin->s); rlogin->s = NULL; notify_remote_exit(rlogin->frontend); } if (error_msg) { /* A socket error has occurred. */ logevent(rlogin->frontend, error_msg); connection_fatal(rlogin->frontend, "%s", error_msg); } /* Otherwise, the remote side closed the connection normally. */ | > > > > > > > > > > | 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 | if (type == 0) msg = dupprintf("Connecting to %s port %d", addrbuf, port); else msg = dupprintf("Failed to connect to %s: %s", addrbuf, error_msg); logevent(rlogin->frontend, msg); sfree(msg); } static int rlogin_closing(Plug plug, const char *error_msg, int error_code, int calling_back) { Rlogin rlogin = (Rlogin) plug; /* * We don't implement independent EOF in each direction for Telnet * connections; as soon as we get word that the remote side has * sent us EOF, we wind up the whole connection. */ if (rlogin->s) { sk_close(rlogin->s); rlogin->s = NULL; if (error_msg) rlogin->closed_on_socket_error = TRUE; notify_remote_exit(rlogin->frontend); } if (error_msg) { /* A socket error has occurred. */ logevent(rlogin->frontend, error_msg); connection_fatal(rlogin->frontend, "%s", error_msg); } /* Otherwise, the remote side closed the connection normally. */ |
︙ | ︙ | |||
118 119 120 121 122 123 124 | rlogin->bufsize = bufsize; } static void rlogin_startup(Rlogin rlogin, const char *ruser) { char z = 0; char *p; | | | | | | | > | < | | | | > > > | | | > | | | | > | | | > | < > | | 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 | rlogin->bufsize = bufsize; } static void rlogin_startup(Rlogin rlogin, const char *ruser) { char z = 0; char *p; sk_write(rlogin->s, &z, 1); p = conf_get_str(rlogin->conf, CONF_localusername); sk_write(rlogin->s, p, strlen(p)); sk_write(rlogin->s, &z, 1); sk_write(rlogin->s, ruser, strlen(ruser)); sk_write(rlogin->s, &z, 1); p = conf_get_str(rlogin->conf, CONF_termtype); sk_write(rlogin->s, p, strlen(p)); sk_write(rlogin->s, "/", 1); p = conf_get_str(rlogin->conf, CONF_termspeed); sk_write(rlogin->s, p, strspn(p, "0123456789")); rlogin->bufsize = sk_write(rlogin->s, &z, 1); rlogin->prompt = NULL; } /* * Called to set up the rlogin connection. * * Returns an error message, or NULL on success. * * Also places the canonical host name into `realhost'. It must be * freed by the caller. */ static const char *rlogin_init(void *frontend_handle, void **backend_handle, Conf *conf, char *host, int port, char **realhost, int nodelay, int keepalive) { static const struct plug_function_table fn_table = { rlogin_log, rlogin_closing, rlogin_receive, rlogin_sent }; SockAddr addr; const char *err; Rlogin rlogin; char *ruser; int addressfamily; char *loghost; rlogin = snew(struct rlogin_tag); rlogin->fn = &fn_table; rlogin->s = NULL; rlogin->closed_on_socket_error = FALSE; rlogin->frontend = frontend_handle; rlogin->term_width = conf_get_int(conf, CONF_width); rlogin->term_height = conf_get_int(conf, CONF_height); rlogin->firstbyte = 1; rlogin->cansize = 0; rlogin->prompt = NULL; rlogin->conf = conf_copy(conf); *backend_handle = rlogin; addressfamily = conf_get_int(conf, CONF_addressfamily); /* * Try to find host. */ { char *buf; buf = dupprintf("Looking up host \"%s\"%s", host, (addressfamily == ADDRTYPE_IPV4 ? " (IPv4)" : (addressfamily == ADDRTYPE_IPV6 ? " (IPv6)" : ""))); logevent(rlogin->frontend, buf); sfree(buf); } addr = name_lookup(host, port, realhost, conf, addressfamily); if ((err = sk_addr_error(addr)) != NULL) { sk_addr_free(addr); return err; } if (port < 0) port = 513; /* default rlogin port */ /* * Open socket. */ rlogin->s = new_connection(addr, *realhost, port, 1, 0, nodelay, keepalive, (Plug) rlogin, conf); if ((err = sk_socket_error(rlogin->s)) != NULL) return err; loghost = conf_get_str(conf, CONF_loghost); if (*loghost) { char *colon; sfree(*realhost); *realhost = dupstr(loghost); colon = strrchr(*realhost, ':'); if (colon) { /* * FIXME: if we ever update this aspect of ssh.c for * IPv6 literal management, this should change in line * with it. */ *colon++ = '\0'; } } /* * Send local username, remote username, terminal type and * terminal speed - unless we don't have the remote username yet, * in which case we prompt for it and may end up deferring doing * anything else until the local prompt mechanism returns. */ if ((ruser = get_remote_username(conf)) != NULL) { rlogin_startup(rlogin, ruser); sfree(ruser); } else { int ret; rlogin->prompt = new_prompts(rlogin->frontend); rlogin->prompt->to_server = TRUE; rlogin->prompt->name = dupstr("Rlogin login name"); add_prompt(rlogin->prompt, dupstr("rlogin username: "), TRUE); ret = get_userpass_input(rlogin->prompt, NULL, 0); if (ret >= 0) { rlogin_startup(rlogin, rlogin->prompt->prompts[0]->result); } } return NULL; } static void rlogin_free(void *handle) { Rlogin rlogin = (Rlogin) handle; if (rlogin->prompt) free_prompts(rlogin->prompt); if (rlogin->s) sk_close(rlogin->s); conf_free(rlogin->conf); sfree(rlogin); } /* * Stub routine (we don't have any need to reconfigure this backend). */ static void rlogin_reconfig(void *handle, Conf *conf) { } /* * Called to send data down the rlogin connection. */ static int rlogin_send(void *handle, char *buf, int len) |
︙ | ︙ | |||
376 377 378 379 380 381 382 383 384 385 386 387 388 389 | } static int rlogin_exitcode(void *handle) { Rlogin rlogin = (Rlogin) handle; if (rlogin->s != NULL) return -1; /* still connected */ else /* If we ever implement RSH, we'll probably need to do this properly */ return 0; } /* * cfg_info for rlogin does nothing at all. | > > | 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 | } static int rlogin_exitcode(void *handle) { Rlogin rlogin = (Rlogin) handle; if (rlogin->s != NULL) return -1; /* still connected */ else if (rlogin->closed_on_socket_error) return INT_MAX; /* a socket error counts as an unclean exit */ else /* If we ever implement RSH, we'll probably need to do this properly */ return 0; } /* * cfg_info for rlogin does nothing at all. |
︙ | ︙ |
Deleted sc.c.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted sc.h.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Changes to sercfg.c.
︙ | ︙ | |||
27 28 29 30 31 32 33 | {"Odd", SER_PAR_ODD}, {"Even", SER_PAR_EVEN}, {"Mark", SER_PAR_MARK}, {"Space", SER_PAR_SPACE}, }; int mask = ctrl->listbox.context.i; int i, j; | | > > > > | | 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 | {"Odd", SER_PAR_ODD}, {"Even", SER_PAR_EVEN}, {"Mark", SER_PAR_MARK}, {"Space", SER_PAR_SPACE}, }; int mask = ctrl->listbox.context.i; int i, j; Conf *conf = (Conf *)data; if (event == EVENT_REFRESH) { /* Fetching this once at the start of the function ensures we * remember what the right value is supposed to be when * operations below cause reentrant calls to this function. */ int oldparity = conf_get_int(conf, CONF_serparity); dlg_update_start(ctrl, dlg); dlg_listbox_clear(ctrl, dlg); for (i = 0; i < lenof(parities); i++) { if (mask & (1 << i)) dlg_listbox_addwithid(ctrl, dlg, parities[i].name, parities[i].val); } |
︙ | ︙ | |||
52 53 54 55 56 57 58 | } } if (i == lenof(parities)) { /* an unsupported setting was chosen */ dlg_listbox_select(ctrl, dlg, 0); oldparity = SER_PAR_NONE; } dlg_update_done(ctrl, dlg); | | | | > > > > | | | | 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 | } } if (i == lenof(parities)) { /* an unsupported setting was chosen */ dlg_listbox_select(ctrl, dlg, 0); oldparity = SER_PAR_NONE; } dlg_update_done(ctrl, dlg); conf_set_int(conf, CONF_serparity, oldparity); /* restore */ } else if (event == EVENT_SELCHANGE) { int i = dlg_listbox_index(ctrl, dlg); if (i < 0) i = SER_PAR_NONE; else i = dlg_listbox_getid(ctrl, dlg, i); conf_set_int(conf, CONF_serparity, i); } } static void serial_flow_handler(union control *ctrl, void *dlg, void *data, int event) { static const struct { const char *name; int val; } flows[] = { {"None", SER_FLOW_NONE}, {"XON/XOFF", SER_FLOW_XONXOFF}, {"RTS/CTS", SER_FLOW_RTSCTS}, {"DSR/DTR", SER_FLOW_DSRDTR}, }; int mask = ctrl->listbox.context.i; int i, j; Conf *conf = (Conf *)data; if (event == EVENT_REFRESH) { /* Fetching this once at the start of the function ensures we * remember what the right value is supposed to be when * operations below cause reentrant calls to this function. */ int oldflow = conf_get_int(conf, CONF_serflow); dlg_update_start(ctrl, dlg); dlg_listbox_clear(ctrl, dlg); for (i = 0; i < lenof(flows); i++) { if (mask & (1 << i)) dlg_listbox_addwithid(ctrl, dlg, flows[i].name, flows[i].val); } for (i = j = 0; i < lenof(flows); i++) { if (mask & (1 << i)) { if (oldflow == flows[i].val) { dlg_listbox_select(ctrl, dlg, j); break; } j++; } } if (i == lenof(flows)) { /* an unsupported setting was chosen */ dlg_listbox_select(ctrl, dlg, 0); oldflow = SER_FLOW_NONE; } dlg_update_done(ctrl, dlg); conf_set_int(conf, CONF_serflow, oldflow);/* restore */ } else if (event == EVENT_SELCHANGE) { int i = dlg_listbox_index(ctrl, dlg); if (i < 0) i = SER_FLOW_NONE; else i = dlg_listbox_getid(ctrl, dlg, i); conf_set_int(conf, CONF_serflow, i); } } void ser_setup_config_box(struct controlbox *b, int midsession, int parity_mask, int flow_mask) { struct controlset *s; |
︙ | ︙ | |||
169 170 171 172 173 174 175 | * midflight, although we do allow all other * reconfiguration. */ s = ctrl_getset(b, "Connection/Serial", "serline", "Select a serial line"); ctrl_editbox(s, "Serial line to connect to", 'l', 40, HELPCTX(serial_line), | | < | | | | 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 | * midflight, although we do allow all other * reconfiguration. */ s = ctrl_getset(b, "Connection/Serial", "serline", "Select a serial line"); ctrl_editbox(s, "Serial line to connect to", 'l', 40, HELPCTX(serial_line), conf_editbox_handler, I(CONF_serline), I(1)); } s = ctrl_getset(b, "Connection/Serial", "sercfg", "Configure the serial line"); ctrl_editbox(s, "Speed (baud)", 's', 40, HELPCTX(serial_speed), conf_editbox_handler, I(CONF_serspeed), I(-1)); ctrl_editbox(s, "Data bits", 'b', 40, HELPCTX(serial_databits), conf_editbox_handler, I(CONF_serdatabits), I(-1)); /* * Stop bits come in units of one half. */ ctrl_editbox(s, "Stop bits", 't', 40, HELPCTX(serial_stopbits), conf_editbox_handler, I(CONF_serstopbits), I(-2)); ctrl_droplist(s, "Parity", 'p', 40, HELPCTX(serial_parity), serial_parity_handler, I(parity_mask)); ctrl_droplist(s, "Flow control", 'f', 40, HELPCTX(serial_flow), serial_flow_handler, I(flow_mask)); } |
Changes to settings.c.
︙ | ︙ | |||
66 67 68 69 70 71 72 | Backend **p; for (p = backends; *p != NULL; p++) if ((*p)->protocol == proto) return *p; return NULL; } | | > | < | < | | | < < < < | < < | < | | < | < | < | < | | | < > | < < | > > > > > | < | | | > | > > | | > | > > | | > > > > > | > | > > > > > > > > > > | > > > < > > | > > > > | | > > > > > > > > > > > > > | > > > > > > > > | > > | > | > > > > > > | > > > | > > > > > > > > > | > | > > > > > > | > | > | | | | > > > > > | > > > | > | | 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 | Backend **p; for (p = backends; *p != NULL; p++) if ((*p)->protocol == proto) return *p; return NULL; } char *get_remote_username(Conf *conf) { char *username = conf_get_str(conf, CONF_username); if (*username) { return dupstr(username); } else if (conf_get_int(conf, CONF_username_from_env)) { /* Use local username. */ return get_username(); /* might still be NULL */ } else { return NULL; } } static char *gpps_raw(void *handle, const char *name, const char *def) { char *ret = read_setting_s(handle, name); if (!ret) ret = platform_default_s(name); if (!ret) ret = def ? dupstr(def) : NULL; /* permit NULL as final fallback */ return ret; } static void gpps(void *handle, const char *name, const char *def, Conf *conf, int primary) { char *val = gpps_raw(handle, name, def); conf_set_str(conf, primary, val); sfree(val); } /* * gppfont and gppfile cannot have local defaults, since the very * format of a Filename or FontSpec is platform-dependent. So the * platform-dependent functions MUST return some sort of value. */ static void gppfont(void *handle, const char *name, Conf *conf, int primary) { FontSpec *result = read_setting_fontspec(handle, name); if (!result) result = platform_default_fontspec(name); conf_set_fontspec(conf, primary, result); fontspec_free(result); } static void gppfile(void *handle, const char *name, Conf *conf, int primary) { Filename *result = read_setting_filename(handle, name); if (!result) result = platform_default_filename(name); conf_set_filename(conf, primary, result); filename_free(result); } static int gppi_raw(void *handle, char *name, int def) { def = platform_default_i(name, def); return read_setting_i(handle, name, def); } static void gppi(void *handle, char *name, int def, Conf *conf, int primary) { conf_set_int(conf, primary, gppi_raw(handle, name, def)); } /* * Read a set of name-value pairs in the format we occasionally use: * NAME\tVALUE\0NAME\tVALUE\0\0 in memory * NAME=VALUE,NAME=VALUE, in storage * `def' is in the storage format. */ static int gppmap(void *handle, char *name, Conf *conf, int primary) { char *buf, *p, *q, *key, *val; /* * Start by clearing any existing subkeys of this key from conf. */ while ((key = conf_get_str_nthstrkey(conf, primary, 0)) != NULL) conf_del_str_str(conf, primary, key); /* * Now read a serialised list from the settings and unmarshal it * into its components. */ buf = gpps_raw(handle, name, NULL); if (!buf) return FALSE; p = buf; while (*p) { q = buf; val = NULL; while (*p && *p != ',') { int c = *p++; if (c == '=') c = '\0'; if (c == '\\') c = *p++; *q++ = c; if (!c) val = q; } if (*p == ',') p++; if (!val) val = q; *q = '\0'; if (primary == CONF_portfwd && strchr(buf, 'D') != NULL) { /* * Backwards-compatibility hack: dynamic forwardings are * indexed in the data store as a third type letter in the * key, 'D' alongside 'L' and 'R' - but really, they * should be filed under 'L' with a special _value_, * because local and dynamic forwardings both involve * _listening_ on a local port, and are hence mutually * exclusive on the same port number. So here we translate * the legacy storage format into the sensible internal * form, by finding the D and turning it into a L. */ char *newkey = dupstr(buf); *strchr(newkey, 'D') = 'L'; conf_set_str_str(conf, primary, newkey, "D"); sfree(newkey); } else { conf_set_str_str(conf, primary, buf, val); } } sfree(buf); return TRUE; } /* * Write a set of name/value pairs in the above format. */ static void wmap(void *handle, char const *outkey, Conf *conf, int primary) { char *buf, *p, *q, *key, *realkey, *val; int len; len = 1; /* allow for NUL */ for (val = conf_get_str_strs(conf, primary, NULL, &key); val != NULL; val = conf_get_str_strs(conf, primary, key, &key)) len += 2 + 2 * (strlen(key) + strlen(val)); /* allow for escaping */ buf = snewn(len, char); p = buf; for (val = conf_get_str_strs(conf, primary, NULL, &key); val != NULL; val = conf_get_str_strs(conf, primary, key, &key)) { if (primary == CONF_portfwd && !strcmp(val, "D")) { /* * Backwards-compatibility hack, as above: translate from * the sensible internal representation of dynamic * forwardings (key "L<port>", value "D") to the * conceptually incoherent legacy storage format (key * "D<port>", value empty). */ char *L; realkey = key; /* restore it at end of loop */ val = ""; key = dupstr(key); L = strchr(key, 'L'); if (L) *L = 'D'; } else { realkey = NULL; } if (p != buf) *p++ = ','; for (q = key; *q; q++) { if (*q == '=' || *q == ',' || *q == '\\') *p++ = '\\'; *p++ = *q; } *p++ = '='; for (q = val; *q; q++) { if (*q == '=' || *q == ',' || *q == '\\') *p++ = '\\'; *p++ = *q; } if (realkey) { free(key); key = realkey; } } *p = '\0'; write_setting_s(handle, outkey, buf); sfree(buf); } static int key2val(const struct keyvalwhere *mapping, int nmaps, char *key) { int i; |
︙ | ︙ | |||
210 211 212 213 214 215 216 | * Helper function to parse a comma-separated list of strings into * a preference list array of values. Any missing values are added * to the end and duplicates are weeded. * XXX: assumes vals in 'mapping' are small +ve integers */ static void gprefs(void *sesskey, char *name, char *def, const struct keyvalwhere *mapping, int nvals, | | | | > | > > | 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 | * Helper function to parse a comma-separated list of strings into * a preference list array of values. Any missing values are added * to the end and duplicates are weeded. * XXX: assumes vals in 'mapping' are small +ve integers */ static void gprefs(void *sesskey, char *name, char *def, const struct keyvalwhere *mapping, int nvals, Conf *conf, int primary) { char *commalist; char *p, *q; int i, j, n, v, pos; unsigned long seen = 0; /* bitmap for weeding dups etc */ /* * Fetch the string which we'll parse as a comma-separated list. */ commalist = gpps_raw(sesskey, name, def); /* * Go through that list and convert it into values. */ n = 0; p = commalist; while (1) { while (*p && *p == ',') p++; if (!*p) break; /* no more words */ q = p; while (*p && *p != ',') p++; if (*p) *p++ = '\0'; v = key2val(mapping, nvals, q); if (v != -1 && !(seen & (1 << v))) { seen |= (1 << v); conf_set_int_int(conf, primary, n, v); n++; } } sfree(commalist); /* * Now go through 'mapping' and add values that weren't mentioned * in the list we fetched. We may have to loop over it multiple * times so that we add values before other values whose default * positions depend on them. */ |
︙ | ︙ | |||
268 269 270 271 272 273 274 | * OK, we can work out where to add this element, so * do so. */ if (mapping[i].vrel == -1) { pos = (mapping[i].where < 0 ? n : 0); } else { for (j = 0; j < n; j++) | > | | > | | | > | > | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | < | | | | | | | | | < | < < < < < < < < < < < < < < < < < < | > > > > > > | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | > > | | | | | | > | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | > | | | | | | | | | | | | | | | > > > | | | | | | | | | | | | | | | | | | | | | > | | | | | | | | | < | < < > < < < | | < < < < < < | < | | | | | | | | | | | | < | > | < | | < | < | | | | | < | | | | | | | | | | | < | | | | | | | | | | | | | < | | < < < < < < < < < < < < < < < < | | | | | | | | | | < | | | | | | | | | | | | | | | | < | | | | | | | | | | | | | | | | | < > | | | | < > | | | | | | | | | | | | | | | | | | | | | | | | > | | | | | | | | > | < | | | | | | | | | | | | | | < | | | | | | | | | | | | | | | | | | > | | | | | | | | | | | | | | | | > > > | | | 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 | * OK, we can work out where to add this element, so * do so. */ if (mapping[i].vrel == -1) { pos = (mapping[i].where < 0 ? n : 0); } else { for (j = 0; j < n; j++) if (conf_get_int_int(conf, primary, j) == mapping[i].vrel) break; assert(j < n); /* implied by (seen & (1<<vrel)) */ pos = (mapping[i].where < 0 ? j : j+1); } /* * And add it. */ for (j = n-1; j >= pos; j--) conf_set_int_int(conf, primary, j+1, conf_get_int_int(conf, primary, j)); conf_set_int_int(conf, primary, pos, mapping[i].v); n++; } } } } /* * Write out a preference list. */ static void wprefs(void *sesskey, char *name, const struct keyvalwhere *mapping, int nvals, Conf *conf, int primary) { char *buf, *p; int i, maxlen; for (maxlen = i = 0; i < nvals; i++) { const char *s = val2key(mapping, nvals, conf_get_int_int(conf, primary, i)); if (s) { maxlen += (maxlen > 0 ? 1 : 0) + strlen(s); } } buf = snewn(maxlen + 1, char); p = buf; for (i = 0; i < nvals; i++) { const char *s = val2key(mapping, nvals, conf_get_int_int(conf, primary, i)); if (s) { p += sprintf(p, "%s%s", (p > buf ? "," : ""), s); } } assert(p - buf == maxlen); *p = '\0'; write_setting_s(sesskey, name, buf); sfree(buf); } char *save_settings(char *section, Conf *conf) { void *sesskey; char *errmsg; sesskey = open_settings_w(section, &errmsg); if (!sesskey) return errmsg; save_open_settings(sesskey, conf); close_settings_w(sesskey); return NULL; } void save_open_settings(void *sesskey, Conf *conf) { int i; char *p; write_setting_i(sesskey, "Present", 1); write_setting_s(sesskey, "HostName", conf_get_str(conf, CONF_host)); write_setting_filename(sesskey, "LogFileName", conf_get_filename(conf, CONF_logfilename)); write_setting_i(sesskey, "LogType", conf_get_int(conf, CONF_logtype)); write_setting_i(sesskey, "LogFileClash", conf_get_int(conf, CONF_logxfovr)); write_setting_i(sesskey, "LogFlush", conf_get_int(conf, CONF_logflush)); write_setting_i(sesskey, "SSHLogOmitPasswords", conf_get_int(conf, CONF_logomitpass)); write_setting_i(sesskey, "SSHLogOmitData", conf_get_int(conf, CONF_logomitdata)); p = "raw"; { const Backend *b = backend_from_proto(conf_get_int(conf, CONF_protocol)); if (b) p = b->name; } write_setting_s(sesskey, "Protocol", p); write_setting_i(sesskey, "PortNumber", conf_get_int(conf, CONF_port)); /* The CloseOnExit numbers are arranged in a different order from * the standard FORCE_ON / FORCE_OFF / AUTO. */ write_setting_i(sesskey, "CloseOnExit", (conf_get_int(conf, CONF_close_on_exit)+2)%3); write_setting_i(sesskey, "WarnOnClose", !!conf_get_int(conf, CONF_warn_on_close)); write_setting_i(sesskey, "PingInterval", conf_get_int(conf, CONF_ping_interval) / 60); /* minutes */ write_setting_i(sesskey, "PingIntervalSecs", conf_get_int(conf, CONF_ping_interval) % 60); /* seconds */ write_setting_i(sesskey, "TCPNoDelay", conf_get_int(conf, CONF_tcp_nodelay)); write_setting_i(sesskey, "TCPKeepalives", conf_get_int(conf, CONF_tcp_keepalives)); write_setting_s(sesskey, "TerminalType", conf_get_str(conf, CONF_termtype)); write_setting_s(sesskey, "TerminalSpeed", conf_get_str(conf, CONF_termspeed)); wmap(sesskey, "TerminalModes", conf, CONF_ttymodes); /* Address family selection */ write_setting_i(sesskey, "AddressFamily", conf_get_int(conf, CONF_addressfamily)); /* proxy settings */ write_setting_s(sesskey, "ProxyExcludeList", conf_get_str(conf, CONF_proxy_exclude_list)); write_setting_i(sesskey, "ProxyDNS", (conf_get_int(conf, CONF_proxy_dns)+2)%3); write_setting_i(sesskey, "ProxyLocalhost", conf_get_int(conf, CONF_even_proxy_localhost)); write_setting_i(sesskey, "ProxyMethod", conf_get_int(conf, CONF_proxy_type)); write_setting_s(sesskey, "ProxyHost", conf_get_str(conf, CONF_proxy_host)); write_setting_i(sesskey, "ProxyPort", conf_get_int(conf, CONF_proxy_port)); write_setting_s(sesskey, "ProxyUsername", conf_get_str(conf, CONF_proxy_username)); write_setting_s(sesskey, "ProxyPassword", conf_get_str(conf, CONF_proxy_password)); write_setting_s(sesskey, "ProxyTelnetCommand", conf_get_str(conf, CONF_proxy_telnet_command)); wmap(sesskey, "Environment", conf, CONF_environmt); write_setting_s(sesskey, "UserName", conf_get_str(conf, CONF_username)); write_setting_i(sesskey, "UserNameFromEnvironment", conf_get_int(conf, CONF_username_from_env)); write_setting_s(sesskey, "LocalUserName", conf_get_str(conf, CONF_localusername)); write_setting_i(sesskey, "NoPTY", conf_get_int(conf, CONF_nopty)); write_setting_i(sesskey, "Compression", conf_get_int(conf, CONF_compression)); write_setting_i(sesskey, "TryAgent", conf_get_int(conf, CONF_tryagent)); write_setting_i(sesskey, "AgentFwd", conf_get_int(conf, CONF_agentfwd)); write_setting_i(sesskey, "GssapiFwd", conf_get_int(conf, CONF_gssapifwd)); write_setting_i(sesskey, "ChangeUsername", conf_get_int(conf, CONF_change_username)); wprefs(sesskey, "Cipher", ciphernames, CIPHER_MAX, conf, CONF_ssh_cipherlist); wprefs(sesskey, "KEX", kexnames, KEX_MAX, conf, CONF_ssh_kexlist); write_setting_i(sesskey, "RekeyTime", conf_get_int(conf, CONF_ssh_rekey_time)); write_setting_s(sesskey, "RekeyBytes", conf_get_str(conf, CONF_ssh_rekey_data)); write_setting_i(sesskey, "SshNoAuth", conf_get_int(conf, CONF_ssh_no_userauth)); write_setting_i(sesskey, "SshBanner", conf_get_int(conf, CONF_ssh_show_banner)); write_setting_i(sesskey, "AuthTIS", conf_get_int(conf, CONF_try_tis_auth)); write_setting_i(sesskey, "AuthKI", conf_get_int(conf, CONF_try_ki_auth)); write_setting_i(sesskey, "AuthGSSAPI", conf_get_int(conf, CONF_try_gssapi_auth)); #ifndef NO_GSSAPI wprefs(sesskey, "GSSLibs", gsslibkeywords, ngsslibs, conf, CONF_ssh_gsslist); write_setting_filename(sesskey, "GSSCustom", conf_get_filename(conf, CONF_ssh_gss_custom)); #endif write_setting_i(sesskey, "SshNoShell", conf_get_int(conf, CONF_ssh_no_shell)); write_setting_i(sesskey, "SshProt", conf_get_int(conf, CONF_sshprot)); write_setting_s(sesskey, "LogHost", conf_get_str(conf, CONF_loghost)); write_setting_i(sesskey, "SSH2DES", conf_get_int(conf, CONF_ssh2_des_cbc)); write_setting_filename(sesskey, "PublicKeyFile", conf_get_filename(conf, CONF_keyfile)); write_setting_s(sesskey, "RemoteCommand", conf_get_str(conf, CONF_remote_cmd)); write_setting_i(sesskey, "RFCEnviron", conf_get_int(conf, CONF_rfc_environ)); write_setting_i(sesskey, "PassiveTelnet", conf_get_int(conf, CONF_passive_telnet)); write_setting_i(sesskey, "BackspaceIsDelete", conf_get_int(conf, CONF_bksp_is_delete)); write_setting_i(sesskey, "RXVTHomeEnd", conf_get_int(conf, CONF_rxvt_homeend)); write_setting_i(sesskey, "LinuxFunctionKeys", conf_get_int(conf, CONF_funky_type)); write_setting_i(sesskey, "NoApplicationKeys", conf_get_int(conf, CONF_no_applic_k)); write_setting_i(sesskey, "NoApplicationCursors", conf_get_int(conf, CONF_no_applic_c)); write_setting_i(sesskey, "NoMouseReporting", conf_get_int(conf, CONF_no_mouse_rep)); write_setting_i(sesskey, "NoRemoteResize", conf_get_int(conf, CONF_no_remote_resize)); write_setting_i(sesskey, "NoAltScreen", conf_get_int(conf, CONF_no_alt_screen)); write_setting_i(sesskey, "NoRemoteWinTitle", conf_get_int(conf, CONF_no_remote_wintitle)); write_setting_i(sesskey, "RemoteQTitleAction", conf_get_int(conf, CONF_remote_qtitle_action)); write_setting_i(sesskey, "NoDBackspace", conf_get_int(conf, CONF_no_dbackspace)); write_setting_i(sesskey, "NoRemoteCharset", conf_get_int(conf, CONF_no_remote_charset)); write_setting_i(sesskey, "ApplicationCursorKeys", conf_get_int(conf, CONF_app_cursor)); write_setting_i(sesskey, "ApplicationKeypad", conf_get_int(conf, CONF_app_keypad)); write_setting_i(sesskey, "NetHackKeypad", conf_get_int(conf, CONF_nethack_keypad)); write_setting_i(sesskey, "AltF4", conf_get_int(conf, CONF_alt_f4)); write_setting_i(sesskey, "AltSpace", conf_get_int(conf, CONF_alt_space)); write_setting_i(sesskey, "AltOnly", conf_get_int(conf, CONF_alt_only)); write_setting_i(sesskey, "ComposeKey", conf_get_int(conf, CONF_compose_key)); write_setting_i(sesskey, "CtrlAltKeys", conf_get_int(conf, CONF_ctrlaltkeys)); write_setting_i(sesskey, "TelnetKey", conf_get_int(conf, CONF_telnet_keyboard)); write_setting_i(sesskey, "TelnetRet", conf_get_int(conf, CONF_telnet_newline)); write_setting_i(sesskey, "LocalEcho", conf_get_int(conf, CONF_localecho)); write_setting_i(sesskey, "LocalEdit", conf_get_int(conf, CONF_localedit)); write_setting_s(sesskey, "Answerback", conf_get_str(conf, CONF_answerback)); write_setting_i(sesskey, "AlwaysOnTop", conf_get_int(conf, CONF_alwaysontop)); write_setting_i(sesskey, "FullScreenOnAltEnter", conf_get_int(conf, CONF_fullscreenonaltenter)); write_setting_i(sesskey, "HideMousePtr", conf_get_int(conf, CONF_hide_mouseptr)); write_setting_i(sesskey, "SunkenEdge", conf_get_int(conf, CONF_sunken_edge)); write_setting_i(sesskey, "WindowBorder", conf_get_int(conf, CONF_window_border)); write_setting_i(sesskey, "CurType", conf_get_int(conf, CONF_cursor_type)); write_setting_i(sesskey, "BlinkCur", conf_get_int(conf, CONF_blink_cur)); write_setting_i(sesskey, "Beep", conf_get_int(conf, CONF_beep)); write_setting_i(sesskey, "BeepInd", conf_get_int(conf, CONF_beep_ind)); write_setting_filename(sesskey, "BellWaveFile", conf_get_filename(conf, CONF_bell_wavefile)); write_setting_i(sesskey, "BellOverload", conf_get_int(conf, CONF_bellovl)); write_setting_i(sesskey, "BellOverloadN", conf_get_int(conf, CONF_bellovl_n)); write_setting_i(sesskey, "BellOverloadT", conf_get_int(conf, CONF_bellovl_t) #ifdef PUTTY_UNIX_H * 1000 #endif ); write_setting_i(sesskey, "BellOverloadS", conf_get_int(conf, CONF_bellovl_s) #ifdef PUTTY_UNIX_H * 1000 #endif ); write_setting_i(sesskey, "ScrollbackLines", conf_get_int(conf, CONF_savelines)); write_setting_i(sesskey, "DECOriginMode", conf_get_int(conf, CONF_dec_om)); write_setting_i(sesskey, "AutoWrapMode", conf_get_int(conf, CONF_wrap_mode)); write_setting_i(sesskey, "LFImpliesCR", conf_get_int(conf, CONF_lfhascr)); write_setting_i(sesskey, "CRImpliesLF", conf_get_int(conf, CONF_crhaslf)); write_setting_i(sesskey, "DisableArabicShaping", conf_get_int(conf, CONF_arabicshaping)); write_setting_i(sesskey, "DisableBidi", conf_get_int(conf, CONF_bidi)); write_setting_i(sesskey, "WinNameAlways", conf_get_int(conf, CONF_win_name_always)); write_setting_s(sesskey, "WinTitle", conf_get_str(conf, CONF_wintitle)); write_setting_i(sesskey, "TermWidth", conf_get_int(conf, CONF_width)); write_setting_i(sesskey, "TermHeight", conf_get_int(conf, CONF_height)); write_setting_fontspec(sesskey, "Font", conf_get_fontspec(conf, CONF_font)); write_setting_i(sesskey, "FontQuality", conf_get_int(conf, CONF_font_quality)); write_setting_i(sesskey, "FontVTMode", conf_get_int(conf, CONF_vtmode)); write_setting_i(sesskey, "UseSystemColours", conf_get_int(conf, CONF_system_colour)); write_setting_i(sesskey, "TryPalette", conf_get_int(conf, CONF_try_palette)); write_setting_i(sesskey, "ANSIColour", conf_get_int(conf, CONF_ansi_colour)); write_setting_i(sesskey, "Xterm256Colour", conf_get_int(conf, CONF_xterm_256_colour)); write_setting_i(sesskey, "BoldAsColour", conf_get_int(conf, CONF_bold_style)-1); for (i = 0; i < 22; i++) { char buf[20], buf2[30]; sprintf(buf, "Colour%d", i); sprintf(buf2, "%d,%d,%d", conf_get_int_int(conf, CONF_colours, i*3+0), conf_get_int_int(conf, CONF_colours, i*3+1), conf_get_int_int(conf, CONF_colours, i*3+2)); write_setting_s(sesskey, buf, buf2); } write_setting_i(sesskey, "RawCNP", conf_get_int(conf, CONF_rawcnp)); write_setting_i(sesskey, "PasteRTF", conf_get_int(conf, CONF_rtf_paste)); write_setting_i(sesskey, "MouseIsXterm", conf_get_int(conf, CONF_mouse_is_xterm)); write_setting_i(sesskey, "RectSelect", conf_get_int(conf, CONF_rect_select)); write_setting_i(sesskey, "MouseOverride", conf_get_int(conf, CONF_mouse_override)); for (i = 0; i < 256; i += 32) { char buf[20], buf2[256]; int j; sprintf(buf, "Wordness%d", i); *buf2 = '\0'; for (j = i; j < i + 32; j++) { sprintf(buf2 + strlen(buf2), "%s%d", (*buf2 ? "," : ""), conf_get_int_int(conf, CONF_wordness, j)); } write_setting_s(sesskey, buf, buf2); } write_setting_s(sesskey, "LineCodePage", conf_get_str(conf, CONF_line_codepage)); write_setting_i(sesskey, "CJKAmbigWide", conf_get_int(conf, CONF_cjk_ambig_wide)); write_setting_i(sesskey, "UTF8Override", conf_get_int(conf, CONF_utf8_override)); write_setting_s(sesskey, "Printer", conf_get_str(conf, CONF_printer)); write_setting_i(sesskey, "CapsLockCyr", conf_get_int(conf, CONF_xlat_capslockcyr)); write_setting_i(sesskey, "ScrollBar", conf_get_int(conf, CONF_scrollbar)); write_setting_i(sesskey, "ScrollBarFullScreen", conf_get_int(conf, CONF_scrollbar_in_fullscreen)); write_setting_i(sesskey, "ScrollOnKey", conf_get_int(conf, CONF_scroll_on_key)); write_setting_i(sesskey, "ScrollOnDisp", conf_get_int(conf, CONF_scroll_on_disp)); write_setting_i(sesskey, "EraseToScrollback", conf_get_int(conf, CONF_erase_to_scrollback)); write_setting_i(sesskey, "LockSize", conf_get_int(conf, CONF_resize_action)); write_setting_i(sesskey, "BCE", conf_get_int(conf, CONF_bce)); write_setting_i(sesskey, "BlinkText", conf_get_int(conf, CONF_blinktext)); write_setting_i(sesskey, "X11Forward", conf_get_int(conf, CONF_x11_forward)); write_setting_s(sesskey, "X11Display", conf_get_str(conf, CONF_x11_display)); write_setting_i(sesskey, "X11AuthType", conf_get_int(conf, CONF_x11_auth)); write_setting_filename(sesskey, "X11AuthFile", conf_get_filename(conf, CONF_xauthfile)); write_setting_i(sesskey, "LocalPortAcceptAll", conf_get_int(conf, CONF_lport_acceptall)); write_setting_i(sesskey, "RemotePortAcceptAll", conf_get_int(conf, CONF_rport_acceptall)); wmap(sesskey, "PortForwardings", conf, CONF_portfwd); write_setting_i(sesskey, "BugIgnore1", 2-conf_get_int(conf, CONF_sshbug_ignore1)); write_setting_i(sesskey, "BugPlainPW1", 2-conf_get_int(conf, CONF_sshbug_plainpw1)); write_setting_i(sesskey, "BugRSA1", 2-conf_get_int(conf, CONF_sshbug_rsa1)); write_setting_i(sesskey, "BugIgnore2", 2-conf_get_int(conf, CONF_sshbug_ignore2)); write_setting_i(sesskey, "BugHMAC2", 2-conf_get_int(conf, CONF_sshbug_hmac2)); write_setting_i(sesskey, "BugDeriveKey2", 2-conf_get_int(conf, CONF_sshbug_derivekey2)); write_setting_i(sesskey, "BugRSAPad2", 2-conf_get_int(conf, CONF_sshbug_rsapad2)); write_setting_i(sesskey, "BugPKSessID2", 2-conf_get_int(conf, CONF_sshbug_pksessid2)); write_setting_i(sesskey, "BugRekey2", 2-conf_get_int(conf, CONF_sshbug_rekey2)); write_setting_i(sesskey, "BugMaxPkt2", 2-conf_get_int(conf, CONF_sshbug_maxpkt2)); write_setting_i(sesskey, "BugWinadj", 2-conf_get_int(conf, CONF_sshbug_winadj)); write_setting_i(sesskey, "StampUtmp", conf_get_int(conf, CONF_stamp_utmp)); write_setting_i(sesskey, "LoginShell", conf_get_int(conf, CONF_login_shell)); write_setting_i(sesskey, "ScrollbarOnLeft", conf_get_int(conf, CONF_scrollbar_on_left)); write_setting_fontspec(sesskey, "BoldFont", conf_get_fontspec(conf, CONF_boldfont)); write_setting_fontspec(sesskey, "WideFont", conf_get_fontspec(conf, CONF_widefont)); write_setting_fontspec(sesskey, "WideBoldFont", conf_get_fontspec(conf, CONF_wideboldfont)); write_setting_i(sesskey, "ShadowBold", conf_get_int(conf, CONF_shadowbold)); write_setting_i(sesskey, "ShadowBoldOffset", conf_get_int(conf, CONF_shadowboldoffset)); write_setting_s(sesskey, "SerialLine", conf_get_str(conf, CONF_serline)); write_setting_i(sesskey, "SerialSpeed", conf_get_int(conf, CONF_serspeed)); write_setting_i(sesskey, "SerialDataBits", conf_get_int(conf, CONF_serdatabits)); write_setting_i(sesskey, "SerialStopHalfbits", conf_get_int(conf, CONF_serstopbits)); write_setting_i(sesskey, "SerialParity", conf_get_int(conf, CONF_serparity)); write_setting_i(sesskey, "SerialFlowControl", conf_get_int(conf, CONF_serflow)); write_setting_s(sesskey, "WindowClass", conf_get_str(conf, CONF_winclass)); write_setting_i(sesskey, "ConnectionSharing", conf_get_int(conf, CONF_ssh_connection_sharing)); write_setting_i(sesskey, "ConnectionSharingUpstream", conf_get_int(conf, CONF_ssh_connection_sharing_upstream)); write_setting_i(sesskey, "ConnectionSharingDownstream", conf_get_int(conf, CONF_ssh_connection_sharing_downstream)); } void load_settings(char *section, Conf *conf) { void *sesskey; sesskey = open_settings_r(section); load_open_settings(sesskey, conf); close_settings_r(sesskey); if (conf_launchable(conf)) add_session_to_jumplist(section); } void load_open_settings(void *sesskey, Conf *conf) { int i; char *prot; conf_set_int(conf, CONF_ssh_subsys, 0); /* FIXME: load this properly */ conf_set_str(conf, CONF_remote_cmd, ""); conf_set_str(conf, CONF_remote_cmd2, ""); conf_set_str(conf, CONF_ssh_nc_host, ""); gpps(sesskey, "HostName", "", conf, CONF_host); gppfile(sesskey, "LogFileName", conf, CONF_logfilename); gppi(sesskey, "LogType", 0, conf, CONF_logtype); gppi(sesskey, "LogFileClash", LGXF_ASK, conf, CONF_logxfovr); gppi(sesskey, "LogFlush", 1, conf, CONF_logflush); gppi(sesskey, "SSHLogOmitPasswords", 1, conf, CONF_logomitpass); gppi(sesskey, "SSHLogOmitData", 0, conf, CONF_logomitdata); prot = gpps_raw(sesskey, "Protocol", "default"); conf_set_int(conf, CONF_protocol, default_protocol); conf_set_int(conf, CONF_port, default_port); { const Backend *b = backend_from_name(prot); if (b) { conf_set_int(conf, CONF_protocol, b->protocol); gppi(sesskey, "PortNumber", default_port, conf, CONF_port); } } sfree(prot); /* Address family selection */ gppi(sesskey, "AddressFamily", ADDRTYPE_UNSPEC, conf, CONF_addressfamily); /* The CloseOnExit numbers are arranged in a different order from * the standard FORCE_ON / FORCE_OFF / AUTO. */ i = gppi_raw(sesskey, "CloseOnExit", 1); conf_set_int(conf, CONF_close_on_exit, (i+1)%3); gppi(sesskey, "WarnOnClose", 1, conf, CONF_warn_on_close); { /* This is two values for backward compatibility with 0.50/0.51 */ int pingmin, pingsec; pingmin = gppi_raw(sesskey, "PingInterval", 0); pingsec = gppi_raw(sesskey, "PingIntervalSecs", 0); conf_set_int(conf, CONF_ping_interval, pingmin * 60 + pingsec); } gppi(sesskey, "TCPNoDelay", 1, conf, CONF_tcp_nodelay); gppi(sesskey, "TCPKeepalives", 0, conf, CONF_tcp_keepalives); gpps(sesskey, "TerminalType", "xterm", conf, CONF_termtype); gpps(sesskey, "TerminalSpeed", "38400,38400", conf, CONF_termspeed); if (!gppmap(sesskey, "TerminalModes", conf, CONF_ttymodes)) { /* This hardcodes a big set of defaults in any new saved * sessions. Let's hope we don't change our mind. */ for (i = 0; ttymodes[i]; i++) conf_set_str_str(conf, CONF_ttymodes, ttymodes[i], "A"); } /* proxy settings */ gpps(sesskey, "ProxyExcludeList", "", conf, CONF_proxy_exclude_list); i = gppi_raw(sesskey, "ProxyDNS", 1); conf_set_int(conf, CONF_proxy_dns, (i+1)%3); gppi(sesskey, "ProxyLocalhost", 0, conf, CONF_even_proxy_localhost); gppi(sesskey, "ProxyMethod", -1, conf, CONF_proxy_type); if (conf_get_int(conf, CONF_proxy_type) == -1) { int i; i = gppi_raw(sesskey, "ProxyType", 0); if (i == 0) conf_set_int(conf, CONF_proxy_type, PROXY_NONE); else if (i == 1) conf_set_int(conf, CONF_proxy_type, PROXY_HTTP); else if (i == 3) conf_set_int(conf, CONF_proxy_type, PROXY_TELNET); else if (i == 4) conf_set_int(conf, CONF_proxy_type, PROXY_CMD); else { i = gppi_raw(sesskey, "ProxySOCKSVersion", 5); if (i == 5) conf_set_int(conf, CONF_proxy_type, PROXY_SOCKS5); else conf_set_int(conf, CONF_proxy_type, PROXY_SOCKS4); } } gpps(sesskey, "ProxyHost", "proxy", conf, CONF_proxy_host); gppi(sesskey, "ProxyPort", 80, conf, CONF_proxy_port); gpps(sesskey, "ProxyUsername", "", conf, CONF_proxy_username); gpps(sesskey, "ProxyPassword", "", conf, CONF_proxy_password); gpps(sesskey, "ProxyTelnetCommand", "connect %host %port\\n", conf, CONF_proxy_telnet_command); gppmap(sesskey, "Environment", conf, CONF_environmt); gpps(sesskey, "UserName", "", conf, CONF_username); gppi(sesskey, "UserNameFromEnvironment", 0, conf, CONF_username_from_env); gpps(sesskey, "LocalUserName", "", conf, CONF_localusername); gppi(sesskey, "NoPTY", 0, conf, CONF_nopty); gppi(sesskey, "Compression", 0, conf, CONF_compression); gppi(sesskey, "TryAgent", 1, conf, CONF_tryagent); gppi(sesskey, "AgentFwd", 0, conf, CONF_agentfwd); gppi(sesskey, "ChangeUsername", 0, conf, CONF_change_username); gppi(sesskey, "GssapiFwd", 0, conf, CONF_gssapifwd); gprefs(sesskey, "Cipher", "\0", ciphernames, CIPHER_MAX, conf, CONF_ssh_cipherlist); { /* Backward-compatibility: we used to have an option to * disable gex under the "bugs" panel after one report of * a server which offered it then choked, but we never got * a server version string or any other reports. */ char *default_kexes; i = 2 - gppi_raw(sesskey, "BugDHGEx2", 0); if (i == FORCE_ON) 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,rsa,WARN"; gprefs(sesskey, "KEX", default_kexes, kexnames, KEX_MAX, conf, CONF_ssh_kexlist); } gppi(sesskey, "RekeyTime", 60, conf, CONF_ssh_rekey_time); gpps(sesskey, "RekeyBytes", "1G", conf, CONF_ssh_rekey_data); gppi(sesskey, "SshProt", 2, conf, CONF_sshprot); gpps(sesskey, "LogHost", "", conf, CONF_loghost); gppi(sesskey, "SSH2DES", 0, conf, CONF_ssh2_des_cbc); gppi(sesskey, "SshNoAuth", 0, conf, CONF_ssh_no_userauth); gppi(sesskey, "SshBanner", 1, conf, CONF_ssh_show_banner); gppi(sesskey, "AuthTIS", 0, conf, CONF_try_tis_auth); gppi(sesskey, "AuthKI", 1, conf, CONF_try_ki_auth); gppi(sesskey, "AuthGSSAPI", 1, conf, CONF_try_gssapi_auth); #ifndef NO_GSSAPI gprefs(sesskey, "GSSLibs", "\0", gsslibkeywords, ngsslibs, conf, CONF_ssh_gsslist); gppfile(sesskey, "GSSCustom", conf, CONF_ssh_gss_custom); #endif gppi(sesskey, "SshNoShell", 0, conf, CONF_ssh_no_shell); gppfile(sesskey, "PublicKeyFile", conf, CONF_keyfile); gpps(sesskey, "RemoteCommand", "", conf, CONF_remote_cmd); gppi(sesskey, "RFCEnviron", 0, conf, CONF_rfc_environ); gppi(sesskey, "PassiveTelnet", 0, conf, CONF_passive_telnet); gppi(sesskey, "BackspaceIsDelete", 1, conf, CONF_bksp_is_delete); gppi(sesskey, "RXVTHomeEnd", 0, conf, CONF_rxvt_homeend); gppi(sesskey, "LinuxFunctionKeys", 0, conf, CONF_funky_type); gppi(sesskey, "NoApplicationKeys", 0, conf, CONF_no_applic_k); gppi(sesskey, "NoApplicationCursors", 0, conf, CONF_no_applic_c); gppi(sesskey, "NoMouseReporting", 0, conf, CONF_no_mouse_rep); gppi(sesskey, "NoRemoteResize", 0, conf, CONF_no_remote_resize); gppi(sesskey, "NoAltScreen", 0, conf, CONF_no_alt_screen); gppi(sesskey, "NoRemoteWinTitle", 0, conf, CONF_no_remote_wintitle); { /* Backward compatibility */ int no_remote_qtitle = gppi_raw(sesskey, "NoRemoteQTitle", 1); /* We deliberately interpret the old setting of "no response" as * "empty string". This changes the behaviour, but hopefully for * the better; the user can always recover the old behaviour. */ gppi(sesskey, "RemoteQTitleAction", no_remote_qtitle ? TITLE_EMPTY : TITLE_REAL, conf, CONF_remote_qtitle_action); } gppi(sesskey, "NoDBackspace", 0, conf, CONF_no_dbackspace); gppi(sesskey, "NoRemoteCharset", 0, conf, CONF_no_remote_charset); gppi(sesskey, "ApplicationCursorKeys", 0, conf, CONF_app_cursor); gppi(sesskey, "ApplicationKeypad", 0, conf, CONF_app_keypad); gppi(sesskey, "NetHackKeypad", 0, conf, CONF_nethack_keypad); gppi(sesskey, "AltF4", 1, conf, CONF_alt_f4); gppi(sesskey, "AltSpace", 0, conf, CONF_alt_space); gppi(sesskey, "AltOnly", 0, conf, CONF_alt_only); gppi(sesskey, "ComposeKey", 0, conf, CONF_compose_key); gppi(sesskey, "CtrlAltKeys", 1, conf, CONF_ctrlaltkeys); gppi(sesskey, "TelnetKey", 0, conf, CONF_telnet_keyboard); gppi(sesskey, "TelnetRet", 1, conf, CONF_telnet_newline); gppi(sesskey, "LocalEcho", AUTO, conf, CONF_localecho); gppi(sesskey, "LocalEdit", AUTO, conf, CONF_localedit); gpps(sesskey, "Answerback", "PuTTY", conf, CONF_answerback); gppi(sesskey, "AlwaysOnTop", 0, conf, CONF_alwaysontop); gppi(sesskey, "FullScreenOnAltEnter", 0, conf, CONF_fullscreenonaltenter); gppi(sesskey, "HideMousePtr", 0, conf, CONF_hide_mouseptr); gppi(sesskey, "SunkenEdge", 0, conf, CONF_sunken_edge); gppi(sesskey, "WindowBorder", 1, conf, CONF_window_border); gppi(sesskey, "CurType", 0, conf, CONF_cursor_type); gppi(sesskey, "BlinkCur", 0, conf, CONF_blink_cur); /* pedantic compiler tells me I can't use conf, CONF_beep as an int * :-) */ gppi(sesskey, "Beep", 1, conf, CONF_beep); gppi(sesskey, "BeepInd", 0, conf, CONF_beep_ind); gppfile(sesskey, "BellWaveFile", conf, CONF_bell_wavefile); gppi(sesskey, "BellOverload", 1, conf, CONF_bellovl); gppi(sesskey, "BellOverloadN", 5, conf, CONF_bellovl_n); i = gppi_raw(sesskey, "BellOverloadT", 2*TICKSPERSEC #ifdef PUTTY_UNIX_H *1000 #endif ); conf_set_int(conf, CONF_bellovl_t, i #ifdef PUTTY_UNIX_H / 1000 #endif ); i = gppi_raw(sesskey, "BellOverloadS", 5*TICKSPERSEC #ifdef PUTTY_UNIX_H *1000 #endif ); conf_set_int(conf, CONF_bellovl_s, i #ifdef PUTTY_UNIX_H / 1000 #endif ); gppi(sesskey, "ScrollbackLines", 2000, conf, CONF_savelines); gppi(sesskey, "DECOriginMode", 0, conf, CONF_dec_om); gppi(sesskey, "AutoWrapMode", 1, conf, CONF_wrap_mode); gppi(sesskey, "LFImpliesCR", 0, conf, CONF_lfhascr); gppi(sesskey, "CRImpliesLF", 0, conf, CONF_crhaslf); gppi(sesskey, "DisableArabicShaping", 0, conf, CONF_arabicshaping); gppi(sesskey, "DisableBidi", 0, conf, CONF_bidi); gppi(sesskey, "WinNameAlways", 1, conf, CONF_win_name_always); gpps(sesskey, "WinTitle", "", conf, CONF_wintitle); gppi(sesskey, "TermWidth", 80, conf, CONF_width); gppi(sesskey, "TermHeight", 24, conf, CONF_height); gppfont(sesskey, "Font", conf, CONF_font); gppi(sesskey, "FontQuality", FQ_DEFAULT, conf, CONF_font_quality); gppi(sesskey, "FontVTMode", VT_UNICODE, conf, CONF_vtmode); gppi(sesskey, "UseSystemColours", 0, conf, CONF_system_colour); gppi(sesskey, "TryPalette", 0, conf, CONF_try_palette); gppi(sesskey, "ANSIColour", 1, conf, CONF_ansi_colour); gppi(sesskey, "Xterm256Colour", 1, conf, CONF_xterm_256_colour); i = gppi_raw(sesskey, "BoldAsColour", 1); conf_set_int(conf, CONF_bold_style, i+1); for (i = 0; i < 22; i++) { static const char *const defaults[] = { "187,187,187", "255,255,255", "0,0,0", "85,85,85", "0,0,0", "0,255,0", "0,0,0", "85,85,85", "187,0,0", "255,85,85", "0,187,0", "85,255,85", "187,187,0", "255,255,85", "0,0,187", "85,85,255", "187,0,187", "255,85,255", "0,187,187", "85,255,255", "187,187,187", "255,255,255" }; char buf[20], *buf2; int c0, c1, c2; sprintf(buf, "Colour%d", i); buf2 = gpps_raw(sesskey, buf, defaults[i]); if (sscanf(buf2, "%d,%d,%d", &c0, &c1, &c2) == 3) { conf_set_int_int(conf, CONF_colours, i*3+0, c0); conf_set_int_int(conf, CONF_colours, i*3+1, c1); conf_set_int_int(conf, CONF_colours, i*3+2, c2); } sfree(buf2); } gppi(sesskey, "RawCNP", 0, conf, CONF_rawcnp); gppi(sesskey, "PasteRTF", 0, conf, CONF_rtf_paste); gppi(sesskey, "MouseIsXterm", 0, conf, CONF_mouse_is_xterm); gppi(sesskey, "RectSelect", 0, conf, CONF_rect_select); gppi(sesskey, "MouseOverride", 1, conf, CONF_mouse_override); for (i = 0; i < 256; i += 32) { static const char *const defaults[] = { "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", "0,1,2,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,1,1,1,1,1,1", "1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,1,1,1,2", "1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,1,1,1,1", "1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1", "1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1", "2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,2,2,2,2,2,2,2,2", "2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,2,2,2,2,2,2,2,2" }; char buf[20], *buf2, *p; int j; sprintf(buf, "Wordness%d", i); buf2 = gpps_raw(sesskey, buf, defaults[i / 32]); p = buf2; for (j = i; j < i + 32; j++) { char *q = p; while (*p && *p != ',') p++; if (*p == ',') *p++ = '\0'; conf_set_int_int(conf, CONF_wordness, j, atoi(q)); } sfree(buf2); } /* * The empty default for LineCodePage will be converted later * into a plausible default for the locale. */ gpps(sesskey, "LineCodePage", "", conf, CONF_line_codepage); gppi(sesskey, "CJKAmbigWide", 0, conf, CONF_cjk_ambig_wide); gppi(sesskey, "UTF8Override", 1, conf, CONF_utf8_override); gpps(sesskey, "Printer", "", conf, CONF_printer); gppi(sesskey, "CapsLockCyr", 0, conf, CONF_xlat_capslockcyr); gppi(sesskey, "ScrollBar", 1, conf, CONF_scrollbar); gppi(sesskey, "ScrollBarFullScreen", 0, conf, CONF_scrollbar_in_fullscreen); gppi(sesskey, "ScrollOnKey", 0, conf, CONF_scroll_on_key); gppi(sesskey, "ScrollOnDisp", 1, conf, CONF_scroll_on_disp); gppi(sesskey, "EraseToScrollback", 1, conf, CONF_erase_to_scrollback); gppi(sesskey, "LockSize", 0, conf, CONF_resize_action); gppi(sesskey, "BCE", 1, conf, CONF_bce); gppi(sesskey, "BlinkText", 0, conf, CONF_blinktext); gppi(sesskey, "X11Forward", 0, conf, CONF_x11_forward); gpps(sesskey, "X11Display", "", conf, CONF_x11_display); gppi(sesskey, "X11AuthType", X11_MIT, conf, CONF_x11_auth); gppfile(sesskey, "X11AuthFile", conf, CONF_xauthfile); gppi(sesskey, "LocalPortAcceptAll", 0, conf, CONF_lport_acceptall); gppi(sesskey, "RemotePortAcceptAll", 0, conf, CONF_rport_acceptall); gppmap(sesskey, "PortForwardings", conf, CONF_portfwd); i = gppi_raw(sesskey, "BugIgnore1", 0); conf_set_int(conf, CONF_sshbug_ignore1, 2-i); i = gppi_raw(sesskey, "BugPlainPW1", 0); conf_set_int(conf, CONF_sshbug_plainpw1, 2-i); i = gppi_raw(sesskey, "BugRSA1", 0); conf_set_int(conf, CONF_sshbug_rsa1, 2-i); i = gppi_raw(sesskey, "BugIgnore2", 0); conf_set_int(conf, CONF_sshbug_ignore2, 2-i); { int i; i = gppi_raw(sesskey, "BugHMAC2", 0); conf_set_int(conf, CONF_sshbug_hmac2, 2-i); if (2-i == AUTO) { i = gppi_raw(sesskey, "BuggyMAC", 0); if (i == 1) conf_set_int(conf, CONF_sshbug_hmac2, FORCE_ON); } } i = gppi_raw(sesskey, "BugDeriveKey2", 0); conf_set_int(conf, CONF_sshbug_derivekey2, 2-i); i = gppi_raw(sesskey, "BugRSAPad2", 0); conf_set_int(conf, CONF_sshbug_rsapad2, 2-i); i = gppi_raw(sesskey, "BugPKSessID2", 0); conf_set_int(conf, CONF_sshbug_pksessid2, 2-i); i = gppi_raw(sesskey, "BugRekey2", 0); conf_set_int(conf, CONF_sshbug_rekey2, 2-i); i = gppi_raw(sesskey, "BugMaxPkt2", 0); conf_set_int(conf, CONF_sshbug_maxpkt2, 2-i); i = gppi_raw(sesskey, "BugWinadj", 0); conf_set_int(conf, CONF_sshbug_winadj, 2-i); conf_set_int(conf, CONF_ssh_simple, FALSE); gppi(sesskey, "StampUtmp", 1, conf, CONF_stamp_utmp); gppi(sesskey, "LoginShell", 1, conf, CONF_login_shell); gppi(sesskey, "ScrollbarOnLeft", 0, conf, CONF_scrollbar_on_left); gppi(sesskey, "ShadowBold", 0, conf, CONF_shadowbold); gppfont(sesskey, "BoldFont", conf, CONF_boldfont); gppfont(sesskey, "WideFont", conf, CONF_widefont); gppfont(sesskey, "WideBoldFont", conf, CONF_wideboldfont); gppi(sesskey, "ShadowBoldOffset", 1, conf, CONF_shadowboldoffset); gpps(sesskey, "SerialLine", "", conf, CONF_serline); gppi(sesskey, "SerialSpeed", 9600, conf, CONF_serspeed); gppi(sesskey, "SerialDataBits", 8, conf, CONF_serdatabits); gppi(sesskey, "SerialStopHalfbits", 2, conf, CONF_serstopbits); gppi(sesskey, "SerialParity", SER_PAR_NONE, conf, CONF_serparity); gppi(sesskey, "SerialFlowControl", SER_FLOW_XONXOFF, conf, CONF_serflow); gpps(sesskey, "WindowClass", "", conf, CONF_winclass); gppi(sesskey, "ConnectionSharing", 0, conf, CONF_ssh_connection_sharing); gppi(sesskey, "ConnectionSharingUpstream", 1, conf, CONF_ssh_connection_sharing_upstream); gppi(sesskey, "ConnectionSharingDownstream", 1, conf, CONF_ssh_connection_sharing_downstream); } void do_defaults(char *session, Conf *conf) { load_settings(session, conf); } static int sessioncmp(const void *av, const void *bv) { const char *a = *(const char *const *) av; const char *b = *(const char *const *) bv; |
︙ | ︙ |
Changes to sftp.c.
︙ | ︙ | |||
40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 | pkt->length += len; sftp_pkt_ensure(pkt, pkt->length); memcpy(pkt->data + pkt->length - len, data, len); } static void sftp_pkt_addbyte(struct sftp_packet *pkt, unsigned char byte) { sftp_pkt_adddata(pkt, &byte, 1); } static struct sftp_packet *sftp_pkt_init(int pkt_type) { struct sftp_packet *pkt; pkt = snew(struct sftp_packet); pkt->data = NULL; pkt->savedpos = -1; pkt->length = 0; pkt->maxlen = 0; sftp_pkt_addbyte(pkt, (unsigned char) pkt_type); return pkt; } /* static void sftp_pkt_addbool(struct sftp_packet *pkt, unsigned char value) { sftp_pkt_adddata(pkt, &value, 1); } */ | > > > > > > > > < < < < < < < | 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 | pkt->length += len; sftp_pkt_ensure(pkt, pkt->length); memcpy(pkt->data + pkt->length - len, data, len); } static void sftp_pkt_addbyte(struct sftp_packet *pkt, unsigned char byte) { sftp_pkt_adddata(pkt, &byte, 1); } static void sftp_pkt_adduint32(struct sftp_packet *pkt, unsigned long value) { unsigned char x[4]; PUT_32BIT(x, value); sftp_pkt_adddata(pkt, x, 4); } static struct sftp_packet *sftp_pkt_init(int pkt_type) { struct sftp_packet *pkt; pkt = snew(struct sftp_packet); pkt->data = NULL; pkt->savedpos = -1; pkt->length = 0; pkt->maxlen = 0; sftp_pkt_adduint32(pkt, 0); /* length field will be filled in later */ sftp_pkt_addbyte(pkt, (unsigned char) pkt_type); return pkt; } /* static void sftp_pkt_addbool(struct sftp_packet *pkt, unsigned char value) { sftp_pkt_adddata(pkt, &value, 1); } */ static void sftp_pkt_adduint64(struct sftp_packet *pkt, uint64 value) { unsigned char x[8]; PUT_32BIT(x, value.hi); PUT_32BIT(x + 4, value.lo); sftp_pkt_adddata(pkt, x, 8); } |
︙ | ︙ | |||
145 146 147 148 149 150 151 | } static int sftp_pkt_getstring(struct sftp_packet *pkt, char **p, int *length) { *p = NULL; if (pkt->length - pkt->savedpos < 4) return 0; | | | 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 | } static int sftp_pkt_getstring(struct sftp_packet *pkt, char **p, int *length) { *p = NULL; if (pkt->length - pkt->savedpos < 4) return 0; *length = toint(GET_32BIT(pkt->data + pkt->savedpos)); pkt->savedpos += 4; if ((int)(pkt->length - pkt->savedpos) < *length || *length < 0) { *length = 0; return 0; } *p = pkt->data + pkt->savedpos; pkt->savedpos += *length; |
︙ | ︙ | |||
211 212 213 214 215 216 217 | /* ---------------------------------------------------------------------- * Send and receive packet functions. */ int sftp_send(struct sftp_packet *pkt) { int ret; | < | | | 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 | /* ---------------------------------------------------------------------- * Send and receive packet functions. */ int sftp_send(struct sftp_packet *pkt) { int ret; PUT_32BIT(pkt->data, pkt->length - 4); ret = sftp_senddata(pkt->data, pkt->length); sftp_pkt_free(pkt); return ret; } struct sftp_packet *sftp_recv(void) { struct sftp_packet *pkt; char x[4]; |
︙ | ︙ | |||
362 363 364 365 366 367 368 | fxp_internal_error("did not receive a valid SFTP packet\n"); return NULL; } req = find234(sftp_requests, &id, sftp_reqfind); if (!req || !req->registered) { fxp_internal_error("request ID mismatch\n"); | < | 362 363 364 365 366 367 368 369 370 371 372 373 374 375 | fxp_internal_error("did not receive a valid SFTP packet\n"); return NULL; } req = find234(sftp_requests, &id, sftp_reqfind); if (!req || !req->registered) { fxp_internal_error("request ID mismatch\n"); return NULL; } del234(sftp_requests, req); return req; } |
︙ | ︙ | |||
544 545 546 547 548 549 550 | return NULL; } } /* * Open a file. */ | | > > > > | | 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 | return NULL; } } /* * Open a file. */ struct sftp_request *fxp_open_send(char *path, int type, struct fxp_attrs *attrs) { struct sftp_request *req = sftp_alloc_request(); struct sftp_packet *pktout; pktout = sftp_pkt_init(SSH_FXP_OPEN); sftp_pkt_adduint32(pktout, req->id); sftp_pkt_addstring(pktout, path); sftp_pkt_adduint32(pktout, type); if (attrs) sftp_pkt_addattrs(pktout, *attrs); else sftp_pkt_adduint32(pktout, 0); /* empty ATTRS structure */ sftp_send(pktout); return req; } struct fxp_handle *fxp_open_recv(struct sftp_packet *pktin, struct sftp_request *req) |
︙ | ︙ | |||
1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 | xfer->eof = FALSE; xfer_download_queue(xfer); return xfer; } int xfer_download_gotpkt(struct fxp_xfer *xfer, struct sftp_packet *pktin) { struct sftp_request *rreq; struct req *rr; rreq = sftp_find_request(pktin); rr = (struct req *)fxp_get_userdata(rreq); | > > > > > > | > | > | 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 | xfer->eof = FALSE; xfer_download_queue(xfer); return xfer; } /* * Returns INT_MIN to indicate that it didn't even get as far as * fxp_read_recv and hence has not freed pktin. */ int xfer_download_gotpkt(struct fxp_xfer *xfer, struct sftp_packet *pktin) { struct sftp_request *rreq; struct req *rr; rreq = sftp_find_request(pktin); if (!rreq) return INT_MIN; /* this packet doesn't even make sense */ rr = (struct req *)fxp_get_userdata(rreq); if (!rr) { fxp_internal_error("request ID is not part of the current download"); return INT_MIN; /* this packet isn't ours */ } rr->retlen = fxp_read_recv(pktin, rreq, rr->buffer, rr->len); #ifdef DEBUG_DOWNLOAD printf("read request %p has returned [%d]\n", rr, rr->retlen); #endif if ((rr->retlen < 0 && fxp_error_type()==SSH_FX_EOF) || rr->retlen == 0) { xfer->eof = TRUE; |
︙ | ︙ | |||
1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 | xfer->req_totalsize += rr->len; #ifdef DEBUG_UPLOAD { char buf[40]; uint64_decimal(rr->offset, buf); printf("queueing write request %p at %s [len %d]\n", rr, buf, len); } #endif } int xfer_upload_gotpkt(struct fxp_xfer *xfer, struct sftp_packet *pktin) { struct sftp_request *rreq; struct req *rr, *prev, *next; int ret; rreq = sftp_find_request(pktin); rr = (struct req *)fxp_get_userdata(rreq); | > > > > > > | > | > | 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 | xfer->req_totalsize += rr->len; #ifdef DEBUG_UPLOAD { char buf[40]; uint64_decimal(rr->offset, buf); printf("queueing write request %p at %s [len %d]\n", rr, buf, len); } #endif } /* * Returns INT_MIN to indicate that it didn't even get as far as * fxp_write_recv and hence has not freed pktin. */ int xfer_upload_gotpkt(struct fxp_xfer *xfer, struct sftp_packet *pktin) { struct sftp_request *rreq; struct req *rr, *prev, *next; int ret; rreq = sftp_find_request(pktin); if (!rreq) return INT_MIN; /* this packet doesn't even make sense */ rr = (struct req *)fxp_get_userdata(rreq); if (!rr) { fxp_internal_error("request ID is not part of the current upload"); return INT_MIN; /* this packet isn't ours */ } ret = fxp_write_recv(pktin, rreq); #ifdef DEBUG_UPLOAD printf("write request %p has returned [%d]\n", rr, ret); #endif /* * Remove this one from the queue. |
︙ | ︙ |
Changes to sftp.h.
︙ | ︙ | |||
78 79 80 81 82 83 84 85 86 87 88 89 90 91 | unsigned long uid; unsigned long gid; unsigned long permissions; unsigned long atime; unsigned long mtime; }; struct fxp_handle { char *hstring; int hlen; }; struct fxp_name { char *filename, *longname; | > > > > > > > > > > > > > | 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 | unsigned long uid; unsigned long gid; unsigned long permissions; unsigned long atime; unsigned long mtime; }; /* * Copy between the possibly-unused permissions field in an fxp_attrs * and a possibly-negative integer containing the same permissions. */ #define PUT_PERMISSIONS(attrs, perms) \ ((perms) >= 0 ? \ ((attrs).flags |= SSH_FILEXFER_ATTR_PERMISSIONS, \ (attrs).permissions = (perms)) : \ ((attrs).flags &= ~SSH_FILEXFER_ATTR_PERMISSIONS)) #define GET_PERMISSIONS(attrs) \ ((attrs).flags & SSH_FILEXFER_ATTR_PERMISSIONS ? \ (attrs).permissions : -1) struct fxp_handle { char *hstring; int hlen; }; struct fxp_name { char *filename, *longname; |
︙ | ︙ | |||
112 113 114 115 116 117 118 | * Canonify a pathname. Concatenate the two given path elements * with a separating slash, unless the second is NULL. */ struct sftp_request *fxp_realpath_send(char *path); char *fxp_realpath_recv(struct sftp_packet *pktin, struct sftp_request *req); /* | | > | > | 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 | * Canonify a pathname. Concatenate the two given path elements * with a separating slash, unless the second is NULL. */ struct sftp_request *fxp_realpath_send(char *path); char *fxp_realpath_recv(struct sftp_packet *pktin, struct sftp_request *req); /* * Open a file. 'attrs' contains attributes to be applied to the file * if it's being created. */ struct sftp_request *fxp_open_send(char *path, int type, struct fxp_attrs *attrs); struct fxp_handle *fxp_open_recv(struct sftp_packet *pktin, struct sftp_request *req); /* * Open a directory. */ struct sftp_request *fxp_opendir_send(char *path); |
︙ | ︙ |
Changes to ssh.c.
︙ | ︙ | |||
13 14 15 16 17 18 19 | #include "tree234.h" #include "ssh.h" #ifndef NO_GSSAPI #include "sshgssc.h" #include "sshgss.h" #endif | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < > | 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 | #include "tree234.h" #include "ssh.h" #ifndef NO_GSSAPI #include "sshgssc.h" #include "sshgss.h" #endif #ifndef FALSE #define FALSE 0 #endif #ifndef TRUE #define TRUE 1 #endif /* * Packet type contexts, so that ssh2_pkt_type can correctly decode * the ambiguous type numbers back into the correct type strings. */ 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_GSSAPI, SSH2_PKTCTX_KBDINTER } Pkt_ACtx; static const char *const ssh2_disconnect_reasons[] = { NULL, "host not allowed to connect", "protocol error", "key exchange failed", "host authentication failed", "MAC error", "compression error", "service not available", "protocol version not supported", "host key not verifiable", "connection lost", "by application", "too many connections", "auth cancelled by user", "no more auth methods available", "illegal user name", }; /* * Various remote-bug flags. */ #define BUG_CHOKES_ON_SSH1_IGNORE 1 #define BUG_SSH2_HMAC 2 #define BUG_NEEDS_SSH1_PLAIN_PASSWORD 4 #define BUG_CHOKES_ON_RSA 8 #define BUG_SSH2_RSA_PADDING 16 #define BUG_SSH2_DERIVEKEY 32 #define BUG_SSH2_REKEY 64 #define BUG_SSH2_PK_SESSIONID 128 #define BUG_SSH2_MAXPKT 256 #define BUG_CHOKES_ON_SSH2_IGNORE 512 #define BUG_CHOKES_ON_WINADJ 1024 /* * Codes for terminal modes. * Most of these are the same in SSH-1 and SSH-2. * This list is derived from RFC 4254 and * SSH-1 RFC-1.2.31. */ |
︙ | ︙ | |||
411 412 413 414 415 416 417 | } #undef translate #undef translatec /* Enumeration values for fields in SSH-1 packets */ enum { PKT_END, PKT_INT, PKT_CHAR, PKT_DATA, PKT_STR, PKT_BIGNUM, | < < < | 279 280 281 282 283 284 285 286 287 288 289 290 291 292 | } #undef translate #undef translatec /* Enumeration values for fields in SSH-1 packets */ enum { PKT_END, PKT_INT, PKT_CHAR, PKT_DATA, PKT_STR, PKT_BIGNUM, }; /* * Coroutine mechanics for the sillier bits of the code. If these * macros look impenetrable to you, you might find it helpful to * read * |
︙ | ︙ | |||
437 438 439 440 441 442 443 | * - right-click ssh.c in the FileView * - click Settings * - select the C/C++ tab and the General category * - under `Debug info:', select anything _other_ than `Program * Database for Edit and Continue'. */ #define crBegin(v) { int *crLine = &v; switch(v) { case 0:; | > | | | | > > > < < < < < | | | | > > | 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 | * - right-click ssh.c in the FileView * - click Settings * - select the C/C++ tab and the General category * - under `Debug info:', select anything _other_ than `Program * Database for Edit and Continue'. */ #define crBegin(v) { int *crLine = &v; switch(v) { case 0:; #define crBeginState crBegin(s->crLine) #define crStateP(t, v) \ struct t *s; \ if (!(v)) { s = (v) = snew(struct t); s->crLine = 0; } \ s = (v); #define crState(t) crStateP(t, ssh->t) #define crFinish(z) } *crLine = 0; return (z); } #define crFinishV } *crLine = 0; return; } #define crFinishFree(z) } sfree(s); return (z); } #define crFinishFreeV } sfree(s); return; } #define crReturn(z) \ do {\ *crLine =__LINE__; return (z); case __LINE__:;\ } while (0) #define crReturnV \ do {\ *crLine=__LINE__; return; case __LINE__:;\ } while (0) #define crStop(z) do{ *crLine = 0; return (z); }while(0) #define crStopV do{ *crLine = 0; return; }while(0) #define crWaitUntil(c) do { crReturn(0); } while (!(c)) #define crWaitUntilV(c) do { crReturnV; } while (!(c)) struct Packet; static struct Packet *ssh1_pkt_init(int pkt_type); static struct Packet *ssh2_pkt_init(int pkt_type); static void ssh_pkt_ensure(struct Packet *, int length); static void ssh_pkt_adddata(struct Packet *, const void *data, int len); static void ssh_pkt_addbyte(struct Packet *, unsigned char value); static void ssh2_pkt_addbool(struct Packet *, unsigned char value); static void ssh_pkt_adduint32(struct Packet *, unsigned long value); static void ssh_pkt_addstring_start(struct Packet *); static void ssh_pkt_addstring_str(struct Packet *, const char *data); static void ssh_pkt_addstring_data(struct Packet *, const char *data, int len); static void ssh_pkt_addstring(struct Packet *, const char *data); static unsigned char *ssh2_mpint_fmt(Bignum b, int *len); static void ssh1_pkt_addmp(struct Packet *, Bignum b); static void ssh2_pkt_addmp(struct Packet *, Bignum b); static int ssh2_pkt_construct(Ssh, struct Packet *); static void ssh2_pkt_send(Ssh, struct Packet *); static void ssh2_pkt_send_noqueue(Ssh, struct Packet *); static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen, struct Packet *pktin); static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, struct Packet *pktin); static void ssh2_channel_check_close(struct ssh_channel *c); static void ssh_channel_destroy(struct ssh_channel *c); /* * Buffer management constants. There are several of these for * various different purposes: * * - SSH1_BUFFER_LIMIT is the amount of backlog that must build up * on a local data stream before we throttle the whole SSH |
︙ | ︙ | |||
526 527 528 529 530 531 532 | #define SSH1_BUFFER_LIMIT 32768 #define SSH_MAX_BACKLOG 32768 #define OUR_V2_WINSIZE 16384 #define OUR_V2_BIGWIN 0x7fffffff #define OUR_V2_MAXPKT 0x4000UL #define OUR_V2_PACKETLIMIT 0x9000UL | < < < | | 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 | #define SSH1_BUFFER_LIMIT 32768 #define SSH_MAX_BACKLOG 32768 #define OUR_V2_WINSIZE 16384 #define OUR_V2_BIGWIN 0x7fffffff #define OUR_V2_MAXPKT 0x4000UL #define OUR_V2_PACKETLIMIT 0x9000UL const static struct ssh_signkey *hostkey_algs[] = { &ssh_rsa, &ssh_dss }; const static struct ssh_mac *macs[] = { &ssh_hmac_sha256, &ssh_hmac_sha1, &ssh_hmac_sha1_96, &ssh_hmac_md5 }; const static struct ssh_mac *buggymacs[] = { &ssh_hmac_sha1_buggy, &ssh_hmac_sha1_96_buggy, &ssh_hmac_md5 }; static void *ssh_comp_none_init(void) { |
︙ | ︙ | |||
570 571 572 573 574 575 576 | }; enum { /* channel types */ CHAN_MAINSESSION, CHAN_X11, CHAN_AGENT, CHAN_SOCKDATA, | | > > > > > > > > > > > > > > > > > > > > | > | | | > > > > > > > > > > > > > > > > > > | | | | | | | | | > > | | > > > | 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 | }; enum { /* channel types */ CHAN_MAINSESSION, CHAN_X11, CHAN_AGENT, CHAN_SOCKDATA, CHAN_SOCKDATA_DORMANT, /* one the remote hasn't confirmed */ /* * CHAN_SHARING indicates a channel which is tracked here on * behalf of a connection-sharing downstream. We do almost nothing * with these channels ourselves: all messages relating to them * get thrown straight to sshshare.c and passed on almost * unmodified to downstream. */ CHAN_SHARING, /* * CHAN_ZOMBIE is used to indicate a channel for which we've * already destroyed the local data source: for instance, if a * forwarded port experiences a socket error on the local side, we * immediately destroy its local socket and turn the SSH channel * into CHAN_ZOMBIE. */ CHAN_ZOMBIE }; typedef void (*handler_fn_t)(Ssh ssh, struct Packet *pktin); typedef void (*chandler_fn_t)(Ssh ssh, struct Packet *pktin, void *ctx); typedef void (*cchandler_fn_t)(struct ssh_channel *, struct Packet *, void *); /* * Each channel has a queue of outstanding CHANNEL_REQUESTS and their * handlers. */ struct outstanding_channel_request { cchandler_fn_t handler; void *ctx; struct outstanding_channel_request *next; }; /* * 2-3-4 tree storing channels. */ struct ssh_channel { Ssh ssh; /* pointer back to main context */ unsigned remoteid, localid; int type; /* True if we opened this channel but server hasn't confirmed. */ int halfopen; /* * In SSH-1, this value contains four bits: * * 1 We have sent SSH1_MSG_CHANNEL_CLOSE. * 2 We have sent SSH1_MSG_CHANNEL_CLOSE_CONFIRMATION. * 4 We have received SSH1_MSG_CHANNEL_CLOSE. * 8 We have received SSH1_MSG_CHANNEL_CLOSE_CONFIRMATION. * * A channel is completely finished with when all four bits are set. * * In SSH-2, the four bits mean: * * 1 We have sent SSH2_MSG_CHANNEL_EOF. * 2 We have sent SSH2_MSG_CHANNEL_CLOSE. * 4 We have received SSH2_MSG_CHANNEL_EOF. * 8 We have received SSH2_MSG_CHANNEL_CLOSE. * * A channel is completely finished with when we have both sent * and received CLOSE. * * The symbolic constants below use the SSH-2 terminology, which * is a bit confusing in SSH-1, but we have to use _something_. */ #define CLOSES_SENT_EOF 1 #define CLOSES_SENT_CLOSE 2 #define CLOSES_RCVD_EOF 4 #define CLOSES_RCVD_CLOSE 8 int closes; /* * This flag indicates that an EOF is pending on the outgoing side * of the channel: that is, wherever we're getting the data for * this channel has sent us some data followed by EOF. We can't * actually send the EOF until we've finished sending the data, so * we set this flag instead to remind us to do so once our buffer * is clear. */ int pending_eof; /* * True if this channel is causing the underlying connection to be * throttled. */ int throttling_conn; union { struct ssh2_data_channel { bufchain outbuffer; 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 channel requests that haven't * been acked. */ struct outstanding_channel_request *chanreq_head, *chanreq_tail; enum { THROTTLED, UNTHROTTLING, UNTHROTTLED } throttle_state; } v2; } v; union { struct ssh_agent_channel { unsigned char *message; unsigned char msglen[4]; unsigned lensofar, totallen; int outstanding_requests; } a; struct ssh_x11_channel { struct X11Connection *xconn; int initial; } x11; struct ssh_pfd_channel { struct PortForwarding *pf; } pfd; struct ssh_sharing_channel { void *ctx; } sharing; } u; }; /* * 2-3-4 tree storing remote->local port forwardings. SSH-1 and SSH-2 * use this structure in different ways, reflecting SSH-2's * altogether saner approach to port forwarding. |
︙ | ︙ | |||
683 684 685 686 687 688 689 | * Hence, in SSH-1 this structure is indexed by destination * host:port pair, whereas in SSH-2 it is indexed by source port. */ struct ssh_portfwd; /* forward declaration */ struct ssh_rportfwd { unsigned sport, dport; | | > > | > > | > > > > > | | | > > > > > | > > > > > > > > > > > > > | < > > > > > | > > > > | | | > | > > > > | > | 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 | * Hence, in SSH-1 this structure is indexed by destination * host:port pair, whereas in SSH-2 it is indexed by source port. */ struct ssh_portfwd; /* forward declaration */ struct ssh_rportfwd { unsigned sport, dport; char *shost, *dhost; char *sportdesc; void *share_ctx; struct ssh_portfwd *pfrec; }; static void free_rportfwd(struct ssh_rportfwd *pf) { if (pf) { sfree(pf->sportdesc); sfree(pf->shost); sfree(pf->dhost); sfree(pf); } } /* * Separately to the rportfwd tree (which is for looking up port * open requests from the server), a tree of _these_ structures is * used to keep track of all the currently open port forwardings, * so that we can reconfigure in mid-session if the user requests * it. */ struct ssh_portfwd { enum { DESTROY, KEEP, CREATE } status; int type; unsigned sport, dport; char *saddr, *daddr; char *sserv, *dserv; struct ssh_rportfwd *remote; int addressfamily; struct PortListener *local; }; #define free_portfwd(pf) ( \ ((pf) ? (sfree((pf)->saddr), sfree((pf)->daddr), \ sfree((pf)->sserv), sfree((pf)->dserv)) : (void)0 ), sfree(pf) ) struct Packet { long length; /* length of packet: see below */ long forcepad; /* SSH-2: force padding to at least this length */ int type; /* only used for incoming packets */ unsigned long sequence; /* SSH-2 incoming sequence number */ unsigned char *data; /* allocated storage */ unsigned char *body; /* offset of payload within `data' */ long savedpos; /* dual-purpose saved packet position: see below */ long maxlen; /* amount of storage allocated for `data' */ long encrypted_len; /* for SSH-2 total-size counting */ /* * A note on the 'length' and 'savedpos' fields above. * * Incoming packets are set up so that pkt->length is measured * relative to pkt->body, which itself points to a few bytes after * pkt->data (skipping some uninteresting header fields including * the packet type code). The ssh_pkt_get* functions all expect * this setup, and they also use pkt->savedpos to indicate how far * through the packet being decoded they've got - and that, too, * is an offset from pkt->body rather than pkt->data. * * During construction of an outgoing packet, however, pkt->length * is measured relative to the base pointer pkt->data, and * pkt->body is not really used for anything until the packet is * ready for sending. In this mode, pkt->savedpos is reused as a * temporary variable by the addstring functions, which write out * a string length field and then keep going back and updating it * as more data is appended to the subsequent string data field; * pkt->savedpos stores the offset (again relative to pkt->data) * of the start of the string data field. */ /* Extra metadata used in SSH packet logging mode, allowing us to * log in the packet header line that the packet came from a * connection-sharing downstream and what if anything unusual was * done to it. The additional_log_text field is expected to be a * static string - it will not be freed. */ unsigned downstream_id; const char *additional_log_text; }; static void ssh1_protocol(Ssh ssh, void *vin, int inlen, struct Packet *pktin); static void ssh2_protocol(Ssh ssh, void *vin, int inlen, struct Packet *pktin); static void ssh2_bare_connection_protocol(Ssh ssh, void *vin, int inlen, struct Packet *pktin); static void ssh1_protocol_setup(Ssh ssh); static void ssh2_protocol_setup(Ssh ssh); static void ssh2_bare_connection_protocol_setup(Ssh ssh); static void ssh_size(void *handle, int width, int height); 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, 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); static int ssh2_pkt_getbool(struct Packet *pkt); static void ssh_pkt_getstring(struct Packet *pkt, char **p, int *length); static void ssh2_timer(void *ctx, unsigned long now); static void do_ssh2_transport(Ssh ssh, void *vin, int inlen, struct Packet *pktin); static void ssh2_msg_unexpected(Ssh ssh, struct Packet *pktin); struct rdpkt1_state_tag { long len, pad, biglen, to_read; unsigned long realcrc, gotcrc; unsigned char *p; int i; int chunk; struct Packet *pktin; }; struct rdpkt2_state_tag { long len, pad, payload, packetlen, maclen; int i; int cipherblk; unsigned long incoming_sequence; struct Packet *pktin; }; struct rdpkt2_bare_state_tag { char length[4]; long packetlen; int i; unsigned long incoming_sequence; struct Packet *pktin; }; struct queued_handler; struct queued_handler { int msg1, msg2; chandler_fn_t handler; void *ctx; struct queued_handler *next; |
︙ | ︙ | |||
809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 | void *cs_cipher_ctx, *sc_cipher_ctx; const struct ssh_mac *csmac, *scmac; void *cs_mac_ctx, *sc_mac_ctx; const struct ssh_compress *cscomp, *sccomp; void *cs_comp_ctx, *sc_comp_ctx; const struct ssh_kex *kex; const struct ssh_signkey *hostkey; unsigned char v2_session_id[SSH2_KEX_MAX_HASH_LEN]; int v2_session_id_len; void *kex_ctx; char *savedhost; int savedport; int send_ok; int echoing, editing; void *frontend; | > > > > > | 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 | void *cs_cipher_ctx, *sc_cipher_ctx; const struct ssh_mac *csmac, *scmac; void *cs_mac_ctx, *sc_mac_ctx; const struct ssh_compress *cscomp, *sccomp; void *cs_comp_ctx, *sc_comp_ctx; const struct ssh_kex *kex; const struct ssh_signkey *hostkey; char *hostkey_str; /* string representation, for easy checking in rekeys */ unsigned char v2_session_id[SSH2_KEX_MAX_HASH_LEN]; int v2_session_id_len; void *kex_ctx; int bare_connection; int attempting_connshare; void *connshare; char *savedhost; int savedport; int send_ok; int echoing, editing; void *frontend; |
︙ | ︙ | |||
841 842 843 844 845 846 847 848 849 850 851 852 853 854 | SSH_STATE_BEFORE_SIZE, SSH_STATE_INTERMED, SSH_STATE_SESSION, SSH_STATE_CLOSED } state; int size_needed, eof_needed; struct Packet **queue; int queuelen, queuesize; int queueing; unsigned char *deferred_send_data; int deferred_len, deferred_size; | > > | 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 | SSH_STATE_BEFORE_SIZE, SSH_STATE_INTERMED, SSH_STATE_SESSION, SSH_STATE_CLOSED } state; int size_needed, eof_needed; int sent_console_eof; int got_pty; /* affects EOF behaviour on main channel */ struct Packet **queue; int queuelen, queuesize; int queueing; unsigned char *deferred_send_data; int deferred_len, deferred_size; |
︙ | ︙ | |||
862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 | bufchain banner; /* accumulates banners during do_ssh2_authconn */ Pkt_KCtx pkt_kctx; Pkt_ACtx pkt_actx; struct X11Display *x11disp; int version; int conn_throttle_count; int overall_bufsize; int throttled_all; int v1_stdout_throttling; unsigned long v2_outgoing_sequence; int ssh1_rdpkt_crstate; int ssh2_rdpkt_crstate; | > > | < < < > > > | < | | > | > > > > > > > > > > > > > > | 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 | bufchain banner; /* accumulates banners during do_ssh2_authconn */ Pkt_KCtx pkt_kctx; Pkt_ACtx pkt_actx; struct X11Display *x11disp; struct X11FakeAuth *x11auth; tree234 *x11authtree; int version; int conn_throttle_count; int overall_bufsize; int throttled_all; int v1_stdout_throttling; unsigned long v2_outgoing_sequence; int ssh1_rdpkt_crstate; int ssh2_rdpkt_crstate; int ssh2_bare_rdpkt_crstate; int ssh_gotdata_crstate; int do_ssh1_connection_crstate; void *do_ssh_init_state; void *do_ssh1_login_state; void *do_ssh2_transport_state; void *do_ssh2_authconn_state; void *do_ssh_connection_init_state; struct rdpkt1_state_tag rdpkt1_state; struct rdpkt2_state_tag rdpkt2_state; struct rdpkt2_bare_state_tag rdpkt2_bare_state; /* SSH-1 and SSH-2 use this for different things, but both use it */ int protocol_initial_phase_done; void (*protocol) (Ssh ssh, void *vin, int inlen, struct Packet *pkt); struct Packet *(*s_rdpkt) (Ssh ssh, unsigned char **data, int *datalen); int (*do_ssh_init)(Ssh ssh, unsigned char c); /* * We maintain our own copy of a Conf structure here. That way, * when we're passed a new one for reconfiguration, we can check * the differences and potentially reconfigure port forwardings * etc in mid-session. */ Conf *conf; /* * Values cached out of conf so as to avoid the tree234 lookup * cost every time they're used. */ int logomitdata; /* * Dynamically allocated username string created during SSH * login. Stored in here rather than in the coroutine state so * that it'll be reliably freed if we shut down the SSH session * at some unexpected moment. */ char *username; /* * Used to transfer data back from async callbacks. */ void *agent_response; int agent_response_len; int user_response; |
︙ | ︙ | |||
932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 | handler_fn_t packet_dispatch[256]; /* * Queues of one-off handler functions for success/failure * indications from a request. */ struct queued_handler *qhead, *qtail; /* * This module deals with sending keepalives. */ Pinger pinger; /* * Track incoming and outgoing data sizes and time, for * size-based rekeys. */ unsigned long incoming_data_size, outgoing_data_size, deferred_data_size; unsigned long max_data_size; int kex_in_progress; | > | | 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 | handler_fn_t packet_dispatch[256]; /* * Queues of one-off handler functions for success/failure * indications from a request. */ struct queued_handler *qhead, *qtail; handler_fn_t q_saved_handler1, q_saved_handler2; /* * This module deals with sending keepalives. */ Pinger pinger; /* * Track incoming and outgoing data sizes and time, for * size-based rekeys. */ unsigned long incoming_data_size, outgoing_data_size, deferred_data_size; unsigned long max_data_size; int kex_in_progress; unsigned long next_rekey, last_rekey; char *deferred_rekey_reason; /* points to STATIC string; don't free */ /* * Fully qualified host name, which we need if doing GSSAPI. */ char *fullhostname; |
︙ | ︙ | |||
976 977 978 979 980 981 982 | va_start(ap, fmt); buf = dupvprintf(fmt, ap); va_end(ap); logevent(buf); sfree(buf); } | < < | > | | | | < | < < < < < < | < | < < < < < < < < < | | < < < | | > | > > > > > > | | < < | | < | > > | | 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 | va_start(ap, fmt); buf = dupvprintf(fmt, ap); va_end(ap); logevent(buf); sfree(buf); } static void bomb_out(Ssh ssh, char *text) { ssh_do_close(ssh, FALSE); logevent(text); connection_fatal(ssh->frontend, "%s", text); sfree(text); } #define bombout(msg) bomb_out(ssh, dupprintf msg) /* Helper function for common bits of parsing ttymodes. */ static void parse_ttymodes(Ssh ssh, void (*do_mode)(void *data, char *mode, char *val), void *data) { char *key, *val; for (val = conf_get_str_strs(ssh->conf, CONF_ttymodes, NULL, &key); val != NULL; val = conf_get_str_strs(ssh->conf, CONF_ttymodes, key, &key)) { /* * val[0] is either 'V', indicating that an explicit value * follows it, or 'A' indicating that we should pass the * value through from the local environment via get_ttymode. */ if (val[0] == 'A') { val = get_ttymode(ssh->frontend, key); if (val) { do_mode(data, key, val); sfree(val); } } else do_mode(data, key, val + 1); /* skip the 'V' */ } } static int ssh_channelcmp(void *av, void *bv) { struct ssh_channel *a = (struct ssh_channel *) av; struct ssh_channel *b = (struct ssh_channel *) bv; |
︙ | ︙ | |||
1066 1067 1068 1069 1070 1071 1072 | return 0; } static int ssh_rportcmp_ssh2(void *av, void *bv) { struct ssh_rportfwd *a = (struct ssh_rportfwd *) av; struct ssh_rportfwd *b = (struct ssh_rportfwd *) bv; | | > > | 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 | return 0; } static int ssh_rportcmp_ssh2(void *av, void *bv) { struct ssh_rportfwd *a = (struct ssh_rportfwd *) av; struct ssh_rportfwd *b = (struct ssh_rportfwd *) bv; int i; if ( (i = strcmp(a->shost, b->shost)) != 0) return i < 0 ? -1 : +1; if (a->sport > b->sport) return +1; if (a->sport < b->sport) return -1; return 0; } |
︙ | ︙ | |||
1197 1198 1199 1200 1201 1202 1203 | } static struct Packet *ssh_new_packet(void) { struct Packet *pkt = snew(struct Packet); pkt->body = pkt->data = NULL; pkt->maxlen = 0; | | > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > | > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 | } static struct Packet *ssh_new_packet(void) { struct Packet *pkt = snew(struct Packet); pkt->body = pkt->data = NULL; pkt->maxlen = 0; return pkt; } static void ssh1_log_incoming_packet(Ssh ssh, struct Packet *pkt) { int nblanks = 0; struct logblank_t blanks[4]; char *str; int slen; pkt->savedpos = 0; if (ssh->logomitdata && (pkt->type == SSH1_SMSG_STDOUT_DATA || pkt->type == SSH1_SMSG_STDERR_DATA || pkt->type == SSH1_MSG_CHANNEL_DATA)) { /* "Session data" packets - omit the data string. */ if (pkt->type == SSH1_MSG_CHANNEL_DATA) ssh_pkt_getuint32(pkt); /* skip channel id */ blanks[nblanks].offset = pkt->savedpos + 4; blanks[nblanks].type = PKTLOG_OMIT; ssh_pkt_getstring(pkt, &str, &slen); if (str) { blanks[nblanks].len = slen; nblanks++; } } log_packet(ssh->logctx, PKT_INCOMING, pkt->type, ssh1_pkt_type(pkt->type), pkt->body, pkt->length, nblanks, blanks, NULL, 0, NULL); } static void ssh1_log_outgoing_packet(Ssh ssh, struct Packet *pkt) { int nblanks = 0; struct logblank_t blanks[4]; char *str; int slen; /* * For outgoing packets, pkt->length represents the length of the * whole packet starting at pkt->data (including some header), and * pkt->body refers to the point within that where the log-worthy * payload begins. However, incoming packets expect pkt->length to * represent only the payload length (that is, it's measured from * pkt->body not from pkt->data). Temporarily adjust our outgoing * packet to conform to the incoming-packet semantics, so that we * can analyse it with the ssh_pkt_get functions. */ pkt->length -= (pkt->body - pkt->data); pkt->savedpos = 0; if (ssh->logomitdata && (pkt->type == SSH1_CMSG_STDIN_DATA || pkt->type == SSH1_MSG_CHANNEL_DATA)) { /* "Session data" packets - omit the data string. */ if (pkt->type == SSH1_MSG_CHANNEL_DATA) ssh_pkt_getuint32(pkt); /* skip channel id */ blanks[nblanks].offset = pkt->savedpos + 4; blanks[nblanks].type = PKTLOG_OMIT; ssh_pkt_getstring(pkt, &str, &slen); if (str) { blanks[nblanks].len = slen; nblanks++; } } if ((pkt->type == SSH1_CMSG_AUTH_PASSWORD || pkt->type == SSH1_CMSG_AUTH_TIS_RESPONSE || pkt->type == SSH1_CMSG_AUTH_CCARD_RESPONSE) && conf_get_int(ssh->conf, CONF_logomitpass)) { /* If this is a password or similar packet, blank the password(s). */ blanks[nblanks].offset = 0; blanks[nblanks].len = pkt->length; blanks[nblanks].type = PKTLOG_BLANK; nblanks++; } else if (pkt->type == SSH1_CMSG_X11_REQUEST_FORWARDING && conf_get_int(ssh->conf, CONF_logomitpass)) { /* * If this is an X forwarding request packet, blank the fake * auth data. * * Note that while we blank the X authentication data here, we * don't take any special action to blank the start of an X11 * channel, so using MIT-MAGIC-COOKIE-1 and actually opening * an X connection without having session blanking enabled is * likely to leak your cookie into the log. */ pkt->savedpos = 0; ssh_pkt_getstring(pkt, &str, &slen); blanks[nblanks].offset = pkt->savedpos; blanks[nblanks].type = PKTLOG_BLANK; ssh_pkt_getstring(pkt, &str, &slen); if (str) { blanks[nblanks].len = pkt->savedpos - blanks[nblanks].offset; nblanks++; } } log_packet(ssh->logctx, PKT_OUTGOING, pkt->data[12], ssh1_pkt_type(pkt->data[12]), pkt->body, pkt->length, nblanks, blanks, NULL, 0, NULL); /* * Undo the above adjustment of pkt->length, to put the packet * back in the state we found it. */ pkt->length += (pkt->body - pkt->data); } /* * Collect incoming data in the incoming packet buffer. * Decipher and verify the packet when it is completely read. * Drop SSH1_MSG_DEBUG and SSH1_MSG_IGNORE packets. * Update the *data and *datalen variables. * Return a Packet structure when a packet is completed. |
︙ | ︙ | |||
1277 1278 1279 1280 1281 1282 1283 | if (st->gotcrc != st->realcrc) { bombout(("Incorrect CRC received on packet")); ssh_free_packet(st->pktin); crStop(NULL); } st->pktin->body = st->pktin->data + st->pad + 1; | < | 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 | if (st->gotcrc != st->realcrc) { bombout(("Incorrect CRC received on packet")); ssh_free_packet(st->pktin); crStop(NULL); } st->pktin->body = st->pktin->data + st->pad + 1; if (ssh->v1_compressing) { unsigned char *decompblk; int decomplen; if (!zlib_decompress_block(ssh->sc_comp_ctx, st->pktin->body - 1, st->pktin->length + 1, &decompblk, &decomplen)) { |
︙ | ︙ | |||
1306 1307 1308 1309 1310 1311 1312 | sfree(decompblk); st->pktin->length = decomplen - 1; } st->pktin->type = st->pktin->body[-1]; /* | > | < < < < < < < < < < < | | < | < < | < < | < < < < > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 | sfree(decompblk); st->pktin->length = decomplen - 1; } st->pktin->type = st->pktin->body[-1]; /* * Now pktin->body and pktin->length identify the semantic content * of the packet, excluding the initial type byte. */ if (ssh->logctx) ssh1_log_incoming_packet(ssh, st->pktin); st->pktin->savedpos = 0; crFinish(st->pktin); } static void ssh2_log_incoming_packet(Ssh ssh, struct Packet *pkt) { int nblanks = 0; struct logblank_t blanks[4]; char *str; int slen; pkt->savedpos = 0; if (ssh->logomitdata && (pkt->type == SSH2_MSG_CHANNEL_DATA || pkt->type == SSH2_MSG_CHANNEL_EXTENDED_DATA)) { /* "Session data" packets - omit the data string. */ ssh_pkt_getuint32(pkt); /* skip channel id */ if (pkt->type == SSH2_MSG_CHANNEL_EXTENDED_DATA) ssh_pkt_getuint32(pkt); /* skip extended data type */ blanks[nblanks].offset = pkt->savedpos + 4; blanks[nblanks].type = PKTLOG_OMIT; ssh_pkt_getstring(pkt, &str, &slen); if (str) { blanks[nblanks].len = slen; nblanks++; } } log_packet(ssh->logctx, PKT_INCOMING, pkt->type, ssh2_pkt_type(ssh->pkt_kctx, ssh->pkt_actx, pkt->type), pkt->body, pkt->length, nblanks, blanks, &pkt->sequence, 0, NULL); } static void ssh2_log_outgoing_packet(Ssh ssh, struct Packet *pkt) { int nblanks = 0; struct logblank_t blanks[4]; char *str; int slen; /* * For outgoing packets, pkt->length represents the length of the * whole packet starting at pkt->data (including some header), and * pkt->body refers to the point within that where the log-worthy * payload begins. However, incoming packets expect pkt->length to * represent only the payload length (that is, it's measured from * pkt->body not from pkt->data). Temporarily adjust our outgoing * packet to conform to the incoming-packet semantics, so that we * can analyse it with the ssh_pkt_get functions. */ pkt->length -= (pkt->body - pkt->data); pkt->savedpos = 0; if (ssh->logomitdata && (pkt->type == SSH2_MSG_CHANNEL_DATA || pkt->type == SSH2_MSG_CHANNEL_EXTENDED_DATA)) { /* "Session data" packets - omit the data string. */ ssh_pkt_getuint32(pkt); /* skip channel id */ if (pkt->type == SSH2_MSG_CHANNEL_EXTENDED_DATA) ssh_pkt_getuint32(pkt); /* skip extended data type */ blanks[nblanks].offset = pkt->savedpos + 4; blanks[nblanks].type = PKTLOG_OMIT; ssh_pkt_getstring(pkt, &str, &slen); if (str) { blanks[nblanks].len = slen; nblanks++; } } if (pkt->type == SSH2_MSG_USERAUTH_REQUEST && conf_get_int(ssh->conf, CONF_logomitpass)) { /* If this is a password packet, blank the password(s). */ pkt->savedpos = 0; ssh_pkt_getstring(pkt, &str, &slen); ssh_pkt_getstring(pkt, &str, &slen); ssh_pkt_getstring(pkt, &str, &slen); if (slen == 8 && !memcmp(str, "password", 8)) { ssh2_pkt_getbool(pkt); /* Blank the password field. */ blanks[nblanks].offset = pkt->savedpos; blanks[nblanks].type = PKTLOG_BLANK; ssh_pkt_getstring(pkt, &str, &slen); if (str) { blanks[nblanks].len = pkt->savedpos - blanks[nblanks].offset; nblanks++; /* If there's another password field beyond it (change of * password), blank that too. */ ssh_pkt_getstring(pkt, &str, &slen); if (str) blanks[nblanks-1].len = pkt->savedpos - blanks[nblanks].offset; } } } else if (ssh->pkt_actx == SSH2_PKTCTX_KBDINTER && pkt->type == SSH2_MSG_USERAUTH_INFO_RESPONSE && conf_get_int(ssh->conf, CONF_logomitpass)) { /* If this is a keyboard-interactive response packet, blank * the responses. */ pkt->savedpos = 0; ssh_pkt_getuint32(pkt); blanks[nblanks].offset = pkt->savedpos; blanks[nblanks].type = PKTLOG_BLANK; while (1) { ssh_pkt_getstring(pkt, &str, &slen); if (!str) break; } blanks[nblanks].len = pkt->savedpos - blanks[nblanks].offset; nblanks++; } else if (pkt->type == SSH2_MSG_CHANNEL_REQUEST && conf_get_int(ssh->conf, CONF_logomitpass)) { /* * If this is an X forwarding request packet, blank the fake * auth data. * * Note that while we blank the X authentication data here, we * don't take any special action to blank the start of an X11 * channel, so using MIT-MAGIC-COOKIE-1 and actually opening * an X connection without having session blanking enabled is * likely to leak your cookie into the log. */ pkt->savedpos = 0; ssh_pkt_getuint32(pkt); ssh_pkt_getstring(pkt, &str, &slen); if (slen == 7 && !memcmp(str, "x11-req", 0)) { ssh2_pkt_getbool(pkt); ssh2_pkt_getbool(pkt); ssh_pkt_getstring(pkt, &str, &slen); blanks[nblanks].offset = pkt->savedpos; blanks[nblanks].type = PKTLOG_BLANK; ssh_pkt_getstring(pkt, &str, &slen); if (str) { blanks[nblanks].len = pkt->savedpos - blanks[nblanks].offset; nblanks++; } } } log_packet(ssh->logctx, PKT_OUTGOING, pkt->data[5], ssh2_pkt_type(ssh->pkt_kctx, ssh->pkt_actx, pkt->data[5]), pkt->body, pkt->length, nblanks, blanks, &ssh->v2_outgoing_sequence, pkt->downstream_id, pkt->additional_log_text); /* * Undo the above adjustment of pkt->length, to put the packet * back in the state we found it. */ pkt->length += (pkt->body - pkt->data); } static struct Packet *ssh2_rdpkt(Ssh ssh, unsigned char **data, int *datalen) { struct rdpkt2_state_tag *st = &ssh->rdpkt2_state; crBegin(ssh->ssh2_rdpkt_crstate); |
︙ | ︙ | |||
1409 1410 1411 1412 1413 1414 1415 | /* Feed that block to the MAC. */ ssh->scmac->bytes(ssh->sc_mac_ctx, st->pktin->data + st->packetlen, st->cipherblk); st->packetlen += st->cipherblk; /* See if that gives us a valid packet. */ if (ssh->scmac->verresult(ssh->sc_mac_ctx, st->pktin->data + st->packetlen) && | | > | 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 | /* Feed that block to the MAC. */ ssh->scmac->bytes(ssh->sc_mac_ctx, st->pktin->data + st->packetlen, st->cipherblk); st->packetlen += st->cipherblk; /* See if that gives us a valid packet. */ if (ssh->scmac->verresult(ssh->sc_mac_ctx, st->pktin->data + st->packetlen) && ((st->len = toint(GET_32BIT(st->pktin->data))) == st->packetlen-4)) break; if (st->packetlen >= OUR_V2_PACKETLIMIT) { bombout(("No valid incoming packet found")); ssh_free_packet(st->pktin); crStop(NULL); } } |
︙ | ︙ | |||
1442 1443 1444 1445 1446 1447 1448 | if (ssh->sccipher) ssh->sccipher->decrypt(ssh->sc_cipher_ctx, st->pktin->data, st->cipherblk); /* * Now get the length figure. */ | | | 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 | if (ssh->sccipher) ssh->sccipher->decrypt(ssh->sc_cipher_ctx, st->pktin->data, st->cipherblk); /* * Now get the length figure. */ st->len = toint(GET_32BIT(st->pktin->data)); /* * _Completely_ silly lengths should be stomped on before they * do us any more damage. */ if (st->len < 0 || st->len > OUR_V2_PACKETLIMIT || (st->len + 4) % st->cipherblk != 0) { |
︙ | ︙ | |||
1534 1535 1536 1537 1538 1539 1540 | } st->pktin->length = 5 + newlen; memcpy(st->pktin->data + 5, newpayload, newlen); sfree(newpayload); } } | > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > | > > | < < < < < | < < < | < < < < < < < < < | < < < | < < < < < | 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 | } st->pktin->length = 5 + newlen; memcpy(st->pktin->data + 5, newpayload, newlen); sfree(newpayload); } } /* * pktin->body and pktin->length should identify the semantic * content of the packet, excluding the initial type byte. */ st->pktin->type = st->pktin->data[5]; st->pktin->body = st->pktin->data + 6; st->pktin->length = st->packetlen - 6 - st->pad; assert(st->pktin->length >= 0); /* one last double-check */ if (ssh->logctx) ssh2_log_incoming_packet(ssh, st->pktin); st->pktin->savedpos = 0; crFinish(st->pktin); } static struct Packet *ssh2_bare_connection_rdpkt(Ssh ssh, unsigned char **data, int *datalen) { struct rdpkt2_bare_state_tag *st = &ssh->rdpkt2_bare_state; crBegin(ssh->ssh2_bare_rdpkt_crstate); /* * Read the packet length field. */ for (st->i = 0; st->i < 4; st->i++) { while ((*datalen) == 0) crReturn(NULL); st->length[st->i] = *(*data)++; (*datalen)--; } st->packetlen = toint(GET_32BIT_MSB_FIRST(st->length)); if (st->packetlen <= 0 || st->packetlen >= OUR_V2_PACKETLIMIT) { bombout(("Invalid packet length received")); crStop(NULL); } st->pktin = ssh_new_packet(); st->pktin->data = snewn(st->packetlen, unsigned char); st->pktin->encrypted_len = st->packetlen; st->pktin->sequence = st->incoming_sequence++; /* * Read the remainder of the packet. */ for (st->i = 0; st->i < st->packetlen; st->i++) { while ((*datalen) == 0) crReturn(NULL); st->pktin->data[st->i] = *(*data)++; (*datalen)--; } /* * pktin->body and pktin->length should identify the semantic * content of the packet, excluding the initial type byte. */ st->pktin->type = st->pktin->data[0]; st->pktin->body = st->pktin->data + 1; st->pktin->length = st->packetlen - 1; /* * Log incoming packet, possibly omitting sensitive fields. */ if (ssh->logctx) ssh2_log_incoming_packet(ssh, st->pktin); st->pktin->savedpos = 0; crFinish(st->pktin); } static int s_wrpkt_prepare(Ssh ssh, struct Packet *pkt, int *offset_p) { int pad, biglen, i, pktoffs; unsigned long crc; #ifdef __SC__ /* * XXX various versions of SC (including 8.8.4) screw up the * register allocation in this function and use the same register * (D6) for len and as a temporary, with predictable results. The * following sledgehammer prevents this. */ volatile #endif int len; if (ssh->logctx) ssh1_log_outgoing_packet(ssh, pkt); if (ssh->v1_compressing) { unsigned char *compblk; int complen; zlib_compress_block(ssh->cs_comp_ctx, pkt->data + 12, pkt->length - 12, &compblk, &complen); |
︙ | ︙ | |||
1629 1630 1631 1632 1633 1634 1635 | return biglen + 4; /* len(length+padding+type+data+CRC) */ } static int s_write(Ssh ssh, void *data, int len) { if (ssh->logctx) log_packet(ssh->logctx, PKT_OUTGOING, -1, NULL, data, len, | | > > | 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 | return biglen + 4; /* len(length+padding+type+data+CRC) */ } static int s_write(Ssh ssh, void *data, int len) { if (ssh->logctx) log_packet(ssh->logctx, PKT_OUTGOING, -1, NULL, data, len, 0, NULL, NULL, 0, NULL); if (!ssh->s) return 0; return sk_write(ssh->s, (char *)data, len); } static void s_wrpkt(Ssh ssh, struct Packet *pkt) { int len, backlog, offset; len = s_wrpkt_prepare(ssh, pkt, &offset); |
︙ | ︙ | |||
1700 1701 1702 1703 1704 1705 1706 | sargp = va_arg(ap, char *); ssh_pkt_addstring(pkt, sargp); break; case PKT_BIGNUM: bn = va_arg(ap, Bignum); ssh1_pkt_addmp(pkt, bn); break; | < < < < < < < < < < | 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 | sargp = va_arg(ap, char *); ssh_pkt_addstring(pkt, sargp); break; case PKT_BIGNUM: bn = va_arg(ap, Bignum); ssh1_pkt_addmp(pkt, bn); break; } } return pkt; } static void send_packet(Ssh ssh, int pkttype, ...) |
︙ | ︙ | |||
1788 1789 1790 1791 1792 1793 1794 | unsigned char *body = pkt->body; int offset = body ? body - pkt->data : 0; pkt->maxlen = length + 256; pkt->data = sresize(pkt->data, pkt->maxlen + APIEXTRA, unsigned char); if (body) pkt->body = pkt->data + offset; } } | | < < < < < < < < < | 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 | unsigned char *body = pkt->body; int offset = body ? body - pkt->data : 0; pkt->maxlen = length + 256; pkt->data = sresize(pkt->data, pkt->maxlen + APIEXTRA, unsigned char); if (body) pkt->body = pkt->data + offset; } } static void ssh_pkt_adddata(struct Packet *pkt, const void *data, int len) { pkt->length += len; ssh_pkt_ensure(pkt, pkt->length); memcpy(pkt->data + pkt->length - len, data, len); } static void ssh_pkt_addbyte(struct Packet *pkt, unsigned char byte) { ssh_pkt_adddata(pkt, &byte, 1); |
︙ | ︙ | |||
1822 1823 1824 1825 1826 1827 1828 | ssh_pkt_adddata(pkt, x, 4); } static void ssh_pkt_addstring_start(struct Packet *pkt) { ssh_pkt_adduint32(pkt, 0); pkt->savedpos = pkt->length; } | | | > | | 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 2060 2061 | ssh_pkt_adddata(pkt, x, 4); } static void ssh_pkt_addstring_start(struct Packet *pkt) { ssh_pkt_adduint32(pkt, 0); pkt->savedpos = pkt->length; } static void ssh_pkt_addstring_str(struct Packet *pkt, const char *data) { ssh_pkt_adddata(pkt, data, strlen(data)); PUT_32BIT(pkt->data + pkt->savedpos - 4, pkt->length - pkt->savedpos); } static void ssh_pkt_addstring_data(struct Packet *pkt, const char *data, int len) { ssh_pkt_adddata(pkt, data, len); PUT_32BIT(pkt->data + pkt->savedpos - 4, pkt->length - pkt->savedpos); } static void ssh_pkt_addstring(struct Packet *pkt, const char *data) { ssh_pkt_addstring_start(pkt); ssh_pkt_addstring_str(pkt, data); } static void ssh1_pkt_addmp(struct Packet *pkt, Bignum b) { int len = ssh1_bignum_length(b); |
︙ | ︙ | |||
1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 | static struct Packet *ssh1_pkt_init(int pkt_type) { struct Packet *pkt = ssh_new_packet(); pkt->length = 4 + 8; /* space for length + max padding */ ssh_pkt_addbyte(pkt, pkt_type); pkt->body = pkt->data + pkt->length; return pkt; } /* For legacy code (SSH-1 and -2 packet construction used to be separate) */ #define ssh2_pkt_ensure(pkt, length) ssh_pkt_ensure(pkt, length) #define ssh2_pkt_adddata(pkt, data, len) ssh_pkt_adddata(pkt, data, len) #define ssh2_pkt_addbyte(pkt, byte) ssh_pkt_addbyte(pkt, byte) #define ssh2_pkt_adduint32(pkt, value) ssh_pkt_adduint32(pkt, value) #define ssh2_pkt_addstring_start(pkt) ssh_pkt_addstring_start(pkt) #define ssh2_pkt_addstring_str(pkt, data) ssh_pkt_addstring_str(pkt, data) #define ssh2_pkt_addstring_data(pkt, data, len) ssh_pkt_addstring_data(pkt, data, len) #define ssh2_pkt_addstring(pkt, data) ssh_pkt_addstring(pkt, data) static struct Packet *ssh2_pkt_init(int pkt_type) { struct Packet *pkt = ssh_new_packet(); pkt->length = 5; /* space for packet length + padding length */ pkt->forcepad = 0; ssh_pkt_addbyte(pkt, (unsigned char) pkt_type); pkt->body = pkt->data + pkt->length; /* after packet type */ return pkt; } /* * Construct an SSH-2 final-form packet: compress it, encrypt it, * put the MAC on it. Final packet, ready to be sent, is stored in * pkt->data. Total length is returned. */ static int ssh2_pkt_construct(Ssh ssh, struct Packet *pkt) { int cipherblk, maclen, padding, i; if (ssh->logctx) | > > > > > > | | > > > > > | > | < | > | 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 2104 2105 2106 2107 2108 2109 2110 2111 2112 2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 2124 2125 2126 2127 2128 2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141 2142 2143 2144 2145 2146 2147 2148 2149 2150 2151 2152 2153 2154 2155 | static struct Packet *ssh1_pkt_init(int pkt_type) { struct Packet *pkt = ssh_new_packet(); pkt->length = 4 + 8; /* space for length + max padding */ ssh_pkt_addbyte(pkt, pkt_type); pkt->body = pkt->data + pkt->length; pkt->type = pkt_type; pkt->downstream_id = 0; pkt->additional_log_text = NULL; return pkt; } /* For legacy code (SSH-1 and -2 packet construction used to be separate) */ #define ssh2_pkt_ensure(pkt, length) ssh_pkt_ensure(pkt, length) #define ssh2_pkt_adddata(pkt, data, len) ssh_pkt_adddata(pkt, data, len) #define ssh2_pkt_addbyte(pkt, byte) ssh_pkt_addbyte(pkt, byte) #define ssh2_pkt_adduint32(pkt, value) ssh_pkt_adduint32(pkt, value) #define ssh2_pkt_addstring_start(pkt) ssh_pkt_addstring_start(pkt) #define ssh2_pkt_addstring_str(pkt, data) ssh_pkt_addstring_str(pkt, data) #define ssh2_pkt_addstring_data(pkt, data, len) ssh_pkt_addstring_data(pkt, data, len) #define ssh2_pkt_addstring(pkt, data) ssh_pkt_addstring(pkt, data) static struct Packet *ssh2_pkt_init(int pkt_type) { struct Packet *pkt = ssh_new_packet(); pkt->length = 5; /* space for packet length + padding length */ pkt->forcepad = 0; pkt->type = pkt_type; ssh_pkt_addbyte(pkt, (unsigned char) pkt_type); pkt->body = pkt->data + pkt->length; /* after packet type */ pkt->downstream_id = 0; pkt->additional_log_text = NULL; return pkt; } /* * Construct an SSH-2 final-form packet: compress it, encrypt it, * put the MAC on it. Final packet, ready to be sent, is stored in * pkt->data. Total length is returned. */ static int ssh2_pkt_construct(Ssh ssh, struct Packet *pkt) { int cipherblk, maclen, padding, i; if (ssh->logctx) ssh2_log_outgoing_packet(ssh, pkt); if (ssh->bare_connection) { /* * Trivial packet construction for the bare connection * protocol. */ PUT_32BIT(pkt->data + 1, pkt->length - 5); pkt->body = pkt->data + 1; ssh->v2_outgoing_sequence++; /* only for diagnostics, really */ return pkt->length - 1; } /* * Compress packet payload. */ { unsigned char *newpayload; int newlen; |
︙ | ︙ | |||
1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 | if (ssh->cscipher) ssh->cscipher->encrypt(ssh->cs_cipher_ctx, pkt->data, pkt->length + padding); pkt->encrypted_len = pkt->length + padding; /* Ready-to-send packet starts at pkt->data. We return length. */ return pkt->length + padding + maclen; } /* * Routines called from the main SSH code to send packets. There * are quite a few of these, because we have two separate * mechanisms for delaying the sending of packets: | > | 2192 2193 2194 2195 2196 2197 2198 2199 2200 2201 2202 2203 2204 2205 2206 | if (ssh->cscipher) ssh->cscipher->encrypt(ssh->cs_cipher_ctx, pkt->data, pkt->length + padding); pkt->encrypted_len = pkt->length + padding; /* Ready-to-send packet starts at pkt->data. We return length. */ pkt->body = pkt->data; return pkt->length + padding + maclen; } /* * Routines called from the main SSH code to send packets. There * are quite a few of these, because we have two separate * mechanisms for delaying the sending of packets: |
︙ | ︙ | |||
2022 2023 2024 2025 2026 2027 2028 | if (ssh->cscipher != NULL && (ssh->cscipher->flags & SSH_CIPHER_IS_CBC)) { /* We need to send two packets, so use the deferral mechanism. */ ssh2_pkt_defer_noqueue(ssh, pkt, FALSE); ssh_pkt_defersend(ssh); return; } len = ssh2_pkt_construct(ssh, pkt); | | > | 2250 2251 2252 2253 2254 2255 2256 2257 2258 2259 2260 2261 2262 2263 2264 2265 2266 2267 2268 2269 2270 | if (ssh->cscipher != NULL && (ssh->cscipher->flags & SSH_CIPHER_IS_CBC)) { /* We need to send two packets, so use the deferral mechanism. */ ssh2_pkt_defer_noqueue(ssh, pkt, FALSE); ssh_pkt_defersend(ssh); return; } len = ssh2_pkt_construct(ssh, pkt); backlog = s_write(ssh, pkt->body, len); if (backlog > SSH_MAX_BACKLOG) ssh_throttle_all(ssh, 1, backlog); ssh->outgoing_data_size += pkt->encrypted_len; if (!ssh->kex_in_progress && !ssh->bare_connection && ssh->max_data_size != 0 && ssh->outgoing_data_size > ssh->max_data_size) do_ssh2_transport(ssh, "too much data sent", -1, NULL); ssh_free_packet(pkt); } |
︙ | ︙ | |||
2059 2060 2061 2062 2063 2064 2065 | len = ssh2_pkt_construct(ssh, pkt); if (ssh->deferred_len + len > ssh->deferred_size) { ssh->deferred_size = ssh->deferred_len + len + 128; ssh->deferred_send_data = sresize(ssh->deferred_send_data, ssh->deferred_size, unsigned char); } | | | 2288 2289 2290 2291 2292 2293 2294 2295 2296 2297 2298 2299 2300 2301 2302 | len = ssh2_pkt_construct(ssh, pkt); if (ssh->deferred_len + len > ssh->deferred_size) { ssh->deferred_size = ssh->deferred_len + len + 128; ssh->deferred_send_data = sresize(ssh->deferred_send_data, ssh->deferred_size, unsigned char); } memcpy(ssh->deferred_send_data + ssh->deferred_len, pkt->body, len); ssh->deferred_len += len; ssh->deferred_data_size += pkt->encrypted_len; ssh_free_packet(pkt); } /* * Queue an SSH-2 packet. |
︙ | ︙ | |||
2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141 2142 | sfree(ssh->deferred_send_data); ssh->deferred_send_data = NULL; if (backlog > SSH_MAX_BACKLOG) ssh_throttle_all(ssh, 1, backlog); ssh->outgoing_data_size += ssh->deferred_data_size; if (!ssh->kex_in_progress && ssh->max_data_size != 0 && ssh->outgoing_data_size > ssh->max_data_size) do_ssh2_transport(ssh, "too much data sent", -1, NULL); ssh->deferred_data_size = 0; } /* | > | 2358 2359 2360 2361 2362 2363 2364 2365 2366 2367 2368 2369 2370 2371 2372 | sfree(ssh->deferred_send_data); ssh->deferred_send_data = NULL; if (backlog > SSH_MAX_BACKLOG) ssh_throttle_all(ssh, 1, backlog); ssh->outgoing_data_size += ssh->deferred_data_size; if (!ssh->kex_in_progress && !ssh->bare_connection && ssh->max_data_size != 0 && ssh->outgoing_data_size > ssh->max_data_size) do_ssh2_transport(ssh, "too much data sent", -1, NULL); ssh->deferred_data_size = 0; } /* |
︙ | ︙ | |||
2273 2274 2275 2276 2277 2278 2279 | static void ssh_pkt_getstring(struct Packet *pkt, char **p, int *length) { int len; *p = NULL; *length = 0; if (pkt->length - pkt->savedpos < 4) return; | | | 2503 2504 2505 2506 2507 2508 2509 2510 2511 2512 2513 2514 2515 2516 2517 | static void ssh_pkt_getstring(struct Packet *pkt, char **p, int *length) { int len; *p = NULL; *length = 0; if (pkt->length - pkt->savedpos < 4) return; len = toint(GET_32BIT(pkt->body + pkt->savedpos)); if (len < 0) return; *length = len; pkt->savedpos += 4; if (pkt->length - pkt->savedpos < *length) return; *p = (char *)(pkt->body + pkt->savedpos); |
︙ | ︙ | |||
2357 2358 2359 2360 2361 2362 2363 | /* dmemdump(pkblob, pkblob_len); */ /* dmemdump(sigblob, sigblob_len); */ /* * See if this is in fact an ssh-rsa signature and a buggy * server; otherwise we can just do this the easy way. */ | | > > > | > > | > > > > | > > | 2587 2588 2589 2590 2591 2592 2593 2594 2595 2596 2597 2598 2599 2600 2601 2602 2603 2604 2605 2606 2607 2608 2609 2610 2611 2612 2613 2614 2615 2616 2617 2618 2619 2620 2621 2622 2623 2624 2625 2626 2627 2628 2629 2630 2631 2632 | /* dmemdump(pkblob, pkblob_len); */ /* dmemdump(sigblob, sigblob_len); */ /* * See if this is in fact an ssh-rsa signature and a buggy * server; otherwise we can just do this the easy way. */ if ((ssh->remote_bugs & BUG_SSH2_RSA_PADDING) && pkblob_len > 4+7+4 && (GET_32BIT(pkblob) == 7 && !memcmp(pkblob+4, "ssh-rsa", 7))) { int pos, len, siglen; /* * Find the byte length of the modulus. */ pos = 4+7; /* skip over "ssh-rsa" */ len = toint(GET_32BIT(pkblob+pos)); /* get length of exponent */ if (len < 0 || len > pkblob_len - pos - 4) goto give_up; pos += 4 + len; /* skip over exponent */ if (pkblob_len - pos < 4) goto give_up; len = toint(GET_32BIT(pkblob+pos)); /* find length of modulus */ if (len < 0 || len > pkblob_len - pos - 4) goto give_up; pos += 4; /* find modulus itself */ while (len > 0 && pkblob[pos] == 0) len--, pos++; /* debug(("modulus length is %d\n", len)); */ /* * Now find the signature integer. */ pos = 4+7; /* skip over "ssh-rsa" */ if (sigblob_len < pos+4) goto give_up; siglen = toint(GET_32BIT(sigblob+pos)); if (siglen != sigblob_len - pos - 4) goto give_up; /* debug(("signature length is %d\n", siglen)); */ if (len != siglen) { unsigned char newlen[4]; ssh2_pkt_addstring_start(pkt); ssh2_pkt_addstring_data(pkt, (char *)sigblob, pos); /* dmemdump(sigblob, pos); */ |
︙ | ︙ | |||
2399 2400 2401 2402 2403 2404 2405 | /* dmemdump(newlen, 1); */ } ssh2_pkt_addstring_data(pkt, (char *)(sigblob+pos), siglen); /* dmemdump(sigblob+pos, siglen); */ return; } | | > > > | 2640 2641 2642 2643 2644 2645 2646 2647 2648 2649 2650 2651 2652 2653 2654 2655 2656 2657 | /* dmemdump(newlen, 1); */ } ssh2_pkt_addstring_data(pkt, (char *)(sigblob+pos), siglen); /* dmemdump(sigblob+pos, siglen); */ return; } /* Otherwise fall through and do it the easy way. We also come * here as a fallback if we discover above that the key blob * is misformatted in some way. */ give_up:; } ssh2_pkt_addstring_start(pkt); ssh2_pkt_addstring_data(pkt, (char *)sigblob, sigblob_len); } /* |
︙ | ︙ | |||
2428 2429 2430 2431 2432 2433 2434 | /* * General notes on server version strings: * - Not all servers reporting "Cisco-1.25" have all the bugs listed * here -- in particular, we've heard of one that's perfectly happy * with SSH1_MSG_IGNOREs -- but this string never seems to change, * so we can't distinguish them. */ | | | | | | | | | | | | | | | | | | | | > > > > > > > > > | < < < < | | > | > > > | | | > < | | | < < < > | | | < > | | | | | | 2672 2673 2674 2675 2676 2677 2678 2679 2680 2681 2682 2683 2684 2685 2686 2687 2688 2689 2690 2691 2692 2693 2694 2695 2696 2697 2698 2699 2700 2701 2702 2703 2704 2705 2706 2707 2708 2709 2710 2711 2712 2713 2714 2715 2716 2717 2718 2719 2720 2721 2722 2723 2724 2725 2726 2727 2728 2729 2730 2731 2732 2733 2734 2735 2736 2737 2738 2739 2740 2741 2742 2743 2744 2745 2746 2747 2748 2749 2750 2751 2752 2753 2754 2755 2756 2757 2758 2759 2760 2761 2762 2763 2764 2765 2766 2767 2768 2769 2770 2771 2772 2773 2774 2775 2776 2777 2778 2779 2780 2781 2782 2783 2784 2785 2786 2787 2788 2789 2790 2791 2792 2793 2794 2795 2796 2797 2798 2799 2800 2801 2802 2803 2804 2805 2806 2807 2808 2809 2810 2811 2812 2813 2814 2815 2816 2817 2818 2819 2820 2821 2822 2823 2824 2825 2826 2827 2828 2829 2830 2831 2832 2833 2834 2835 2836 2837 2838 2839 2840 2841 2842 2843 2844 2845 2846 2847 2848 2849 2850 2851 2852 2853 2854 2855 2856 2857 2858 2859 2860 2861 2862 2863 2864 2865 2866 2867 2868 2869 2870 2871 2872 2873 2874 2875 2876 2877 2878 2879 2880 2881 2882 2883 2884 2885 2886 2887 2888 2889 2890 2891 2892 2893 2894 2895 2896 2897 2898 2899 2900 2901 2902 2903 2904 2905 2906 2907 2908 2909 2910 2911 2912 2913 2914 2915 2916 2917 2918 2919 2920 2921 2922 2923 2924 2925 2926 2927 2928 2929 2930 2931 2932 2933 2934 2935 2936 2937 2938 2939 2940 2941 2942 2943 2944 2945 2946 2947 2948 2949 2950 2951 2952 2953 2954 2955 2956 2957 2958 2959 2960 2961 2962 2963 2964 2965 2966 2967 2968 | /* * General notes on server version strings: * - Not all servers reporting "Cisco-1.25" have all the bugs listed * here -- in particular, we've heard of one that's perfectly happy * with SSH1_MSG_IGNOREs -- but this string never seems to change, * so we can't distinguish them. */ if (conf_get_int(ssh->conf, CONF_sshbug_ignore1) == FORCE_ON || (conf_get_int(ssh->conf, CONF_sshbug_ignore1) == AUTO && (!strcmp(imp, "1.2.18") || !strcmp(imp, "1.2.19") || !strcmp(imp, "1.2.20") || !strcmp(imp, "1.2.21") || !strcmp(imp, "1.2.22") || !strcmp(imp, "Cisco-1.25") || !strcmp(imp, "OSU_1.4alpha3") || !strcmp(imp, "OSU_1.5alpha4")))) { /* * These versions don't support SSH1_MSG_IGNORE, so we have * to use a different defence against password length * sniffing. */ ssh->remote_bugs |= BUG_CHOKES_ON_SSH1_IGNORE; logevent("We believe remote version has SSH-1 ignore bug"); } if (conf_get_int(ssh->conf, CONF_sshbug_plainpw1) == FORCE_ON || (conf_get_int(ssh->conf, CONF_sshbug_plainpw1) == AUTO && (!strcmp(imp, "Cisco-1.25") || !strcmp(imp, "OSU_1.4alpha3")))) { /* * These versions need a plain password sent; they can't * handle having a null and a random length of data after * the password. */ ssh->remote_bugs |= BUG_NEEDS_SSH1_PLAIN_PASSWORD; logevent("We believe remote version needs a plain SSH-1 password"); } if (conf_get_int(ssh->conf, CONF_sshbug_rsa1) == FORCE_ON || (conf_get_int(ssh->conf, CONF_sshbug_rsa1) == AUTO && (!strcmp(imp, "Cisco-1.25")))) { /* * These versions apparently have no clue whatever about * RSA authentication and will panic and die if they see * an AUTH_RSA message. */ ssh->remote_bugs |= BUG_CHOKES_ON_RSA; logevent("We believe remote version can't handle SSH-1 RSA authentication"); } if (conf_get_int(ssh->conf, CONF_sshbug_hmac2) == FORCE_ON || (conf_get_int(ssh->conf, CONF_sshbug_hmac2) == AUTO && !wc_match("* VShell", imp) && (wc_match("2.1.0*", imp) || wc_match("2.0.*", imp) || wc_match("2.2.0*", imp) || wc_match("2.3.0*", imp) || wc_match("2.1 *", imp)))) { /* * These versions have the HMAC bug. */ ssh->remote_bugs |= BUG_SSH2_HMAC; logevent("We believe remote version has SSH-2 HMAC bug"); } if (conf_get_int(ssh->conf, CONF_sshbug_derivekey2) == FORCE_ON || (conf_get_int(ssh->conf, CONF_sshbug_derivekey2) == AUTO && !wc_match("* VShell", imp) && (wc_match("2.0.0*", imp) || wc_match("2.0.10*", imp) ))) { /* * These versions have the key-derivation bug (failing to * include the literal shared secret in the hashes that * generate the keys). */ ssh->remote_bugs |= BUG_SSH2_DERIVEKEY; logevent("We believe remote version has SSH-2 key-derivation bug"); } if (conf_get_int(ssh->conf, CONF_sshbug_rsapad2) == FORCE_ON || (conf_get_int(ssh->conf, CONF_sshbug_rsapad2) == AUTO && (wc_match("OpenSSH_2.[5-9]*", imp) || wc_match("OpenSSH_3.[0-2]*", imp)))) { /* * These versions have the SSH-2 RSA padding bug. */ ssh->remote_bugs |= BUG_SSH2_RSA_PADDING; logevent("We believe remote version has SSH-2 RSA padding bug"); } if (conf_get_int(ssh->conf, CONF_sshbug_pksessid2) == FORCE_ON || (conf_get_int(ssh->conf, CONF_sshbug_pksessid2) == AUTO && wc_match("OpenSSH_2.[0-2]*", imp))) { /* * These versions have the SSH-2 session-ID bug in * public-key authentication. */ ssh->remote_bugs |= BUG_SSH2_PK_SESSIONID; logevent("We believe remote version has SSH-2 public-key-session-ID bug"); } if (conf_get_int(ssh->conf, CONF_sshbug_rekey2) == FORCE_ON || (conf_get_int(ssh->conf, CONF_sshbug_rekey2) == AUTO && (wc_match("DigiSSH_2.0", imp) || wc_match("OpenSSH_2.[0-4]*", imp) || wc_match("OpenSSH_2.5.[0-3]*", imp) || wc_match("Sun_SSH_1.0", imp) || wc_match("Sun_SSH_1.0.1", imp) || /* All versions <= 1.2.6 (they changed their format in 1.2.7) */ wc_match("WeOnlyDo-*", imp)))) { /* * These versions have the SSH-2 rekey bug. */ ssh->remote_bugs |= BUG_SSH2_REKEY; logevent("We believe remote version has SSH-2 rekey bug"); } if (conf_get_int(ssh->conf, CONF_sshbug_maxpkt2) == FORCE_ON || (conf_get_int(ssh->conf, CONF_sshbug_maxpkt2) == AUTO && (wc_match("1.36_sshlib GlobalSCAPE", imp) || 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"); } if (conf_get_int(ssh->conf, CONF_sshbug_ignore2) == FORCE_ON) { /* * Servers that don't support SSH2_MSG_IGNORE. Currently, * none detected automatically. */ ssh->remote_bugs |= BUG_CHOKES_ON_SSH2_IGNORE; logevent("We believe remote version has SSH-2 ignore bug"); } if (conf_get_int(ssh->conf, CONF_sshbug_winadj) == FORCE_ON) { /* * Servers that don't support our winadj request for one * reason or another. Currently, none detected automatically. */ ssh->remote_bugs |= BUG_CHOKES_ON_WINADJ; logevent("We believe remote version has winadj bug"); } } /* * The `software version' part of an SSH version string is required * to contain no spaces or minus signs. */ static void ssh_fix_verstring(char *str) { /* Eat "<protoversion>-". */ while (*str && *str != '-') str++; assert(*str == '-'); str++; /* Convert minus signs and spaces in the remaining string into * underscores. */ while (*str) { if (*str == '-' || *str == ' ') *str = '_'; str++; } } /* * Send an appropriate SSH version string. */ static void ssh_send_verstring(Ssh ssh, const char *protoname, char *svers) { char *verstring; if (ssh->version == 2) { /* * Construct a v2 version string. */ verstring = dupprintf("%s2.0-%s\015\012", protoname, sshver); } else { /* * Construct a v1 version string. */ assert(!strcmp(protoname, "SSH-")); /* no v1 bare connection protocol */ verstring = dupprintf("SSH-%s-%s\012", (ssh_versioncmp(svers, "1.5") <= 0 ? svers : "1.5"), sshver); } ssh_fix_verstring(verstring + strlen(protoname)); 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) { static const char protoname[] = "SSH-"; struct do_ssh_init_state { int crLine; int vslen; char version[10]; char *vstring; int vstrsize; int i; int proto1, proto2; }; crState(do_ssh_init_state); crBeginState; /* Search for a line beginning with the protocol name prefix in * the input. */ for (;;) { for (s->i = 0; protoname[s->i]; s->i++) { if ((char)c != protoname[s->i]) goto no; crReturn(1); } break; no: while (c != '\012') crReturn(1); crReturn(1); } s->vstrsize = sizeof(protoname) + 16; s->vstring = snewn(s->vstrsize, char); strcpy(s->vstring, protoname); s->vslen = strlen(protoname); s->i = 0; while (1) { if (s->vslen >= s->vstrsize - 1) { s->vstrsize += 16; s->vstring = sresize(s->vstring, s->vstrsize, char); } s->vstring[s->vslen++] = c; if (s->i >= 0) { if (c == '-') { s->version[s->i] = '\0'; s->i = -1; } else if (s->i < sizeof(s->version) - 1) s->version[s->i++] = c; } else if (c == '\012') break; crReturn(1); /* get another char */ } ssh->agentfwd_enabled = FALSE; ssh->rdpkt2_state.incoming_sequence = 0; s->vstring[s->vslen] = 0; s->vstring[strcspn(s->vstring, "\015\012")] = '\0';/* remove EOL chars */ logeventf(ssh, "Server version: %s", s->vstring); ssh_detect_bugs(ssh, s->vstring); /* * Decide which SSH protocol version to support. */ /* Anything strictly below "2.0" means protocol 1 is supported. */ s->proto1 = ssh_versioncmp(s->version, "2.0") < 0; /* Anything greater or equal to "1.99" means protocol 2 is supported. */ s->proto2 = ssh_versioncmp(s->version, "1.99") >= 0; if (conf_get_int(ssh->conf, CONF_sshprot) == 0 && !s->proto1) { bombout(("SSH protocol version 1 required by user but not provided by server")); crStop(0); } if (conf_get_int(ssh->conf, CONF_sshprot) == 3 && !s->proto2) { bombout(("SSH protocol version 2 required by user but not provided by server")); crStop(0); } if (s->proto2 && (conf_get_int(ssh->conf, CONF_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 (conf_get_int(ssh->conf, CONF_sshprot) != 3) ssh_send_verstring(ssh, protoname, s->version); if (ssh->version == 2) { size_t len; /* * Record their version string. */ len = strcspn(s->vstring, "\015\012"); |
︙ | ︙ | |||
2734 2735 2736 2737 2738 2739 2740 | 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; | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 2985 2986 2987 2988 2989 2990 2991 2992 2993 2994 2995 2996 2997 2998 2999 3000 3001 3002 3003 3004 3005 3006 3007 3008 3009 3010 3011 3012 3013 3014 3015 3016 3017 3018 3019 3020 3021 3022 3023 3024 3025 3026 3027 3028 3029 3030 3031 3032 3033 3034 3035 3036 3037 3038 3039 3040 3041 3042 3043 3044 3045 3046 3047 3048 3049 3050 3051 3052 3053 3054 3055 3056 3057 3058 3059 3060 3061 3062 3063 3064 3065 3066 3067 3068 3069 3070 3071 3072 3073 3074 3075 3076 3077 3078 3079 3080 3081 3082 3083 3084 3085 3086 3087 3088 3089 3090 3091 3092 3093 3094 3095 3096 3097 3098 3099 3100 3101 3102 3103 3104 3105 3106 3107 3108 3109 3110 | 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->conf, &ssh_backend, ssh); sfree(s->vstring); crFinish(0); } static int do_ssh_connection_init(Ssh ssh, unsigned char c) { /* * Ordinary SSH begins with the banner "SSH-x.y-...". This is just * the ssh-connection part, extracted and given a trivial binary * packet protocol, so we replace 'SSH-' at the start with a new * name. In proper SSH style (though of course this part of the * proper SSH protocol _isn't_ subject to this kind of * DNS-domain-based extension), we define the new name in our * extension space. */ static const char protoname[] = "SSHCONNECTION@putty.projects.tartarus.org-"; struct do_ssh_connection_init_state { int crLine; int vslen; char version[10]; char *vstring; int vstrsize; int i; }; crState(do_ssh_connection_init_state); crBeginState; /* Search for a line beginning with the protocol name prefix in * the input. */ for (;;) { for (s->i = 0; protoname[s->i]; s->i++) { if ((char)c != protoname[s->i]) goto no; crReturn(1); } break; no: while (c != '\012') crReturn(1); crReturn(1); } s->vstrsize = sizeof(protoname) + 16; s->vstring = snewn(s->vstrsize, char); strcpy(s->vstring, protoname); s->vslen = strlen(protoname); s->i = 0; while (1) { if (s->vslen >= s->vstrsize - 1) { s->vstrsize += 16; s->vstring = sresize(s->vstring, s->vstrsize, char); } s->vstring[s->vslen++] = c; if (s->i >= 0) { if (c == '-') { s->version[s->i] = '\0'; s->i = -1; } else if (s->i < sizeof(s->version) - 1) s->version[s->i++] = c; } else if (c == '\012') break; crReturn(1); /* get another char */ } ssh->agentfwd_enabled = FALSE; ssh->rdpkt2_bare_state.incoming_sequence = 0; s->vstring[s->vslen] = 0; s->vstring[strcspn(s->vstring, "\015\012")] = '\0';/* remove EOL chars */ logeventf(ssh, "Server version: %s", s->vstring); ssh_detect_bugs(ssh, s->vstring); /* * Decide which SSH protocol version to support. This is easy in * bare ssh-connection mode: only 2.0 is legal. */ if (ssh_versioncmp(s->version, "2.0") < 0) { bombout(("Server announces compatibility with SSH-1 in bare ssh-connection protocol")); crStop(0); } if (conf_get_int(ssh->conf, CONF_sshprot) == 0) { bombout(("Bare ssh-connection protocol cannot be run in SSH-1-only mode")); crStop(0); } ssh->version = 2; logeventf(ssh, "Using bare ssh-connection protocol"); /* Send the version string, if we haven't already */ ssh_send_verstring(ssh, protoname, s->version); /* * Initialise bare connection protocol. */ ssh->protocol = ssh2_bare_connection_protocol; ssh2_bare_connection_protocol_setup(ssh); ssh->s_rdpkt = ssh2_bare_connection_rdpkt; update_specials_menu(ssh->frontend); ssh->state = SSH_STATE_BEFORE_SIZE; ssh->pinger = pinger_new(ssh->conf, &ssh_backend, ssh); /* * Get authconn (really just conn) under way. */ do_ssh2_authconn(ssh, NULL, 0, NULL); sfree(s->vstring); crFinish(0); } static void ssh_process_incoming_data(Ssh ssh, |
︙ | ︙ | |||
2792 2793 2794 2795 2796 2797 2798 | } static void ssh_gotdata(Ssh ssh, unsigned char *data, int datalen) { /* Log raw data, if we're in that mode. */ if (ssh->logctx) log_packet(ssh->logctx, PKT_INCOMING, -1, NULL, data, datalen, | | | | 3154 3155 3156 3157 3158 3159 3160 3161 3162 3163 3164 3165 3166 3167 3168 3169 3170 3171 3172 3173 3174 3175 3176 3177 3178 3179 3180 3181 3182 | } static void ssh_gotdata(Ssh ssh, unsigned char *data, int datalen) { /* Log raw data, if we're in that mode. */ if (ssh->logctx) log_packet(ssh->logctx, PKT_INCOMING, -1, NULL, data, datalen, 0, NULL, NULL, 0, NULL); crBegin(ssh->ssh_gotdata_crstate); /* * To begin with, feed the characters one by one to the * protocol initialisation / selection function do_ssh_init(). * When that returns 0, we're done with the initial greeting * exchange and can move on to packet discipline. */ while (1) { int ret; /* need not be kept across crReturn */ if (datalen == 0) crReturnV; /* more data please */ ret = ssh->do_ssh_init(ssh, *data); data++; datalen--; if (ret == 0) break; } /* |
︙ | ︙ | |||
2870 2871 2872 2873 2874 2875 2876 | * Now we must shut down any port- and X-forwarded channels going * through this connection. */ if (ssh->channels) { while (NULL != (c = index234(ssh->channels, 0))) { switch (c->type) { case CHAN_X11: | | | | | | > > > > > > > > > > > | | > | | > > > | | > | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 3232 3233 3234 3235 3236 3237 3238 3239 3240 3241 3242 3243 3244 3245 3246 3247 3248 3249 3250 3251 3252 3253 3254 3255 3256 3257 3258 3259 3260 3261 3262 3263 3264 3265 3266 3267 3268 3269 3270 3271 3272 3273 3274 3275 3276 3277 3278 3279 3280 3281 3282 3283 3284 3285 3286 3287 3288 3289 3290 3291 3292 3293 3294 3295 3296 3297 3298 3299 3300 3301 3302 3303 3304 3305 3306 3307 3308 3309 3310 3311 3312 3313 3314 3315 3316 3317 3318 3319 3320 3321 3322 3323 3324 3325 3326 3327 3328 3329 3330 3331 3332 3333 3334 3335 3336 3337 3338 3339 3340 3341 3342 3343 | * Now we must shut down any port- and X-forwarded channels going * through this connection. */ if (ssh->channels) { while (NULL != (c = index234(ssh->channels, 0))) { switch (c->type) { case CHAN_X11: x11_close(c->u.x11.xconn); break; case CHAN_SOCKDATA: case CHAN_SOCKDATA_DORMANT: pfd_close(c->u.pfd.pf); break; } del234(ssh->channels, c); /* moving next one to index 0 */ if (ssh->version == 2) bufchain_clear(&c->v.v2.outbuffer); sfree(c); } } /* * Go through port-forwardings, and close any associated * listening sockets. */ if (ssh->portfwds) { struct ssh_portfwd *pf; while (NULL != (pf = index234(ssh->portfwds, 0))) { /* Dispose of any listening socket. */ if (pf->local) pfl_terminate(pf->local); del234(ssh->portfwds, pf); /* moving next one to index 0 */ free_portfwd(pf); } freetree234(ssh->portfwds); ssh->portfwds = NULL; } return ret; } static void ssh_socket_log(Plug plug, int type, SockAddr addr, int port, const char *error_msg, int error_code) { Ssh ssh = (Ssh) plug; char addrbuf[256], *msg; if (ssh->attempting_connshare) { /* * While we're attempting connection sharing, don't loudly log * everything that happens. Real TCP connections need to be * logged when we _start_ trying to connect, because it might * be ages before they respond if something goes wrong; but * connection sharing is local and quick to respond, and it's * sufficient to simply wait and see whether it worked * afterwards. */ } else { sk_getaddr(addr, addrbuf, lenof(addrbuf)); if (type == 0) { if (sk_addr_needs_port(addr)) { msg = dupprintf("Connecting to %s port %d", addrbuf, port); } else { msg = dupprintf("Connecting to %s", addrbuf); } } else { msg = dupprintf("Failed to connect to %s: %s", addrbuf, error_msg); } logevent(msg); sfree(msg); } } void ssh_connshare_log(Ssh ssh, int event, const char *logtext, const char *ds_err, const char *us_err) { if (event == SHARE_NONE) { /* In this case, 'logtext' is an error message indicating a * reason why connection sharing couldn't be set up _at all_. * Failing that, ds_err and us_err indicate why we couldn't be * a downstream and an upstream respectively. */ if (logtext) { logeventf(ssh, "Could not set up connection sharing: %s", logtext); } else { if (ds_err) logeventf(ssh, "Could not set up connection sharing" " as downstream: %s", ds_err); if (us_err) logeventf(ssh, "Could not set up connection sharing" " as upstream: %s", us_err); } } else if (event == SHARE_DOWNSTREAM) { /* In this case, 'logtext' is a local endpoint address */ logeventf(ssh, "Using existing shared connection at %s", logtext); /* Also we should mention this in the console window to avoid * confusing users as to why this window doesn't behave the * usual way. */ if ((flags & FLAG_VERBOSE) || (flags & FLAG_INTERACTIVE)) { c_write_str(ssh,"Reusing a shared connection to this server.\r\n"); } } else if (event == SHARE_UPSTREAM) { /* In this case, 'logtext' is a local endpoint address too */ logeventf(ssh, "Sharing this connection at %s", logtext); } } static int ssh_closing(Plug plug, const char *error_msg, int error_code, int calling_back) { Ssh ssh = (Ssh) plug; int need_notify = ssh_do_close(ssh, FALSE); |
︙ | ︙ | |||
2978 2979 2980 2981 2982 2983 2984 | * Also places the canonical host name into `realhost'. It must be * freed by the caller. */ static const char *connect_to_host(Ssh ssh, char *host, int port, char **realhost, int nodelay, int keepalive) { static const struct plug_function_table fn_table = { | | > > | > | | | 3390 3391 3392 3393 3394 3395 3396 3397 3398 3399 3400 3401 3402 3403 3404 3405 3406 3407 3408 3409 3410 3411 3412 3413 3414 3415 3416 3417 3418 3419 3420 | * Also places the canonical host name into `realhost'. It must be * freed by the caller. */ static const char *connect_to_host(Ssh ssh, char *host, int port, char **realhost, int nodelay, int keepalive) { static const struct plug_function_table fn_table = { ssh_socket_log, ssh_closing, ssh_receive, ssh_sent, NULL }; SockAddr addr; const char *err; char *loghost; int addressfamily, sshprot; loghost = conf_get_str(ssh->conf, CONF_loghost); if (*loghost) { char *colon; ssh->savedhost = dupstr(loghost); ssh->savedport = 22; /* default ssh port */ /* * A colon suffix on savedhost also lets us affect * savedport. * * (FIXME: do something about IPv6 address literals here.) |
︙ | ︙ | |||
3013 3014 3015 3016 3017 3018 3019 3020 | } else { ssh->savedhost = dupstr(host); if (port < 0) port = 22; /* default ssh port */ ssh->savedport = port; } /* | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | > | | | | < | | | | | < < < < | | > | | | | > > | | | | | | 3428 3429 3430 3431 3432 3433 3434 3435 3436 3437 3438 3439 3440 3441 3442 3443 3444 3445 3446 3447 3448 3449 3450 3451 3452 3453 3454 3455 3456 3457 3458 3459 3460 3461 3462 3463 3464 3465 3466 3467 3468 3469 3470 3471 3472 3473 3474 3475 3476 3477 3478 3479 3480 3481 3482 3483 3484 3485 3486 3487 3488 3489 3490 3491 3492 3493 3494 3495 3496 3497 3498 3499 3500 3501 3502 3503 3504 3505 3506 3507 3508 3509 3510 3511 3512 3513 | } else { ssh->savedhost = dupstr(host); if (port < 0) port = 22; /* default ssh port */ ssh->savedport = port; } ssh->fn = &fn_table; /* make 'ssh' usable as a Plug */ /* * Try connection-sharing, in case that means we don't open a * socket after all. ssh_connection_sharing_init will connect to a * previously established upstream if it can, and failing that, * establish a listening socket for _us_ to be the upstream. In * the latter case it will return NULL just as if it had done * nothing, because here we only need to care if we're a * downstream and need to do our connection setup differently. */ ssh->connshare = NULL; ssh->attempting_connshare = TRUE; /* affects socket logging behaviour */ ssh->s = ssh_connection_sharing_init(ssh->savedhost, ssh->savedport, ssh->conf, ssh, &ssh->connshare); ssh->attempting_connshare = FALSE; if (ssh->s != NULL) { /* * We are a downstream. */ ssh->bare_connection = TRUE; ssh->do_ssh_init = do_ssh_connection_init; ssh->fullhostname = NULL; *realhost = dupstr(host); /* best we can do */ } else { /* * We're not a downstream, so open a normal socket. */ ssh->do_ssh_init = do_ssh_init; /* * Try to find host. */ addressfamily = conf_get_int(ssh->conf, CONF_addressfamily); logeventf(ssh, "Looking up host \"%s\"%s", host, (addressfamily == ADDRTYPE_IPV4 ? " (IPv4)" : (addressfamily == ADDRTYPE_IPV6 ? " (IPv6)" : ""))); addr = name_lookup(host, port, realhost, ssh->conf, addressfamily); if ((err = sk_addr_error(addr)) != NULL) { sk_addr_free(addr); return err; } ssh->fullhostname = dupstr(*realhost); /* save in case of GSSAPI */ ssh->s = new_connection(addr, *realhost, port, 0, 1, nodelay, keepalive, (Plug) ssh, ssh->conf); if ((err = sk_socket_error(ssh->s)) != NULL) { ssh->s = NULL; notify_remote_exit(ssh->frontend); return err; } } /* * If the SSH version number's fixed, set it now, and if it's SSH-2, * send the version string too. */ sshprot = conf_get_int(ssh->conf, CONF_sshprot); if (sshprot == 0) ssh->version = 1; if (sshprot == 3 && !ssh->bare_connection) { ssh->version = 2; ssh_send_verstring(ssh, "SSH-", NULL); } /* * loghost, if configured, overrides realhost. */ if (*loghost) { sfree(*realhost); *realhost = dupstr(loghost); } return NULL; } /* * Throttle or unthrottle the SSH connection. |
︙ | ︙ | |||
3099 3100 3101 3102 3103 3104 3105 | switch (c->type) { case CHAN_MAINSESSION: /* * This is treated separately, outside the switch. */ break; case CHAN_X11: | | | | 3543 3544 3545 3546 3547 3548 3549 3550 3551 3552 3553 3554 3555 3556 3557 3558 3559 3560 3561 3562 3563 | switch (c->type) { case CHAN_MAINSESSION: /* * This is treated separately, outside the switch. */ break; case CHAN_X11: x11_override_throttle(c->u.x11.xconn, enable); break; case CHAN_AGENT: /* Agent channels require no buffer management. */ break; case CHAN_SOCKDATA: pfd_override_throttle(c->u.pfd.pf, enable); break; } } } static void ssh_agent_callback(void *sshv, void *reply, int replylen) { |
︙ | ︙ | |||
3148 3149 3150 3151 3152 3153 3154 3155 3156 3157 3158 3159 3160 3161 3162 3163 3164 3165 3166 | static void ssh_agentf_callback(void *cv, void *reply, int replylen) { struct ssh_channel *c = (struct ssh_channel *)cv; Ssh ssh = c->ssh; void *sentreply = reply; if (!sentreply) { /* Fake SSH_AGENT_FAILURE. */ sentreply = "\0\0\0\1\5"; replylen = 5; } if (ssh->version == 2) { ssh2_add_channel_data(c, sentreply, replylen); ssh2_try_send(c); } else { send_packet(ssh, SSH1_MSG_CHANNEL_DATA, PKT_INT, c->remoteid, PKT_INT, replylen, | > < < > > > > > > | 3592 3593 3594 3595 3596 3597 3598 3599 3600 3601 3602 3603 3604 3605 3606 3607 3608 3609 3610 3611 3612 3613 3614 3615 3616 3617 3618 3619 3620 3621 3622 3623 3624 3625 3626 3627 3628 3629 | static void ssh_agentf_callback(void *cv, void *reply, int replylen) { struct ssh_channel *c = (struct ssh_channel *)cv; Ssh ssh = c->ssh; void *sentreply = reply; c->u.a.outstanding_requests--; if (!sentreply) { /* Fake SSH_AGENT_FAILURE. */ sentreply = "\0\0\0\1\5"; replylen = 5; } if (ssh->version == 2) { ssh2_add_channel_data(c, sentreply, replylen); ssh2_try_send(c); } else { send_packet(ssh, SSH1_MSG_CHANNEL_DATA, PKT_INT, c->remoteid, PKT_INT, replylen, PKT_DATA, sentreply, replylen, PKT_END); } if (reply) sfree(reply); /* * If we've already seen an incoming EOF but haven't sent an * outgoing one, this may be the moment to send it. */ if (c->u.a.outstanding_requests == 0 && (c->closes & CLOSES_RCVD_EOF)) sshfwd_write_eof(c); } /* * Client-initiated disconnection. Send a DISCONNECT if `wire_reason' * non-NULL, otherwise just close the connection. `client_reason' == NULL * => log `wire_reason'. */ |
︙ | ︙ | |||
3210 3211 3212 3213 3214 3215 3216 | * Handle the key exchange and user authentication phases. */ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen, struct Packet *pktin) { int i, j, ret; unsigned char cookie[8], *ptr; | < > < > > | | | | | | | | | > | | | | | 3659 3660 3661 3662 3663 3664 3665 3666 3667 3668 3669 3670 3671 3672 3673 3674 3675 3676 3677 3678 3679 3680 3681 3682 3683 3684 3685 3686 3687 3688 3689 3690 3691 3692 3693 3694 3695 3696 3697 3698 3699 3700 3701 3702 3703 3704 3705 3706 3707 3708 3709 3710 3711 3712 3713 3714 3715 3716 3717 3718 3719 3720 3721 3722 3723 3724 3725 3726 3727 3728 3729 3730 3731 3732 3733 3734 3735 3736 3737 3738 3739 3740 3741 3742 3743 3744 3745 3746 3747 3748 3749 3750 3751 3752 3753 3754 3755 3756 3757 3758 3759 3760 3761 3762 3763 3764 3765 3766 3767 3768 3769 3770 3771 3772 3773 3774 3775 3776 3777 3778 3779 3780 3781 3782 3783 3784 3785 3786 | * Handle the key exchange and user authentication phases. */ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen, struct Packet *pktin) { int i, j, ret; unsigned char cookie[8], *ptr; struct MD5Context md5c; struct do_ssh1_login_state { int crLine; int len; unsigned char *rsabuf, *keystr1, *keystr2; unsigned long supported_ciphers_mask, supported_auths_mask; int tried_publickey, tried_agent; int tis_auth_refused, ccard_auth_refused; unsigned char session_id[16]; int cipher_type; void *publickey_blob; int publickey_bloblen; char *publickey_comment; int publickey_encrypted; prompts_t *cur_prompt; char c; int pwpkt_type; unsigned char request[5], *response, *p; int responselen; int keyi, nkeys; int authed; struct RSAKey key; Bignum challenge; char *commentp; int commentlen; int dlgret; Filename *keyfile; struct RSAKey servkey, hostkey; }; crState(do_ssh1_login_state); crBeginState; if (!pktin) crWaitUntil(pktin); if (pktin->type != SSH1_SMSG_PUBLIC_KEY) { bombout(("Public key packet not received")); crStop(0); } logevent("Received public keys"); ptr = ssh_pkt_getdata(pktin, 8); if (!ptr) { bombout(("SSH-1 public key packet stopped before random cookie")); crStop(0); } memcpy(cookie, ptr, 8); if (!ssh1_pkt_getrsakey(pktin, &s->servkey, &s->keystr1) || !ssh1_pkt_getrsakey(pktin, &s->hostkey, &s->keystr2)) { bombout(("Failed to read SSH-1 public keys from public key packet")); crStop(0); } /* * Log the host key fingerprint. */ { char logmsg[80]; logevent("Host key fingerprint is:"); strcpy(logmsg, " "); s->hostkey.comment = NULL; rsa_fingerprint(logmsg + strlen(logmsg), sizeof(logmsg) - strlen(logmsg), &s->hostkey); logevent(logmsg); } 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; ssh->v1_local_protoflags |= SSH1_PROTOFLAG_SCREEN_NUMBER; MD5Init(&md5c); MD5Update(&md5c, s->keystr2, s->hostkey.bytes); MD5Update(&md5c, s->keystr1, s->servkey.bytes); MD5Update(&md5c, cookie, 8); MD5Final(s->session_id, &md5c); for (i = 0; i < 32; i++) ssh->session_key[i] = random_byte(); /* * Verify that the `bits' and `bytes' parameters match. */ if (s->hostkey.bits > s->hostkey.bytes * 8 || s->servkey.bits > s->servkey.bytes * 8) { bombout(("SSH-1 public keys were badly formatted")); crStop(0); } s->len = (s->hostkey.bytes > s->servkey.bytes ? s->hostkey.bytes : s->servkey.bytes); s->rsabuf = snewn(s->len, unsigned char); /* * Verify the host key. */ { /* * First format the key into a string. */ int len = rsastr_len(&s->hostkey); char fingerprint[100]; char *keystr = snewn(len, char); rsastr_fmt(keystr, &s->hostkey); rsa_fingerprint(fingerprint, sizeof(fingerprint), &s->hostkey); ssh_set_frozen(ssh, 1); s->dlgret = verify_ssh_host_key(ssh->frontend, ssh->savedhost, ssh->savedport, "rsa", keystr, fingerprint, ssh_dialog_callback, ssh); sfree(keystr); |
︙ | ︙ | |||
3355 3356 3357 3358 3359 3360 3361 | for (i = 0; i < 32; i++) { s->rsabuf[i] = ssh->session_key[i]; if (i < 16) s->rsabuf[i] ^= s->session_id[i]; } | | | | | | > | | 3806 3807 3808 3809 3810 3811 3812 3813 3814 3815 3816 3817 3818 3819 3820 3821 3822 3823 3824 3825 3826 3827 3828 3829 3830 3831 3832 3833 3834 3835 3836 3837 3838 3839 3840 3841 3842 | for (i = 0; i < 32; i++) { s->rsabuf[i] = ssh->session_key[i]; if (i < 16) s->rsabuf[i] ^= s->session_id[i]; } if (s->hostkey.bytes > s->servkey.bytes) { ret = rsaencrypt(s->rsabuf, 32, &s->servkey); if (ret) ret = rsaencrypt(s->rsabuf, s->servkey.bytes, &s->hostkey); } else { ret = rsaencrypt(s->rsabuf, 32, &s->hostkey); if (ret) ret = rsaencrypt(s->rsabuf, s->hostkey.bytes, &s->servkey); } if (!ret) { bombout(("SSH-1 public key encryptions failed due to bad formatting")); crStop(0); } logevent("Encrypted session key"); { int cipher_chosen = 0, warn = 0; char *cipher_string = NULL; int i; for (i = 0; !cipher_chosen && i < CIPHER_MAX; i++) { int next_cipher = conf_get_int_int(ssh->conf, CONF_ssh_cipherlist, i); if (next_cipher == CIPHER_WARN) { /* If/when we choose a cipher, warn about it */ warn = 1; } else if (next_cipher == CIPHER_AES) { /* XXX Probably don't need to mention this. */ logevent("AES not supported in SSH-1, skipping"); } else { |
︙ | ︙ | |||
3464 3465 3466 3467 3468 3469 3470 | ssh->v1_cipher_ctx = ssh->cipher->make_context(); ssh->cipher->sesskey(ssh->v1_cipher_ctx, ssh->session_key); logeventf(ssh, "Initialised %s encryption", ssh->cipher->text_name); ssh->crcda_ctx = crcda_make_context(); logevent("Installing CRC compensation attack detector"); | | | | | | | | | | | | | | < | < | < | | | 3916 3917 3918 3919 3920 3921 3922 3923 3924 3925 3926 3927 3928 3929 3930 3931 3932 3933 3934 3935 3936 3937 3938 3939 3940 3941 3942 3943 3944 3945 3946 3947 3948 3949 3950 3951 3952 3953 3954 3955 3956 3957 3958 3959 3960 3961 3962 3963 3964 3965 3966 3967 3968 3969 3970 3971 3972 3973 3974 3975 3976 3977 3978 3979 3980 3981 3982 3983 3984 | ssh->v1_cipher_ctx = ssh->cipher->make_context(); ssh->cipher->sesskey(ssh->v1_cipher_ctx, ssh->session_key); logeventf(ssh, "Initialised %s encryption", ssh->cipher->text_name); ssh->crcda_ctx = crcda_make_context(); logevent("Installing CRC compensation attack detector"); if (s->servkey.modulus) { sfree(s->servkey.modulus); s->servkey.modulus = NULL; } if (s->servkey.exponent) { sfree(s->servkey.exponent); s->servkey.exponent = NULL; } if (s->hostkey.modulus) { sfree(s->hostkey.modulus); s->hostkey.modulus = NULL; } if (s->hostkey.exponent) { sfree(s->hostkey.exponent); s->hostkey.exponent = NULL; } crWaitUntil(pktin); if (pktin->type != SSH1_SMSG_SUCCESS) { bombout(("Encryption not successfully enabled")); crStop(0); } logevent("Successfully started encryption"); fflush(stdout); /* FIXME eh? */ { if ((ssh->username = get_remote_username(ssh->conf)) == NULL) { int ret; /* need not be kept over crReturn */ s->cur_prompt = new_prompts(ssh->frontend); s->cur_prompt->to_server = TRUE; s->cur_prompt->name = dupstr("SSH login name"); add_prompt(s->cur_prompt, dupstr("login as: "), TRUE); ret = get_userpass_input(s->cur_prompt, NULL, 0); while (ret < 0) { ssh->send_ok = 1; crWaitUntil(!pktin); ret = get_userpass_input(s->cur_prompt, in, inlen); ssh->send_ok = 0; } if (!ret) { /* * Failed to get a username. Terminate. */ free_prompts(s->cur_prompt); ssh_disconnect(ssh, "No username provided", NULL, 0, TRUE); crStop(0); } ssh->username = dupstr(s->cur_prompt->prompts[0]->result); free_prompts(s->cur_prompt); } send_packet(ssh, SSH1_CMSG_USER, PKT_STR, ssh->username, PKT_END); { char *userlog = dupprintf("Sent username \"%s\"", ssh->username); logevent(userlog); if (flags & FLAG_INTERACTIVE && (!((flags & FLAG_STDERR) && (flags & FLAG_VERBOSE)))) { c_write_str(ssh, userlog); c_write_str(ssh, "\r\n"); } sfree(userlog); |
︙ | ︙ | |||
3544 3545 3546 3547 3548 3549 3550 | } else { s->tried_publickey = s->tried_agent = 0; } s->tis_auth_refused = s->ccard_auth_refused = 0; /* * Load the public half of any configured keyfile for later use. */ | > | | | | | | | | | 3993 3994 3995 3996 3997 3998 3999 4000 4001 4002 4003 4004 4005 4006 4007 4008 4009 4010 4011 4012 4013 4014 4015 4016 4017 4018 4019 4020 4021 4022 4023 4024 4025 4026 4027 4028 4029 4030 4031 4032 4033 4034 4035 4036 4037 4038 4039 4040 4041 4042 4043 4044 4045 4046 4047 4048 4049 | } else { s->tried_publickey = s->tried_agent = 0; } s->tis_auth_refused = s->ccard_auth_refused = 0; /* * Load the public half of any configured keyfile for later use. */ s->keyfile = conf_get_filename(ssh->conf, CONF_keyfile); if (!filename_is_null(s->keyfile)) { int keytype; logeventf(ssh, "Reading private key file \"%.150s\"", filename_to_str(s->keyfile)); keytype = key_type(s->keyfile); if (keytype == SSH_KEYTYPE_SSH1) { const char *error; if (rsakey_pubblob(s->keyfile, &s->publickey_blob, &s->publickey_bloblen, &s->publickey_comment, &error)) { s->publickey_encrypted = rsakey_encrypted(s->keyfile, NULL); } else { char *msgbuf; logeventf(ssh, "Unable to load private key (%s)", error); msgbuf = dupprintf("Unable to load private key file " "\"%.150s\" (%s)\r\n", filename_to_str(s->keyfile), error); c_write_str(ssh, msgbuf); sfree(msgbuf); s->publickey_blob = NULL; } } else { char *msgbuf; logeventf(ssh, "Unable to use this key file (%s)", key_type_to_str(keytype)); msgbuf = dupprintf("Unable to use key file \"%.150s\"" " (%s)\r\n", filename_to_str(s->keyfile), key_type_to_str(keytype)); c_write_str(ssh, msgbuf); sfree(msgbuf); s->publickey_blob = NULL; } } else s->publickey_blob = NULL; while (pktin->type == SSH1_SMSG_FAILURE) { s->pwpkt_type = SSH1_CMSG_AUTH_PASSWORD; if (conf_get_int(ssh->conf, CONF_tryagent) && agent_exists() && !s->tried_agent) { /* * Attempt RSA authentication using Pageant. */ void *r; s->authed = FALSE; s->tried_agent = 1; |
︙ | ︙ | |||
3615 3616 3617 3618 3619 3620 3621 | r = ssh->agent_response; s->responselen = ssh->agent_response_len; } s->response = (unsigned char *) r; if (s->response && s->responselen >= 5 && s->response[4] == SSH1_AGENT_RSA_IDENTITIES_ANSWER) { s->p = s->response + 5; | > > > > | > | | | | > | | 4065 4066 4067 4068 4069 4070 4071 4072 4073 4074 4075 4076 4077 4078 4079 4080 4081 4082 4083 4084 4085 4086 4087 4088 4089 4090 4091 4092 4093 4094 4095 4096 4097 4098 4099 4100 4101 4102 4103 4104 4105 4106 4107 4108 4109 4110 | r = ssh->agent_response; s->responselen = ssh->agent_response_len; } s->response = (unsigned char *) r; if (s->response && s->responselen >= 5 && s->response[4] == SSH1_AGENT_RSA_IDENTITIES_ANSWER) { s->p = s->response + 5; s->nkeys = toint(GET_32BIT(s->p)); if (s->nkeys < 0) { logeventf(ssh, "Pageant reported negative key count %d", s->nkeys); s->nkeys = 0; } s->p += 4; logeventf(ssh, "Pageant has %d SSH-1 keys", s->nkeys); for (s->keyi = 0; s->keyi < s->nkeys; s->keyi++) { unsigned char *pkblob = s->p; s->p += 4; { int n, ok = FALSE; do { /* do while (0) to make breaking easy */ n = ssh1_read_bignum (s->p, toint(s->responselen-(s->p-s->response)), &s->key.exponent); if (n < 0) break; s->p += n; n = ssh1_read_bignum (s->p, toint(s->responselen-(s->p-s->response)), &s->key.modulus); if (n < 0) break; s->p += n; if (s->responselen - (s->p-s->response) < 4) break; s->commentlen = toint(GET_32BIT(s->p)); s->p += 4; if (s->commentlen < 0 || toint(s->responselen - (s->p-s->response)) < s->commentlen) break; s->commentp = (char *)s->p; s->p += s->commentlen; ok = TRUE; } while (0); if (!ok) { |
︙ | ︙ | |||
3769 3770 3771 3772 3773 3774 3775 3776 | /* * Try public key authentication with the specified * key file. */ int got_passphrase; /* need not be kept over crReturn */ if (flags & FLAG_VERBOSE) c_write_str(ssh, "Trying public key authentication.\r\n"); logeventf(ssh, "Trying public key \"%s\"", | > | | < > | | | | 4225 4226 4227 4228 4229 4230 4231 4232 4233 4234 4235 4236 4237 4238 4239 4240 4241 4242 4243 4244 4245 4246 4247 4248 4249 4250 4251 4252 4253 4254 4255 4256 4257 4258 4259 4260 4261 4262 4263 4264 4265 4266 4267 4268 4269 4270 4271 4272 4273 4274 4275 4276 4277 4278 4279 4280 4281 4282 4283 4284 4285 4286 4287 4288 4289 4290 4291 4292 4293 4294 | /* * Try public key authentication with the specified * key file. */ int got_passphrase; /* need not be kept over crReturn */ if (flags & FLAG_VERBOSE) c_write_str(ssh, "Trying public key authentication.\r\n"); s->keyfile = conf_get_filename(ssh->conf, CONF_keyfile); logeventf(ssh, "Trying public key \"%s\"", filename_to_str(s->keyfile)); s->tried_publickey = 1; got_passphrase = FALSE; while (!got_passphrase) { /* * Get a passphrase, if necessary. */ char *passphrase = NULL; /* only written after crReturn */ const char *error; if (!s->publickey_encrypted) { if (flags & FLAG_VERBOSE) c_write_str(ssh, "No passphrase required.\r\n"); passphrase = NULL; } else { int ret; /* need not be kept over crReturn */ s->cur_prompt = new_prompts(ssh->frontend); s->cur_prompt->to_server = FALSE; s->cur_prompt->name = dupstr("SSH key passphrase"); add_prompt(s->cur_prompt, dupprintf("Passphrase for key \"%.100s\": ", s->publickey_comment), FALSE); ret = get_userpass_input(s->cur_prompt, NULL, 0); while (ret < 0) { ssh->send_ok = 1; crWaitUntil(!pktin); ret = get_userpass_input(s->cur_prompt, in, inlen); ssh->send_ok = 0; } if (!ret) { /* Failed to get a passphrase. Terminate. */ free_prompts(s->cur_prompt); ssh_disconnect(ssh, NULL, "Unable to authenticate", 0, TRUE); crStop(0); } passphrase = dupstr(s->cur_prompt->prompts[0]->result); free_prompts(s->cur_prompt); } /* * Try decrypting key with passphrase. */ s->keyfile = conf_get_filename(ssh->conf, CONF_keyfile); ret = loadrsakey(s->keyfile, &s->key, passphrase, &error); if (passphrase) { smemclr(passphrase, strlen(passphrase)); sfree(passphrase); } if (ret == 1) { /* Correct passphrase. */ got_passphrase = TRUE; } else if (ret == 0) { c_write_str(ssh, "Couldn't load private key from "); c_write_str(ssh, filename_to_str(s->keyfile)); c_write_str(ssh, " ("); c_write_str(ssh, error); c_write_str(ssh, ").\r\n"); got_passphrase = FALSE; break; /* go and try something else */ } else if (ret == -1) { c_write_str(ssh, "Wrong passphrase.\r\n"); /* FIXME */ |
︙ | ︙ | |||
3906 3907 3908 3909 3910 3911 3912 | } /* * Otherwise, try various forms of password-like authentication. */ s->cur_prompt = new_prompts(ssh->frontend); | | | 4363 4364 4365 4366 4367 4368 4369 4370 4371 4372 4373 4374 4375 4376 4377 | } /* * Otherwise, try various forms of password-like authentication. */ s->cur_prompt = new_prompts(ssh->frontend); if (conf_get_int(ssh->conf, CONF_try_tis_auth) && (s->supported_auths_mask & (1 << SSH1_AUTH_TIS)) && !s->tis_auth_refused) { s->pwpkt_type = SSH1_CMSG_AUTH_TIS_RESPONSE; logevent("Requested TIS authentication"); send_packet(ssh, SSH1_CMSG_AUTH_TIS, PKT_END); crWaitUntil(pktin); if (pktin->type != SSH1_SMSG_AUTH_TIS_CHALLENGE) { |
︙ | ︙ | |||
3945 3946 3947 3948 3949 3950 3951 | prompt = dupstr("Response: "); } s->cur_prompt->instruction = dupprintf("Using TIS authentication.%s%s", (*instr_suf) ? "\n" : "", instr_suf); s->cur_prompt->instr_reqd = TRUE; | | | | 4402 4403 4404 4405 4406 4407 4408 4409 4410 4411 4412 4413 4414 4415 4416 4417 4418 4419 4420 | prompt = dupstr("Response: "); } s->cur_prompt->instruction = dupprintf("Using TIS authentication.%s%s", (*instr_suf) ? "\n" : "", instr_suf); s->cur_prompt->instr_reqd = TRUE; add_prompt(s->cur_prompt, prompt, FALSE); sfree(instr_suf); } } if (conf_get_int(ssh->conf, CONF_try_tis_auth) && (s->supported_auths_mask & (1 << SSH1_AUTH_CCARD)) && !s->ccard_auth_refused) { s->pwpkt_type = SSH1_CMSG_AUTH_CCARD_RESPONSE; logevent("Requested CryptoCard authentication"); send_packet(ssh, SSH1_CMSG_AUTH_CCARD, PKT_END); crWaitUntil(pktin); if (pktin->type != SSH1_SMSG_AUTH_CCARD_CHALLENGE) { |
︙ | ︙ | |||
3988 3989 3990 3991 3992 3993 3994 | prompt = dupstr("Response: "); } s->cur_prompt->instruction = dupprintf("Using CryptoCard authentication.%s%s", (*instr_suf) ? "\n" : "", instr_suf); s->cur_prompt->instr_reqd = TRUE; | | | | | | 4445 4446 4447 4448 4449 4450 4451 4452 4453 4454 4455 4456 4457 4458 4459 4460 4461 4462 4463 4464 4465 4466 4467 4468 4469 4470 4471 4472 | prompt = dupstr("Response: "); } s->cur_prompt->instruction = dupprintf("Using CryptoCard authentication.%s%s", (*instr_suf) ? "\n" : "", instr_suf); s->cur_prompt->instr_reqd = TRUE; add_prompt(s->cur_prompt, prompt, FALSE); sfree(instr_suf); } } 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("%s@%s's password: ", ssh->username, ssh->savedhost), FALSE); } /* * Show password prompt, having first obtained it via a TIS * or CryptoCard exchange if we're doing TIS or CryptoCard * authentication. */ |
︙ | ︙ | |||
4093 4094 4095 4096 4097 4098 4099 | assert(pwlen >= bottom && pwlen <= top); randomstr = snewn(top + 1, char); for (i = bottom; i <= top; i++) { if (i == pwlen) { defer_packet(ssh, s->pwpkt_type, | < | | | 4550 4551 4552 4553 4554 4555 4556 4557 4558 4559 4560 4561 4562 4563 4564 4565 | assert(pwlen >= bottom && pwlen <= top); randomstr = snewn(top + 1, char); for (i = bottom; i <= top; i++) { if (i == pwlen) { defer_packet(ssh, s->pwpkt_type, PKT_STR,s->cur_prompt->prompts[0]->result, PKT_END); } else { for (j = 0; j < i; j++) { do { randomstr[j] = random_byte(); } while (randomstr[j] == '\0'); } randomstr[i] = '\0'; |
︙ | ︙ | |||
4133 4134 4135 4136 4137 4138 4139 | while (len < sizeof(string)) { string[len++] = (char) random_byte(); } } else { ss = s->cur_prompt->prompts[0]->result; } logevent("Sending length-padded password"); | | | | | | | | 4589 4590 4591 4592 4593 4594 4595 4596 4597 4598 4599 4600 4601 4602 4603 4604 4605 4606 4607 4608 4609 4610 4611 4612 4613 4614 4615 4616 4617 4618 4619 4620 4621 4622 | while (len < sizeof(string)) { string[len++] = (char) random_byte(); } } else { ss = s->cur_prompt->prompts[0]->result; } logevent("Sending length-padded password"); send_packet(ssh, s->pwpkt_type, PKT_INT, len, PKT_DATA, ss, len, PKT_END); } else { /* * The server is believed unable to cope with * any of our password camouflage methods. */ int len; len = strlen(s->cur_prompt->prompts[0]->result); logevent("Sending unpadded password"); send_packet(ssh, s->pwpkt_type, PKT_INT, len, PKT_DATA, s->cur_prompt->prompts[0]->result, len, PKT_END); } } else { send_packet(ssh, s->pwpkt_type, PKT_STR, s->cur_prompt->prompts[0]->result, PKT_END); } logevent("Sent password"); free_prompts(s->cur_prompt); crWaitUntil(pktin); if (pktin->type == SSH1_SMSG_FAILURE) { if (flags & FLAG_VERBOSE) c_write_str(ssh, "Access denied\r\n"); |
︙ | ︙ | |||
4178 4179 4180 4181 4182 4183 4184 | } logevent("Authentication successful"); crFinish(1); } | > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | < < < < < < < < < < < < < < < < | | | > > | | | | > | > > | > | | | 4634 4635 4636 4637 4638 4639 4640 4641 4642 4643 4644 4645 4646 4647 4648 4649 4650 4651 4652 4653 4654 4655 4656 4657 4658 4659 4660 4661 4662 4663 4664 4665 4666 4667 4668 4669 4670 4671 4672 4673 4674 4675 4676 4677 4678 4679 4680 4681 4682 4683 4684 4685 4686 4687 4688 4689 4690 4691 4692 4693 4694 4695 4696 4697 4698 4699 4700 4701 4702 4703 4704 4705 4706 4707 4708 4709 4710 4711 4712 4713 4714 4715 4716 4717 4718 4719 4720 4721 4722 4723 4724 4725 4726 4727 4728 | } logevent("Authentication successful"); crFinish(1); } static void ssh_channel_try_eof(struct ssh_channel *c) { Ssh ssh = c->ssh; assert(c->pending_eof); /* precondition for calling us */ if (c->halfopen) return; /* can't close: not even opened yet */ if (ssh->version == 2 && bufchain_size(&c->v.v2.outbuffer) > 0) return; /* can't send EOF: pending outgoing data */ c->pending_eof = FALSE; /* we're about to send it */ if (ssh->version == 1) { send_packet(ssh, SSH1_MSG_CHANNEL_CLOSE, PKT_INT, c->remoteid, PKT_END); c->closes |= CLOSES_SENT_EOF; } else { struct Packet *pktout; pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_EOF); ssh2_pkt_adduint32(pktout, c->remoteid); ssh2_pkt_send(ssh, pktout); c->closes |= CLOSES_SENT_EOF; ssh2_channel_check_close(c); } } Conf *sshfwd_get_conf(struct ssh_channel *c) { Ssh ssh = c->ssh; return ssh->conf; } void sshfwd_write_eof(struct ssh_channel *c) { Ssh ssh = c->ssh; if (ssh->state == SSH_STATE_CLOSED) return; if (c->closes & CLOSES_SENT_EOF) return; c->pending_eof = TRUE; ssh_channel_try_eof(c); } void sshfwd_unclean_close(struct ssh_channel *c, const char *err) { Ssh ssh = c->ssh; if (ssh->state == SSH_STATE_CLOSED) return; switch (c->type) { case CHAN_X11: x11_close(c->u.x11.xconn); logeventf(ssh, "Forwarded X11 connection terminated due to local " "error: %s", err); break; case CHAN_SOCKDATA: case CHAN_SOCKDATA_DORMANT: pfd_close(c->u.pfd.pf); logeventf(ssh, "Forwarded port closed due to local error: %s", err); break; } c->type = CHAN_ZOMBIE; c->pending_eof = FALSE; /* this will confuse a zombie channel */ ssh2_channel_check_close(c); } int sshfwd_write(struct ssh_channel *c, char *buf, int len) { Ssh ssh = c->ssh; if (ssh->state == SSH_STATE_CLOSED) return 0; if (ssh->version == 1) { send_packet(ssh, SSH1_MSG_CHANNEL_DATA, PKT_INT, c->remoteid, PKT_INT, len, PKT_DATA, buf, len, PKT_END); /* * In SSH-1 we can return 0 here - implying that forwarded * connections are never individually throttled - because * the only circumstance that can cause throttling will be * the whole SSH connection backing up, in which case * _everything_ will be throttled as a whole. */ |
︙ | ︙ | |||
4300 4301 4302 4303 4304 4305 4306 | assert(qh != NULL); assert(pktin->type == qh->msg1 || pktin->type == qh->msg2); if (qh->msg1 > 0) { assert(ssh->packet_dispatch[qh->msg1] == ssh_queueing_handler); | | | | | < | 4759 4760 4761 4762 4763 4764 4765 4766 4767 4768 4769 4770 4771 4772 4773 4774 4775 4776 4777 4778 4779 4780 4781 4782 4783 4784 4785 4786 4787 4788 4789 4790 4791 4792 | assert(qh != NULL); assert(pktin->type == qh->msg1 || pktin->type == qh->msg2); if (qh->msg1 > 0) { assert(ssh->packet_dispatch[qh->msg1] == ssh_queueing_handler); ssh->packet_dispatch[qh->msg1] = ssh->q_saved_handler1; } if (qh->msg2 > 0) { assert(ssh->packet_dispatch[qh->msg2] == ssh_queueing_handler); ssh->packet_dispatch[qh->msg2] = ssh->q_saved_handler2; } if (qh->next) { ssh->qhead = qh->next; if (ssh->qhead->msg1 > 0) { ssh->q_saved_handler1 = ssh->packet_dispatch[ssh->qhead->msg1]; ssh->packet_dispatch[ssh->qhead->msg1] = ssh_queueing_handler; } if (ssh->qhead->msg2 > 0) { ssh->q_saved_handler2 = ssh->packet_dispatch[ssh->qhead->msg2]; ssh->packet_dispatch[ssh->qhead->msg2] = ssh_queueing_handler; } } else { ssh->qhead = ssh->qtail = NULL; } qh->handler(ssh, pktin, qh->ctx); sfree(qh); } |
︙ | ︙ | |||
4344 4345 4346 4347 4348 4349 4350 | qh->ctx = ctx; qh->next = NULL; if (ssh->qtail == NULL) { ssh->qhead = qh; if (qh->msg1 > 0) { | | | | 4802 4803 4804 4805 4806 4807 4808 4809 4810 4811 4812 4813 4814 4815 4816 4817 4818 4819 4820 | qh->ctx = ctx; qh->next = NULL; if (ssh->qtail == NULL) { ssh->qhead = qh; if (qh->msg1 > 0) { ssh->q_saved_handler1 = ssh->packet_dispatch[ssh->qhead->msg1]; ssh->packet_dispatch[qh->msg1] = ssh_queueing_handler; } if (qh->msg2 > 0) { ssh->q_saved_handler2 = ssh->packet_dispatch[ssh->qhead->msg2]; ssh->packet_dispatch[qh->msg2] = ssh_queueing_handler; } } else { ssh->qtail->next = qh; } ssh->qtail = qh; } |
︙ | ︙ | |||
4376 4377 4378 4379 4380 4381 4382 | rpf = del234(ssh->rportfwds, pf); assert(rpf == pf); pf->pfrec->remote = NULL; free_rportfwd(pf); } } | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | < > > > | > > | | | < < | | < < | < | < < < | | | < | < < | < < < < < < | < < < < | < < < < < < < < < < < < < < < < < < < < < < < < < < < | | < < > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | 4834 4835 4836 4837 4838 4839 4840 4841 4842 4843 4844 4845 4846 4847 4848 4849 4850 4851 4852 4853 4854 4855 4856 4857 4858 4859 4860 4861 4862 4863 4864 4865 4866 4867 4868 4869 4870 4871 4872 4873 4874 4875 4876 4877 4878 4879 4880 4881 4882 4883 4884 4885 4886 4887 4888 4889 4890 4891 4892 4893 4894 4895 4896 4897 4898 4899 4900 4901 4902 4903 4904 4905 4906 4907 4908 4909 4910 4911 4912 4913 4914 4915 4916 4917 4918 4919 4920 4921 4922 4923 4924 4925 4926 4927 4928 4929 4930 4931 4932 4933 4934 4935 4936 4937 4938 4939 4940 4941 4942 4943 4944 4945 4946 4947 4948 4949 4950 4951 4952 4953 4954 4955 4956 4957 4958 4959 4960 4961 4962 4963 4964 4965 4966 4967 4968 4969 4970 4971 4972 4973 4974 4975 4976 4977 4978 4979 4980 4981 | rpf = del234(ssh->rportfwds, pf); assert(rpf == pf); pf->pfrec->remote = NULL; free_rportfwd(pf); } } int ssh_alloc_sharing_rportfwd(Ssh ssh, const char *shost, int sport, void *share_ctx) { struct ssh_rportfwd *pf = snew(struct ssh_rportfwd); pf->dhost = NULL; pf->dport = 0; pf->share_ctx = share_ctx; pf->shost = dupstr(shost); pf->sport = sport; pf->sportdesc = NULL; if (!ssh->rportfwds) { assert(ssh->version == 2); ssh->rportfwds = newtree234(ssh_rportcmp_ssh2); } if (add234(ssh->rportfwds, pf) != pf) { sfree(pf->shost); sfree(pf); return FALSE; } return TRUE; } static void ssh_sharing_global_request_response(Ssh ssh, struct Packet *pktin, void *ctx) { share_got_pkt_from_server(ctx, pktin->type, pktin->body, pktin->length); } void ssh_sharing_queue_global_request(Ssh ssh, void *share_ctx) { ssh_queue_handler(ssh, SSH2_MSG_REQUEST_SUCCESS, SSH2_MSG_REQUEST_FAILURE, ssh_sharing_global_request_response, share_ctx); } static void ssh_setup_portfwd(Ssh ssh, Conf *conf) { struct ssh_portfwd *epf; int i; char *key, *val; if (!ssh->portfwds) { ssh->portfwds = newtree234(ssh_portcmp); } else { /* * Go through the existing port forwardings and tag them * with status==DESTROY. Any that we want to keep will be * re-enabled (status==KEEP) as we go through the * configuration and find out which bits are the same as * they were before. */ struct ssh_portfwd *epf; int i; for (i = 0; (epf = index234(ssh->portfwds, i)) != NULL; i++) epf->status = DESTROY; } for (val = conf_get_str_strs(conf, CONF_portfwd, NULL, &key); val != NULL; val = conf_get_str_strs(conf, CONF_portfwd, key, &key)) { char *kp, *kp2, *vp, *vp2; char address_family, type; int sport,dport,sserv,dserv; char *sports, *dports, *saddr, *host; kp = key; address_family = 'A'; type = 'L'; if (*kp == 'A' || *kp == '4' || *kp == '6') address_family = *kp++; if (*kp == 'L' || *kp == 'R') type = *kp++; if ((kp2 = strchr(kp, ':')) != NULL) { /* * There's a colon in the middle of the source port * string, which means that the part before it is * actually a source address. */ saddr = dupprintf("%.*s", (int)(kp2 - kp), kp); sports = kp2+1; } else { saddr = NULL; sports = kp; } sport = atoi(sports); sserv = 0; if (sport == 0) { sserv = 1; sport = net_service_lookup(sports); if (!sport) { logeventf(ssh, "Service lookup failed for source" " port \"%s\"", sports); } } if (type == 'L' && !strcmp(val, "D")) { /* dynamic forwarding */ host = NULL; dports = NULL; dport = -1; dserv = 0; type = 'D'; } else { /* ordinary forwarding */ vp = val; vp2 = vp + strcspn(vp, ":"); host = dupprintf("%.*s", (int)(vp2 - vp), vp); if (vp2) vp2++; dports = vp2; dport = atoi(dports); dserv = 0; if (dport == 0) { dserv = 1; dport = net_service_lookup(dports); if (!dport) { logeventf(ssh, "Service lookup failed for destination" " port \"%s\"", dports); } } } if (sport && dport) { /* Set up a description of the source port. */ struct ssh_portfwd *pfrec, *epfrec; pfrec = snew(struct ssh_portfwd); pfrec->type = type; pfrec->saddr = saddr; pfrec->sserv = sserv ? dupstr(sports) : NULL; pfrec->sport = sport; pfrec->daddr = host; pfrec->dserv = dserv ? dupstr(dports) : NULL; pfrec->dport = dport; pfrec->local = NULL; pfrec->remote = NULL; pfrec->addressfamily = (address_family == '4' ? ADDRTYPE_IPV4 : address_family == '6' ? ADDRTYPE_IPV6 : ADDRTYPE_UNSPEC); |
︙ | ︙ | |||
4520 4521 4522 4523 4524 4525 4526 4527 4528 4529 4530 4531 4532 4533 | * Anything else indicates that there was a duplicate * in our input, which we'll silently ignore. */ free_portfwd(pfrec); } else { pfrec->status = CREATE; } } } /* * Now go through and destroy any port forwardings which were * not re-enabled. */ | > > > | 4995 4996 4997 4998 4999 5000 5001 5002 5003 5004 5005 5006 5007 5008 5009 5010 5011 | * Anything else indicates that there was a duplicate * in our input, which we'll silently ignore. */ free_portfwd(pfrec); } else { pfrec->status = CREATE; } } else { sfree(saddr); sfree(host); } } /* * Now go through and destroy any port forwardings which were * not re-enabled. */ |
︙ | ︙ | |||
4573 4574 4575 4576 4577 4578 4579 | */ } else { pktout = ssh2_pkt_init(SSH2_MSG_GLOBAL_REQUEST); ssh2_pkt_addstring(pktout, "cancel-tcpip-forward"); ssh2_pkt_addbool(pktout, 0);/* _don't_ want reply */ if (epf->saddr) { ssh2_pkt_addstring(pktout, epf->saddr); | | | | | | | 5051 5052 5053 5054 5055 5056 5057 5058 5059 5060 5061 5062 5063 5064 5065 5066 5067 5068 5069 5070 5071 5072 5073 5074 5075 5076 5077 5078 5079 5080 | */ } else { pktout = ssh2_pkt_init(SSH2_MSG_GLOBAL_REQUEST); ssh2_pkt_addstring(pktout, "cancel-tcpip-forward"); ssh2_pkt_addbool(pktout, 0);/* _don't_ want reply */ if (epf->saddr) { ssh2_pkt_addstring(pktout, epf->saddr); } else if (conf_get_int(conf, CONF_rport_acceptall)) { /* XXX: rport_acceptall may not represent * what was used to open the original connection, * since it's reconfigurable. */ ssh2_pkt_addstring(pktout, ""); } else { ssh2_pkt_addstring(pktout, "localhost"); } ssh2_pkt_adduint32(pktout, epf->sport); ssh2_pkt_send(ssh, pktout); } del234(ssh->rportfwds, rpf); free_rportfwd(rpf); } else if (epf->local) { pfl_terminate(epf->local); } delpos234(ssh->portfwds, i); free_portfwd(epf); i--; /* so we don't skip one in the list */ } |
︙ | ︙ | |||
4621 4622 4623 4624 4625 4626 4627 | epf->dserv ? epf->dserv : "", epf->dserv ? "(" : "", epf->dport, epf->dserv ? ")" : ""); } if (epf->type == 'L') { | | | < | | > > < | < | | > > > | | > > > > > > > | 5099 5100 5101 5102 5103 5104 5105 5106 5107 5108 5109 5110 5111 5112 5113 5114 5115 5116 5117 5118 5119 5120 5121 5122 5123 5124 5125 5126 5127 5128 5129 5130 5131 5132 5133 5134 5135 5136 5137 5138 5139 5140 5141 5142 5143 5144 5145 5146 5147 5148 5149 5150 5151 5152 5153 5154 5155 5156 5157 5158 5159 5160 5161 | epf->dserv ? epf->dserv : "", epf->dserv ? "(" : "", epf->dport, epf->dserv ? ")" : ""); } if (epf->type == 'L') { char *err = pfl_listen(epf->daddr, epf->dport, epf->saddr, epf->sport, ssh, conf, &epf->local, epf->addressfamily); logeventf(ssh, "Local %sport %s forwarding to %s%s%s", epf->addressfamily == ADDRTYPE_IPV4 ? "IPv4 " : epf->addressfamily == ADDRTYPE_IPV6 ? "IPv6 " : "", sportdesc, dportdesc, err ? " failed: " : "", err ? err : ""); if (err) sfree(err); } else if (epf->type == 'D') { char *err = pfl_listen(NULL, -1, epf->saddr, epf->sport, ssh, conf, &epf->local, epf->addressfamily); logeventf(ssh, "Local %sport %s SOCKS dynamic forwarding%s%s", epf->addressfamily == ADDRTYPE_IPV4 ? "IPv4 " : epf->addressfamily == ADDRTYPE_IPV6 ? "IPv6 " : "", sportdesc, err ? " failed: " : "", err ? err : ""); if (err) sfree(err); } else { struct ssh_rportfwd *pf; /* * Ensure the remote port forwardings tree exists. */ if (!ssh->rportfwds) { if (ssh->version == 1) ssh->rportfwds = newtree234(ssh_rportcmp_ssh1); else ssh->rportfwds = newtree234(ssh_rportcmp_ssh2); } pf = snew(struct ssh_rportfwd); pf->share_ctx = NULL; pf->dhost = dupstr(epf->daddr); pf->dport = epf->dport; if (epf->saddr) { pf->shost = dupstr(epf->saddr); } else if (conf_get_int(conf, CONF_rport_acceptall)) { pf->shost = dupstr(""); } else { pf->shost = dupstr("localhost"); } pf->sport = epf->sport; if (add234(ssh->rportfwds, pf) != pf) { logeventf(ssh, "Duplicate remote port forwarding to %s:%d", epf->daddr, epf->dport); sfree(pf); } else { logeventf(ssh, "Requesting remote port %s" |
︙ | ︙ | |||
4689 4690 4691 4692 4693 4694 4695 | SSH1_SMSG_FAILURE, ssh_rportfwd_succfail, pf); } else { struct Packet *pktout; pktout = ssh2_pkt_init(SSH2_MSG_GLOBAL_REQUEST); ssh2_pkt_addstring(pktout, "tcpip-forward"); ssh2_pkt_addbool(pktout, 1);/* want reply */ | < < < < < | < | | 5176 5177 5178 5179 5180 5181 5182 5183 5184 5185 5186 5187 5188 5189 5190 5191 | SSH1_SMSG_FAILURE, ssh_rportfwd_succfail, pf); } else { struct Packet *pktout; pktout = ssh2_pkt_init(SSH2_MSG_GLOBAL_REQUEST); ssh2_pkt_addstring(pktout, "tcpip-forward"); ssh2_pkt_addbool(pktout, 1);/* want reply */ ssh2_pkt_addstring(pktout, pf->shost); ssh2_pkt_adduint32(pktout, pf->sport); ssh2_pkt_send(ssh, pktout); ssh_queue_handler(ssh, SSH2_MSG_REQUEST_SUCCESS, SSH2_MSG_REQUEST_FAILURE, ssh_rportfwd_succfail, pf); } } |
︙ | ︙ | |||
4746 4747 4748 4749 4750 4751 4752 | send_packet(ssh, SSH1_MSG_CHANNEL_OPEN_FAILURE, PKT_INT, remoteid, PKT_END); logevent("Rejected X11 connect request"); } else { c = snew(struct ssh_channel); c->ssh = ssh; | | < < < < < < < < | | | | | | | | | | | | < | 5227 5228 5229 5230 5231 5232 5233 5234 5235 5236 5237 5238 5239 5240 5241 5242 5243 5244 5245 5246 5247 5248 5249 5250 5251 5252 5253 | send_packet(ssh, SSH1_MSG_CHANNEL_OPEN_FAILURE, PKT_INT, remoteid, PKT_END); logevent("Rejected X11 connect request"); } else { c = snew(struct ssh_channel); c->ssh = ssh; c->u.x11.xconn = x11_init(ssh->x11authtree, c, NULL, -1); c->remoteid = remoteid; c->halfopen = FALSE; c->localid = alloc_channel_id(ssh); c->closes = 0; c->pending_eof = FALSE; c->throttling_conn = 0; c->type = CHAN_X11; /* identify channel type */ add234(ssh->channels, c); send_packet(ssh, SSH1_MSG_CHANNEL_OPEN_CONFIRMATION, PKT_INT, c->remoteid, PKT_INT, c->localid, PKT_END); logevent("Opened X11 forward channel"); } } static void ssh1_smsg_agent_open(Ssh ssh, struct Packet *pktin) { /* Remote side is trying to open a channel to talk to our * agent. Give them back a local channel number. */ |
︙ | ︙ | |||
4789 4790 4791 4792 4793 4794 4795 | } else { c = snew(struct ssh_channel); c->ssh = ssh; c->remoteid = remoteid; c->halfopen = FALSE; c->localid = alloc_channel_id(ssh); c->closes = 0; | | > > < | < < < < | < > > > | | | | > | > > | | | < | | < | > > > > > > | | | > | | < > > > | < | | > | > > > > > | > > > > | > | > > > | > > > | < | | > | | | > | > > > | 5261 5262 5263 5264 5265 5266 5267 5268 5269 5270 5271 5272 5273 5274 5275 5276 5277 5278 5279 5280 5281 5282 5283 5284 5285 5286 5287 5288 5289 5290 5291 5292 5293 5294 5295 5296 5297 5298 5299 5300 5301 5302 5303 5304 5305 5306 5307 5308 5309 5310 5311 5312 5313 5314 5315 5316 5317 5318 5319 5320 5321 5322 5323 5324 5325 5326 5327 5328 5329 5330 5331 5332 5333 5334 5335 5336 5337 5338 5339 5340 5341 5342 5343 5344 5345 5346 5347 5348 5349 5350 5351 5352 5353 5354 5355 5356 5357 5358 5359 5360 5361 5362 5363 5364 5365 5366 5367 5368 5369 5370 5371 5372 5373 5374 5375 5376 5377 5378 5379 5380 5381 5382 5383 5384 5385 5386 5387 5388 5389 5390 5391 5392 5393 5394 5395 5396 5397 5398 5399 5400 5401 5402 5403 5404 5405 5406 5407 5408 5409 5410 5411 5412 5413 5414 5415 5416 5417 5418 5419 5420 5421 5422 5423 5424 5425 5426 5427 5428 5429 5430 5431 5432 5433 5434 5435 5436 5437 5438 5439 5440 5441 5442 5443 5444 5445 5446 | } else { c = snew(struct ssh_channel); c->ssh = ssh; c->remoteid = remoteid; c->halfopen = FALSE; c->localid = alloc_channel_id(ssh); c->closes = 0; c->pending_eof = FALSE; c->throttling_conn = 0; c->type = CHAN_AGENT; /* identify channel type */ c->u.a.lensofar = 0; c->u.a.message = NULL; c->u.a.outstanding_requests = 0; add234(ssh->channels, c); send_packet(ssh, SSH1_MSG_CHANNEL_OPEN_CONFIRMATION, PKT_INT, c->remoteid, PKT_INT, c->localid, PKT_END); } } static void ssh1_msg_port_open(Ssh ssh, struct Packet *pktin) { /* Remote side is trying to open a channel to talk to a * forwarded port. Give them back a local channel number. */ struct ssh_rportfwd pf, *pfp; int remoteid; int hostsize, port; char *host; char *err; remoteid = ssh_pkt_getuint32(pktin); ssh_pkt_getstring(pktin, &host, &hostsize); port = ssh_pkt_getuint32(pktin); pf.dhost = dupprintf("%.*s", hostsize, host); pf.dport = port; pfp = find234(ssh->rportfwds, &pf, NULL); if (pfp == NULL) { logeventf(ssh, "Rejected remote port open request for %s:%d", pf.dhost, port); send_packet(ssh, SSH1_MSG_CHANNEL_OPEN_FAILURE, PKT_INT, remoteid, PKT_END); } else { struct ssh_channel *c = snew(struct ssh_channel); c->ssh = ssh; logeventf(ssh, "Received remote port open request for %s:%d", pf.dhost, port); err = pfd_connect(&c->u.pfd.pf, pf.dhost, port, c, ssh->conf, pfp->pfrec->addressfamily); if (err != NULL) { logeventf(ssh, "Port open failed: %s", err); sfree(err); sfree(c); send_packet(ssh, SSH1_MSG_CHANNEL_OPEN_FAILURE, PKT_INT, remoteid, PKT_END); } else { c->remoteid = remoteid; c->halfopen = FALSE; c->localid = alloc_channel_id(ssh); c->closes = 0; c->pending_eof = FALSE; c->throttling_conn = 0; c->type = CHAN_SOCKDATA; /* identify channel type */ add234(ssh->channels, c); send_packet(ssh, SSH1_MSG_CHANNEL_OPEN_CONFIRMATION, PKT_INT, c->remoteid, PKT_INT, c->localid, PKT_END); logevent("Forwarded port opened successfully"); } } sfree(pf.dhost); } static void ssh1_msg_channel_open_confirmation(Ssh ssh, struct Packet *pktin) { unsigned int remoteid = ssh_pkt_getuint32(pktin); unsigned int localid = ssh_pkt_getuint32(pktin); struct ssh_channel *c; c = find234(ssh->channels, &remoteid, ssh_channelfind); if (c && c->type == CHAN_SOCKDATA_DORMANT) { c->remoteid = localid; c->halfopen = FALSE; c->type = CHAN_SOCKDATA; c->throttling_conn = 0; pfd_confirm(c->u.pfd.pf); } if (c && c->pending_eof) { /* * We have a pending close on this channel, * which we decided on before the server acked * the channel open. So now we know the * remoteid, we can close it again. */ ssh_channel_try_eof(c); } } static void ssh1_msg_channel_open_failure(Ssh ssh, struct Packet *pktin) { unsigned int remoteid = ssh_pkt_getuint32(pktin); struct ssh_channel *c; c = find234(ssh->channels, &remoteid, ssh_channelfind); if (c && c->type == CHAN_SOCKDATA_DORMANT) { logevent("Forwarded connection refused by server"); pfd_close(c->u.pfd.pf); del234(ssh->channels, c); sfree(c); } } static void ssh1_msg_channel_close(Ssh ssh, struct Packet *pktin) { /* Remote side closes a channel. */ unsigned i = ssh_pkt_getuint32(pktin); struct ssh_channel *c; c = find234(ssh->channels, &i, ssh_channelfind); if (c && !c->halfopen) { if (pktin->type == SSH1_MSG_CHANNEL_CLOSE && !(c->closes & CLOSES_RCVD_EOF)) { /* * Received CHANNEL_CLOSE, which we translate into * outgoing EOF. */ int send_close = FALSE; c->closes |= CLOSES_RCVD_EOF; switch (c->type) { case CHAN_X11: if (c->u.x11.xconn) x11_send_eof(c->u.x11.xconn); else send_close = TRUE; break; case CHAN_SOCKDATA: if (c->u.pfd.pf) pfd_send_eof(c->u.pfd.pf); else send_close = TRUE; break; case CHAN_AGENT: send_close = TRUE; break; } if (send_close && !(c->closes & CLOSES_SENT_EOF)) { send_packet(ssh, SSH1_MSG_CHANNEL_CLOSE, PKT_INT, c->remoteid, PKT_END); c->closes |= CLOSES_SENT_EOF; } } if (pktin->type == SSH1_MSG_CHANNEL_CLOSE_CONFIRMATION && !(c->closes & CLOSES_RCVD_CLOSE)) { if (!(c->closes & CLOSES_SENT_EOF)) { bombout(("Received CHANNEL_CLOSE_CONFIRMATION for channel %d" " for which we never sent CHANNEL_CLOSE\n", i)); } c->closes |= CLOSES_RCVD_CLOSE; } if (!((CLOSES_SENT_EOF | CLOSES_RCVD_EOF) & ~c->closes) && !(c->closes & CLOSES_SENT_CLOSE)) { send_packet(ssh, SSH1_MSG_CHANNEL_CLOSE_CONFIRMATION, PKT_INT, c->remoteid, PKT_END); c->closes |= CLOSES_SENT_CLOSE; } if (!((CLOSES_SENT_CLOSE | CLOSES_RCVD_CLOSE) & ~c->closes)) ssh_channel_destroy(c); } else { bombout(("Received CHANNEL_CLOSE%s for %s channel %d\n", pktin->type == SSH1_MSG_CHANNEL_CLOSE ? "" : "_CONFIRMATION", c ? "half-open" : "nonexistent", i)); } } |
︙ | ︙ | |||
4955 4956 4957 4958 4959 4960 4961 | ssh_pkt_getstring(pktin, &p, &len); c = find234(ssh->channels, &i, ssh_channelfind); if (c) { int bufsize = 0; switch (c->type) { case CHAN_X11: | | | | 5456 5457 5458 5459 5460 5461 5462 5463 5464 5465 5466 5467 5468 5469 5470 5471 5472 5473 | ssh_pkt_getstring(pktin, &p, &len); c = find234(ssh->channels, &i, ssh_channelfind); if (c) { int bufsize = 0; switch (c->type) { case CHAN_X11: bufsize = x11_send(c->u.x11.xconn, p, len); break; case CHAN_SOCKDATA: bufsize = pfd_send(c->u.pfd.pf, p, len); break; case CHAN_AGENT: /* Data for an agent message. Buffer it. */ while (len > 0) { if (c->u.a.lensofar < 4) { unsigned int l = min(4 - c->u.a.lensofar, (unsigned)len); memcpy(c->u.a.msglen + c->u.a.lensofar, p, |
︙ | ︙ | |||
4991 4992 4993 4994 4995 4996 4997 4998 4999 5000 5001 5002 5003 5004 | p += l; len -= l; c->u.a.lensofar += l; } if (c->u.a.lensofar == c->u.a.totallen) { void *reply; int replylen; if (agent_query(c->u.a.message, c->u.a.totallen, &reply, &replylen, ssh_agentf_callback, c)) ssh_agentf_callback(c, reply, replylen); sfree(c->u.a.message); c->u.a.lensofar = 0; | > | 5492 5493 5494 5495 5496 5497 5498 5499 5500 5501 5502 5503 5504 5505 5506 | p += l; len -= l; c->u.a.lensofar += l; } if (c->u.a.lensofar == c->u.a.totallen) { void *reply; int replylen; c->u.a.outstanding_requests++; if (agent_query(c->u.a.message, c->u.a.totallen, &reply, &replylen, ssh_agentf_callback, c)) ssh_agentf_callback(c, reply, replylen); sfree(c->u.a.message); c->u.a.lensofar = 0; |
︙ | ︙ | |||
5045 5046 5047 5048 5049 5050 5051 5052 5053 5054 5055 5056 5057 5058 5059 5060 5061 5062 5063 5064 5065 5066 5067 5068 5069 5070 5071 | arg = ssh_tty_parse_boolean(val); break; } ssh2_pkt_addbyte(pktout, ssh_ttymodes[i].opcode); ssh2_pkt_addbyte(pktout, arg); } static void do_ssh1_connection(Ssh ssh, unsigned char *in, int inlen, struct Packet *pktin) { crBegin(ssh->do_ssh1_connection_crstate); ssh->packet_dispatch[SSH1_SMSG_STDOUT_DATA] = ssh->packet_dispatch[SSH1_SMSG_STDERR_DATA] = ssh1_smsg_stdout_stderr_data; ssh->packet_dispatch[SSH1_MSG_CHANNEL_OPEN_CONFIRMATION] = ssh1_msg_channel_open_confirmation; ssh->packet_dispatch[SSH1_MSG_CHANNEL_OPEN_FAILURE] = ssh1_msg_channel_open_failure; ssh->packet_dispatch[SSH1_MSG_CHANNEL_CLOSE] = ssh->packet_dispatch[SSH1_MSG_CHANNEL_CLOSE_CONFIRMATION] = ssh1_msg_channel_close; ssh->packet_dispatch[SSH1_MSG_CHANNEL_DATA] = ssh1_msg_channel_data; ssh->packet_dispatch[SSH1_SMSG_EXIT_STATUS] = ssh1_smsg_exit_status; | > > > > | | | > | > > > > > > > > > | < < < < < < < | | | < | < | | | | | < | < | | | | | | | | | | | | | | | | | | > | | | | | < | | | > > | | 5547 5548 5549 5550 5551 5552 5553 5554 5555 5556 5557 5558 5559 5560 5561 5562 5563 5564 5565 5566 5567 5568 5569 5570 5571 5572 5573 5574 5575 5576 5577 5578 5579 5580 5581 5582 5583 5584 5585 5586 5587 5588 5589 5590 5591 5592 5593 5594 5595 5596 5597 5598 5599 5600 5601 5602 5603 5604 5605 5606 5607 5608 5609 5610 5611 5612 5613 5614 5615 5616 5617 5618 5619 5620 5621 5622 5623 5624 5625 5626 5627 5628 5629 5630 5631 5632 5633 5634 5635 5636 5637 5638 5639 5640 5641 5642 5643 5644 5645 5646 5647 5648 5649 5650 5651 5652 5653 5654 5655 5656 5657 5658 5659 5660 5661 5662 5663 5664 5665 5666 5667 5668 5669 5670 5671 5672 5673 5674 5675 5676 5677 5678 5679 5680 5681 5682 5683 5684 5685 5686 5687 5688 5689 5690 | arg = ssh_tty_parse_boolean(val); break; } ssh2_pkt_addbyte(pktout, ssh_ttymodes[i].opcode); ssh2_pkt_addbyte(pktout, arg); } int ssh_agent_forwarding_permitted(Ssh ssh) { return conf_get_int(ssh->conf, CONF_agentfwd) && agent_exists(); } static void do_ssh1_connection(Ssh ssh, unsigned char *in, int inlen, struct Packet *pktin) { crBegin(ssh->do_ssh1_connection_crstate); ssh->packet_dispatch[SSH1_SMSG_STDOUT_DATA] = ssh->packet_dispatch[SSH1_SMSG_STDERR_DATA] = ssh1_smsg_stdout_stderr_data; ssh->packet_dispatch[SSH1_MSG_CHANNEL_OPEN_CONFIRMATION] = ssh1_msg_channel_open_confirmation; ssh->packet_dispatch[SSH1_MSG_CHANNEL_OPEN_FAILURE] = ssh1_msg_channel_open_failure; ssh->packet_dispatch[SSH1_MSG_CHANNEL_CLOSE] = ssh->packet_dispatch[SSH1_MSG_CHANNEL_CLOSE_CONFIRMATION] = ssh1_msg_channel_close; ssh->packet_dispatch[SSH1_MSG_CHANNEL_DATA] = ssh1_msg_channel_data; ssh->packet_dispatch[SSH1_SMSG_EXIT_STATUS] = ssh1_smsg_exit_status; if (ssh_agent_forwarding_permitted(ssh)) { logevent("Requesting agent forwarding"); send_packet(ssh, SSH1_CMSG_AGENT_REQUEST_FORWARDING, PKT_END); do { crReturnV; } while (!pktin); if (pktin->type != SSH1_SMSG_SUCCESS && pktin->type != SSH1_SMSG_FAILURE) { bombout(("Protocol confusion")); crStopV; } else if (pktin->type == SSH1_SMSG_FAILURE) { logevent("Agent forwarding refused"); } else { logevent("Agent forwarding enabled"); ssh->agentfwd_enabled = TRUE; ssh->packet_dispatch[SSH1_SMSG_AGENT_OPEN] = ssh1_smsg_agent_open; } } if (conf_get_int(ssh->conf, CONF_x11_forward)) { ssh->x11disp = x11_setup_display(conf_get_str(ssh->conf, CONF_x11_display), ssh->conf); if (!ssh->x11disp) { /* FIXME: return an error message from x11_setup_display */ logevent("X11 forwarding not enabled: unable to" " initialise X display"); } else { ssh->x11auth = x11_invent_fake_auth (ssh->x11authtree, conf_get_int(ssh->conf, CONF_x11_auth)); ssh->x11auth->disp = ssh->x11disp; logevent("Requesting X11 forwarding"); if (ssh->v1_local_protoflags & SSH1_PROTOFLAG_SCREEN_NUMBER) { send_packet(ssh, SSH1_CMSG_X11_REQUEST_FORWARDING, PKT_STR, ssh->x11auth->protoname, PKT_STR, ssh->x11auth->datastring, PKT_INT, ssh->x11disp->screennum, PKT_END); } else { send_packet(ssh, SSH1_CMSG_X11_REQUEST_FORWARDING, PKT_STR, ssh->x11auth->protoname, PKT_STR, ssh->x11auth->datastring, PKT_END); } do { crReturnV; } while (!pktin); if (pktin->type != SSH1_SMSG_SUCCESS && pktin->type != SSH1_SMSG_FAILURE) { bombout(("Protocol confusion")); crStopV; } else if (pktin->type == SSH1_SMSG_FAILURE) { logevent("X11 forwarding refused"); } else { logevent("X11 forwarding enabled"); ssh->X11_fwd_enabled = TRUE; ssh->packet_dispatch[SSH1_SMSG_X11_OPEN] = ssh1_smsg_x11_open; } } } ssh_setup_portfwd(ssh, ssh->conf); ssh->packet_dispatch[SSH1_MSG_PORT_OPEN] = ssh1_msg_port_open; if (!conf_get_int(ssh->conf, CONF_nopty)) { struct Packet *pkt; /* Unpick the terminal-speed string. */ /* XXX perhaps we should allow no speeds to be sent. */ ssh->ospeed = 38400; ssh->ispeed = 38400; /* last-resort defaults */ sscanf(conf_get_str(ssh->conf, CONF_termspeed), "%d,%d", &ssh->ospeed, &ssh->ispeed); /* Send the pty request. */ pkt = ssh1_pkt_init(SSH1_CMSG_REQUEST_PTY); ssh_pkt_addstring(pkt, conf_get_str(ssh->conf, CONF_termtype)); ssh_pkt_adduint32(pkt, ssh->term_height); ssh_pkt_adduint32(pkt, ssh->term_width); ssh_pkt_adduint32(pkt, 0); /* width in pixels */ ssh_pkt_adduint32(pkt, 0); /* height in pixels */ parse_ttymodes(ssh, ssh1_send_ttymode, (void *)pkt); ssh_pkt_addbyte(pkt, SSH1_TTY_OP_ISPEED); ssh_pkt_adduint32(pkt, ssh->ispeed); ssh_pkt_addbyte(pkt, SSH1_TTY_OP_OSPEED); ssh_pkt_adduint32(pkt, ssh->ospeed); ssh_pkt_addbyte(pkt, SSH_TTY_OP_END); s_wrpkt(ssh, pkt); ssh->state = SSH_STATE_INTERMED; do { crReturnV; } while (!pktin); if (pktin->type != SSH1_SMSG_SUCCESS && pktin->type != SSH1_SMSG_FAILURE) { bombout(("Protocol confusion")); crStopV; } else if (pktin->type == SSH1_SMSG_FAILURE) { c_write_str(ssh, "Server refused to allocate pty\r\n"); ssh->editing = ssh->echoing = 1; } else { logeventf(ssh, "Allocated pty (ospeed %dbps, ispeed %dbps)", ssh->ospeed, ssh->ispeed); ssh->got_pty = TRUE; } } else { ssh->editing = ssh->echoing = 1; } if (conf_get_int(ssh->conf, CONF_compression)) { send_packet(ssh, SSH1_CMSG_REQUEST_COMPRESSION, PKT_INT, 6, PKT_END); do { crReturnV; } while (!pktin); if (pktin->type != SSH1_SMSG_SUCCESS && pktin->type != SSH1_SMSG_FAILURE) { bombout(("Protocol confusion")); |
︙ | ︙ | |||
5197 5198 5199 5200 5201 5202 5203 | * Start the shell or command. * * Special case: if the first-choice command is an SSH-2 * subsystem (hence not usable here) and the second choice * exists, we fall straight back to that. */ { | | | < | | | | 5704 5705 5706 5707 5708 5709 5710 5711 5712 5713 5714 5715 5716 5717 5718 5719 5720 5721 5722 | * Start the shell or command. * * Special case: if the first-choice command is an SSH-2 * subsystem (hence not usable here) and the second choice * exists, we fall straight back to that. */ { char *cmd = conf_get_str(ssh->conf, CONF_remote_cmd); if (conf_get_int(ssh->conf, CONF_ssh_subsys) && conf_get_str(ssh->conf, CONF_remote_cmd2)) { cmd = conf_get_str(ssh->conf, CONF_remote_cmd2); ssh->fallback_cmd = TRUE; } if (*cmd) send_packet(ssh, SSH1_CMSG_EXEC_CMD, PKT_STR, cmd, PKT_END); else send_packet(ssh, SSH1_CMSG_EXEC_SHELL, PKT_END); logevent("Started session"); |
︙ | ︙ | |||
5245 5246 5247 5248 5249 5250 5251 | bombout(("Strange packet received: type %d", pktin->type)); crStopV; } } else { while (inlen > 0) { int len = min(inlen, 512); send_packet(ssh, SSH1_CMSG_STDIN_DATA, | | | | 5751 5752 5753 5754 5755 5756 5757 5758 5759 5760 5761 5762 5763 5764 5765 5766 | bombout(("Strange packet received: type %d", pktin->type)); crStopV; } } else { while (inlen > 0) { int len = min(inlen, 512); send_packet(ssh, SSH1_CMSG_STDIN_DATA, PKT_INT, len, PKT_DATA, in, len, PKT_END); in += len; inlen -= len; } } } crFinishV; |
︙ | ︙ | |||
5407 5408 5409 5410 5411 5412 5413 | h->bytes(s, keyspace, h->hlen); h->final(s, keyspace + h->hlen); } /* * Handle the SSH-2 transport layer. */ | | > | 5913 5914 5915 5916 5917 5918 5919 5920 5921 5922 5923 5924 5925 5926 5927 5928 5929 5930 5931 5932 | h->bytes(s, keyspace, h->hlen); h->final(s, keyspace + h->hlen); } /* * Handle the SSH-2 transport layer. */ static void do_ssh2_transport(Ssh ssh, void *vin, int inlen, struct Packet *pktin) { unsigned char *in = (unsigned char *)vin; struct do_ssh2_transport_state { int crLine; int nbits, pbits, warn_kex, warn_cscipher, warn_sccipher; Bignum p, g, e, f, K; void *our_kexinit; int our_kexinitlen; int kex_init_value, kex_reply_value; const struct ssh_mac **maclist; int nmacs; |
︙ | ︙ | |||
5445 5446 5447 5448 5449 5450 5451 | struct Packet *pktout; int dlgret; int guessok; int ignorepkt; }; crState(do_ssh2_transport_state); | > > | | | | 5952 5953 5954 5955 5956 5957 5958 5959 5960 5961 5962 5963 5964 5965 5966 5967 5968 5969 5970 5971 5972 5973 5974 5975 5976 5977 5978 5979 5980 5981 5982 5983 5984 5985 5986 5987 5988 5989 5990 5991 5992 5993 5994 5995 5996 | struct Packet *pktout; int dlgret; int guessok; int ignorepkt; }; crState(do_ssh2_transport_state); assert(!ssh->bare_connection); crBeginState; s->cscipher_tobe = s->sccipher_tobe = NULL; s->csmac_tobe = s->scmac_tobe = NULL; s->cscomp_tobe = s->sccomp_tobe = NULL; s->got_session_id = s->activated_authconn = FALSE; s->userauth_succeeded = FALSE; s->pending_compression = FALSE; /* * Be prepared to work around the buggy MAC problem. */ if (ssh->remote_bugs & BUG_SSH2_HMAC) s->maclist = buggymacs, s->nmacs = lenof(buggymacs); else s->maclist = macs, s->nmacs = lenof(macs); begin_key_exchange: ssh->pkt_kctx = SSH2_PKTCTX_NOKEX; { int i, j, k, commalist_started; /* * Set up the preferred key exchange. (NULL => warn below here) */ s->n_preferred_kex = 0; for (i = 0; i < KEX_MAX; i++) { switch (conf_get_int_int(ssh->conf, CONF_ssh_kexlist, i)) { case KEX_DHGEX: s->preferred_kex[s->n_preferred_kex++] = &ssh_diffiehellman_gex; break; case KEX_DHGROUP14: s->preferred_kex[s->n_preferred_kex++] = &ssh_diffiehellman_group14; |
︙ | ︙ | |||
5505 5506 5507 5508 5509 5510 5511 | } /* * Set up the preferred ciphers. (NULL => warn below here) */ s->n_preferred_ciphers = 0; for (i = 0; i < CIPHER_MAX; i++) { | | | | 6014 6015 6016 6017 6018 6019 6020 6021 6022 6023 6024 6025 6026 6027 6028 6029 6030 6031 6032 6033 | } /* * Set up the preferred ciphers. (NULL => warn below here) */ s->n_preferred_ciphers = 0; for (i = 0; i < CIPHER_MAX; i++) { switch (conf_get_int_int(ssh->conf, CONF_ssh_cipherlist, i)) { case CIPHER_BLOWFISH: s->preferred_ciphers[s->n_preferred_ciphers++] = &ssh2_blowfish; break; case CIPHER_DES: if (conf_get_int(ssh->conf, CONF_ssh2_des_cbc)) { s->preferred_ciphers[s->n_preferred_ciphers++] = &ssh2_des; } break; case CIPHER_3DES: s->preferred_ciphers[s->n_preferred_ciphers++] = &ssh2_3des; break; case CIPHER_AES: |
︙ | ︙ | |||
5536 5537 5538 5539 5540 5541 5542 | break; } } /* * Set up preferred compression. */ | | | 6045 6046 6047 6048 6049 6050 6051 6052 6053 6054 6055 6056 6057 6058 6059 | break; } } /* * Set up preferred compression. */ if (conf_get_int(ssh->conf, CONF_compression)) s->preferred_comp = &ssh_zlib; else s->preferred_comp = &ssh_comp_none; /* * Enable queueing of outgoing auth- or connection-layer * packets while we are in the middle of a key exchange. |
︙ | ︙ | |||
5572 5573 5574 5575 5576 5577 5578 | if (commalist_started) ssh2_pkt_addstring_str(s->pktout, ","); ssh2_pkt_addstring_str(s->pktout, k->list[j]->name); commalist_started = 1; } } /* List server host key algorithms. */ | > > > > > | | | | | | > > > > > > > > > > > | > | | | | | | | | | | | | < < < < < < < < < < < | < | > | | | | | | < < < < < < | 6081 6082 6083 6084 6085 6086 6087 6088 6089 6090 6091 6092 6093 6094 6095 6096 6097 6098 6099 6100 6101 6102 6103 6104 6105 6106 6107 6108 6109 6110 6111 6112 6113 6114 6115 6116 6117 6118 6119 6120 6121 6122 6123 6124 6125 6126 6127 6128 6129 6130 6131 6132 6133 6134 6135 6136 6137 6138 6139 | if (commalist_started) ssh2_pkt_addstring_str(s->pktout, ","); ssh2_pkt_addstring_str(s->pktout, k->list[j]->name); commalist_started = 1; } } /* List server host key algorithms. */ if (!s->got_session_id) { /* * In the first key exchange, we list all the algorithms * we're prepared to cope with. */ ssh2_pkt_addstring_start(s->pktout); for (i = 0; i < lenof(hostkey_algs); i++) { ssh2_pkt_addstring_str(s->pktout, hostkey_algs[i]->name); if (i < lenof(hostkey_algs) - 1) ssh2_pkt_addstring_str(s->pktout, ","); } } else { /* * In subsequent key exchanges, we list only the kex * algorithm that was selected in the first key exchange, * so that we keep getting the same host key and hence * don't have to interrupt the user's session to ask for * reverification. */ assert(ssh->kex); ssh2_pkt_addstring(s->pktout, ssh->hostkey->name); } /* List encryption algorithms (client->server then server->client). */ for (k = 0; k < 2; k++) { ssh2_pkt_addstring_start(s->pktout); commalist_started = 0; for (i = 0; i < s->n_preferred_ciphers; i++) { const struct ssh2_ciphers *c = s->preferred_ciphers[i]; if (!c) continue; /* warning flag */ for (j = 0; j < c->nciphers; j++) { if (commalist_started) ssh2_pkt_addstring_str(s->pktout, ","); ssh2_pkt_addstring_str(s->pktout, c->list[j]->name); commalist_started = 1; } } } /* List MAC algorithms (client->server then server->client). */ for (j = 0; j < 2; j++) { ssh2_pkt_addstring_start(s->pktout); for (i = 0; i < s->nmacs; i++) { ssh2_pkt_addstring_str(s->pktout, s->maclist[i]->name); if (i < s->nmacs - 1) ssh2_pkt_addstring_str(s->pktout, ","); } } /* List client->server compression algorithms, * then server->client compression algorithms. (We use the * same set twice.) */ for (j = 0; j < 2; j++) { ssh2_pkt_addstring_start(s->pktout); assert(lenof(compressions) > 1); |
︙ | ︙ | |||
5663 5664 5665 5666 5667 5668 5669 | s->our_kexinitlen = s->pktout->length - 5; s->our_kexinit = snewn(s->our_kexinitlen, unsigned char); memcpy(s->our_kexinit, s->pktout->data + 5, s->our_kexinitlen); ssh2_pkt_send_noqueue(ssh, s->pktout); if (!pktin) | | | | 6172 6173 6174 6175 6176 6177 6178 6179 6180 6181 6182 6183 6184 6185 6186 6187 6188 6189 6190 6191 6192 6193 6194 6195 6196 6197 6198 | s->our_kexinitlen = s->pktout->length - 5; s->our_kexinit = snewn(s->our_kexinitlen, unsigned char); memcpy(s->our_kexinit, s->pktout->data + 5, s->our_kexinitlen); ssh2_pkt_send_noqueue(ssh, s->pktout); if (!pktin) crWaitUntilV(pktin); /* * Now examine the other side's KEXINIT to see what we're up * to. */ { char *str, *preferred; int i, j, len; if (pktin->type != SSH2_MSG_KEXINIT) { bombout(("expected key exchange packet from server")); crStopV; } ssh->kex = NULL; ssh->hostkey = NULL; s->cscipher_tobe = NULL; s->sccipher_tobe = NULL; s->csmac_tobe = NULL; s->scmac_tobe = NULL; |
︙ | ︙ | |||
5710 5711 5712 5713 5714 5715 5716 | } if (ssh->kex) break; } if (!ssh->kex) { bombout(("Couldn't agree a key exchange algorithm (available: %s)", str ? str : "(null)")); | | > > > > > > | 6219 6220 6221 6222 6223 6224 6225 6226 6227 6228 6229 6230 6231 6232 6233 6234 6235 6236 6237 6238 6239 6240 6241 6242 6243 6244 6245 6246 6247 6248 6249 6250 6251 6252 6253 | } if (ssh->kex) break; } if (!ssh->kex) { bombout(("Couldn't agree a key exchange algorithm (available: %s)", str ? str : "(null)")); crStopV; } /* * Note that the server's guess is considered wrong if it doesn't match * the first algorithm in our list, even if it's still the algorithm * we end up using. */ s->guessok = first_in_commasep_string(preferred, str, len); ssh_pkt_getstring(pktin, &str, &len); /* host key algorithms */ for (i = 0; i < lenof(hostkey_algs); i++) { if (in_commasep_string(hostkey_algs[i]->name, str, len)) { ssh->hostkey = hostkey_algs[i]; break; } } if (!ssh->hostkey) { bombout(("Couldn't agree a host key algorithm (available: %s)", str ? str : "(null)")); crStopV; } s->guessok = s->guessok && first_in_commasep_string(hostkey_algs[0]->name, str, len); ssh_pkt_getstring(pktin, &str, &len); /* client->server cipher */ for (i = 0; i < s->n_preferred_ciphers; i++) { const struct ssh2_ciphers *c = s->preferred_ciphers[i]; if (!c) { s->warn_cscipher = TRUE; |
︙ | ︙ | |||
5746 5747 5748 5749 5750 5751 5752 | } if (s->cscipher_tobe) break; } if (!s->cscipher_tobe) { bombout(("Couldn't agree a client-to-server cipher (available: %s)", str ? str : "(null)")); | | | 6261 6262 6263 6264 6265 6266 6267 6268 6269 6270 6271 6272 6273 6274 6275 | } if (s->cscipher_tobe) break; } if (!s->cscipher_tobe) { bombout(("Couldn't agree a client-to-server cipher (available: %s)", str ? str : "(null)")); crStopV; } ssh_pkt_getstring(pktin, &str, &len); /* server->client cipher */ for (i = 0; i < s->n_preferred_ciphers; i++) { const struct ssh2_ciphers *c = s->preferred_ciphers[i]; if (!c) { s->warn_sccipher = TRUE; |
︙ | ︙ | |||
5768 5769 5770 5771 5772 5773 5774 | } if (s->sccipher_tobe) break; } if (!s->sccipher_tobe) { bombout(("Couldn't agree a server-to-client cipher (available: %s)", str ? str : "(null)")); | | | 6283 6284 6285 6286 6287 6288 6289 6290 6291 6292 6293 6294 6295 6296 6297 | } if (s->sccipher_tobe) break; } if (!s->sccipher_tobe) { bombout(("Couldn't agree a server-to-client cipher (available: %s)", str ? str : "(null)")); crStopV; } ssh_pkt_getstring(pktin, &str, &len); /* client->server mac */ for (i = 0; i < s->nmacs; i++) { if (in_commasep_string(s->maclist[i]->name, str, len)) { s->csmac_tobe = s->maclist[i]; break; |
︙ | ︙ | |||
5825 5826 5827 5828 5829 5830 5831 5832 5833 5834 5835 5836 5837 5838 | logevent("Server supports delayed compression; " "will try this later"); } ssh_pkt_getstring(pktin, &str, &len); /* client->server language */ ssh_pkt_getstring(pktin, &str, &len); /* server->client language */ s->ignorepkt = ssh2_pkt_getbool(pktin) && !s->guessok; if (s->warn_kex) { ssh_set_frozen(ssh, 1); s->dlgret = askalg(ssh->frontend, "key-exchange algorithm", ssh->kex->name, ssh_dialog_callback, ssh); if (s->dlgret < 0) { do { | > > > > > > > > > > | | | | | | | | | < < < < < < < < < < | | 6340 6341 6342 6343 6344 6345 6346 6347 6348 6349 6350 6351 6352 6353 6354 6355 6356 6357 6358 6359 6360 6361 6362 6363 6364 6365 6366 6367 6368 6369 6370 6371 6372 6373 6374 6375 6376 6377 6378 6379 6380 6381 6382 6383 6384 6385 6386 6387 6388 6389 6390 6391 6392 6393 6394 6395 6396 6397 6398 6399 6400 6401 6402 6403 6404 6405 6406 6407 6408 6409 6410 6411 6412 6413 6414 6415 6416 6417 6418 6419 6420 6421 6422 6423 6424 6425 6426 6427 6428 6429 6430 6431 6432 6433 6434 6435 6436 6437 6438 6439 | logevent("Server supports delayed compression; " "will try this later"); } ssh_pkt_getstring(pktin, &str, &len); /* client->server language */ ssh_pkt_getstring(pktin, &str, &len); /* server->client language */ s->ignorepkt = ssh2_pkt_getbool(pktin) && !s->guessok; ssh->exhash = ssh->kex->hash->init(); hash_string(ssh->kex->hash, ssh->exhash, ssh->v_c, strlen(ssh->v_c)); hash_string(ssh->kex->hash, ssh->exhash, ssh->v_s, strlen(ssh->v_s)); hash_string(ssh->kex->hash, ssh->exhash, s->our_kexinit, s->our_kexinitlen); sfree(s->our_kexinit); /* Include the type byte in the hash of server's KEXINIT */ hash_string(ssh->kex->hash, ssh->exhash, pktin->body - 1, pktin->length + 1); if (s->warn_kex) { ssh_set_frozen(ssh, 1); s->dlgret = askalg(ssh->frontend, "key-exchange algorithm", ssh->kex->name, ssh_dialog_callback, ssh); if (s->dlgret < 0) { do { crReturnV; if (pktin) { bombout(("Unexpected data from server while" " waiting for user response")); crStopV; } } while (pktin || inlen > 0); s->dlgret = ssh->user_response; } ssh_set_frozen(ssh, 0); if (s->dlgret == 0) { ssh_disconnect(ssh, "User aborted at kex warning", NULL, 0, TRUE); crStopV; } } if (s->warn_cscipher) { ssh_set_frozen(ssh, 1); s->dlgret = askalg(ssh->frontend, "client-to-server cipher", s->cscipher_tobe->name, ssh_dialog_callback, ssh); if (s->dlgret < 0) { do { crReturnV; if (pktin) { bombout(("Unexpected data from server while" " waiting for user response")); crStopV; } } while (pktin || inlen > 0); s->dlgret = ssh->user_response; } ssh_set_frozen(ssh, 0); if (s->dlgret == 0) { ssh_disconnect(ssh, "User aborted at cipher warning", NULL, 0, TRUE); crStopV; } } if (s->warn_sccipher) { ssh_set_frozen(ssh, 1); s->dlgret = askalg(ssh->frontend, "server-to-client cipher", s->sccipher_tobe->name, ssh_dialog_callback, ssh); if (s->dlgret < 0) { do { crReturnV; if (pktin) { bombout(("Unexpected data from server while" " waiting for user response")); crStopV; } } while (pktin || inlen > 0); s->dlgret = ssh->user_response; } ssh_set_frozen(ssh, 0); if (s->dlgret == 0) { ssh_disconnect(ssh, "User aborted at cipher warning", NULL, 0, TRUE); crStopV; } } if (s->ignorepkt) /* first_kex_packet_follows */ crWaitUntilV(pktin); /* Ignore packet */ } if (ssh->kex->main_type == KEXTYPE_DH) { /* * 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... |
︙ | ︙ | |||
5947 5948 5949 5950 5951 5952 5953 | * 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); | | | | | 6462 6463 6464 6465 6466 6467 6468 6469 6470 6471 6472 6473 6474 6475 6476 6477 6478 6479 6480 6481 6482 6483 6484 6485 | * 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); crWaitUntilV(pktin); if (pktin->type != SSH2_MSG_KEX_DH_GEX_GROUP) { bombout(("expected key exchange group packet from server")); crStopV; } 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")); crStopV; } 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_kctx = SSH2_PKTCTX_DHGROUP; ssh->kex_ctx = dh_setup_group(ssh->kex); |
︙ | ︙ | |||
5982 5983 5984 5985 5986 5987 5988 | 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 */ | | | | | 6497 6498 6499 6500 6501 6502 6503 6504 6505 6506 6507 6508 6509 6510 6511 6512 6513 6514 6515 6516 6517 6518 6519 6520 6521 6522 | 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 */ crWaitUntilV(pktin); if (pktin->type != s->kex_reply_value) { bombout(("expected key exchange reply packet from server")); crStopV; } 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")); crStopV; } ssh_pkt_getstring(pktin, &s->sigdata, &s->siglen); 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. */ |
︙ | ︙ | |||
6026 6027 6028 6029 6030 6031 6032 | logeventf(ssh, "Doing RSA key exchange with hash %s", ssh->kex->hash->text_name); ssh->pkt_kctx = SSH2_PKTCTX_RSAKEX; /* * RSA key exchange. First expect a KEXRSA_PUBKEY packet * from the server. */ | | | | | 6541 6542 6543 6544 6545 6546 6547 6548 6549 6550 6551 6552 6553 6554 6555 6556 6557 6558 6559 6560 6561 6562 6563 6564 6565 6566 6567 6568 6569 6570 6571 6572 6573 6574 6575 6576 6577 | logeventf(ssh, "Doing RSA key exchange with hash %s", ssh->kex->hash->text_name); ssh->pkt_kctx = SSH2_PKTCTX_RSAKEX; /* * RSA key exchange. First expect a KEXRSA_PUBKEY packet * from the server. */ crWaitUntilV(pktin); if (pktin->type != SSH2_MSG_KEXRSA_PUBKEY) { bombout(("expected RSA public key packet from server")); crStopV; } 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")); crStopV; } 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 |
︙ | ︙ | |||
6108 6109 6110 6111 6112 6113 6114 | sfree(kstr2); sfree(kstr1); sfree(outstr); } ssh_rsakex_freekey(s->rsakey); | | | | 6623 6624 6625 6626 6627 6628 6629 6630 6631 6632 6633 6634 6635 6636 6637 6638 6639 6640 6641 | sfree(kstr2); sfree(kstr1); sfree(outstr); } ssh_rsakex_freekey(s->rsakey); crWaitUntilV(pktin); if (pktin->type != SSH2_MSG_KEXRSA_DONE) { sfree(s->rsakeydata); bombout(("expected signature packet from server")); crStopV; } ssh_pkt_getstring(pktin, &s->sigdata, &s->siglen); sfree(s->rsakeydata); } |
︙ | ︙ | |||
6136 6137 6138 6139 6140 6141 6142 | #endif if (!s->hkey || !ssh->hostkey->verifysig(s->hkey, s->sigdata, s->siglen, (char *)s->exchange_hash, ssh->kex->hash->hlen)) { bombout(("Server's host key did not match the signature supplied")); | | < < < < > > > > > | | | | | | | | | | | | | | | | | | | | | | | | < | | < | > > > > > > > > > > > > > > > > | > | 6651 6652 6653 6654 6655 6656 6657 6658 6659 6660 6661 6662 6663 6664 6665 6666 6667 6668 6669 6670 6671 6672 6673 6674 6675 6676 6677 6678 6679 6680 6681 6682 6683 6684 6685 6686 6687 6688 6689 6690 6691 6692 6693 6694 6695 6696 6697 6698 6699 6700 6701 6702 6703 6704 6705 6706 6707 6708 6709 6710 6711 6712 6713 6714 6715 6716 6717 6718 | #endif if (!s->hkey || !ssh->hostkey->verifysig(s->hkey, s->sigdata, s->siglen, (char *)s->exchange_hash, ssh->kex->hash->hlen)) { bombout(("Server's host key did not match the signature supplied")); crStopV; } s->keystr = ssh->hostkey->fmtkey(s->hkey); if (!s->got_session_id) { /* * Authenticate remote host: verify host key. (We've already * checked the signature of the exchange hash.) */ s->fingerprint = ssh->hostkey->fingerprint(s->hkey); ssh_set_frozen(ssh, 1); s->dlgret = verify_ssh_host_key(ssh->frontend, ssh->savedhost, ssh->savedport, ssh->hostkey->keytype, s->keystr, s->fingerprint, ssh_dialog_callback, ssh); if (s->dlgret < 0) { do { crReturnV; if (pktin) { bombout(("Unexpected data from server while waiting" " for user host key response")); crStopV; } } while (pktin || inlen > 0); s->dlgret = ssh->user_response; } ssh_set_frozen(ssh, 0); if (s->dlgret == 0) { ssh_disconnect(ssh, "User aborted at host key verification", NULL, 0, TRUE); crStopV; } logevent("Host key fingerprint is:"); logevent(s->fingerprint); sfree(s->fingerprint); /* * Save this host key, to check against the one presented in * subsequent rekeys. */ ssh->hostkey_str = s->keystr; } else { /* * In a rekey, we never present an interactive host key * verification request to the user. Instead, we simply * enforce that the key we're seeing this time is identical to * the one we saw before. */ if (strcmp(ssh->hostkey_str, s->keystr)) { bombout(("Host key was different in repeat key exchange")); crStopV; } sfree(s->keystr); } ssh->hostkey->freekey(s->hkey); /* * The exchange hash from the very first key exchange is also * the session id, used in session key construction and * authentication. */ |
︙ | ︙ | |||
6235 6236 6237 6238 6239 6240 6241 | assert(ssh->cscipher->blksize <= ssh->kex->hash->hlen * SSH2_MKKEY_ITERS); ssh->cscipher->setiv(ssh->cs_cipher_ctx, keyspace); ssh2_mkkey(ssh,s->K,s->exchange_hash,'E',keyspace); assert(ssh->csmac->len <= ssh->kex->hash->hlen * SSH2_MKKEY_ITERS); ssh->csmac->setkey(ssh->cs_mac_ctx, keyspace); | | | | | 6766 6767 6768 6769 6770 6771 6772 6773 6774 6775 6776 6777 6778 6779 6780 6781 6782 6783 6784 6785 6786 6787 6788 6789 6790 6791 6792 6793 6794 6795 6796 6797 6798 6799 6800 6801 6802 6803 6804 | assert(ssh->cscipher->blksize <= ssh->kex->hash->hlen * SSH2_MKKEY_ITERS); ssh->cscipher->setiv(ssh->cs_cipher_ctx, keyspace); ssh2_mkkey(ssh,s->K,s->exchange_hash,'E',keyspace); assert(ssh->csmac->len <= ssh->kex->hash->hlen * SSH2_MKKEY_ITERS); ssh->csmac->setkey(ssh->cs_mac_ctx, keyspace); smemclr(keyspace, sizeof(keyspace)); } logeventf(ssh, "Initialised %.200s client->server encryption", ssh->cscipher->text_name); logeventf(ssh, "Initialised %.200s client->server MAC algorithm", ssh->csmac->text_name); if (ssh->cscomp->text_name) logeventf(ssh, "Initialised %s compression", ssh->cscomp->text_name); /* * Now our end of the key exchange is complete, we can send all * our queued higher-layer packets. */ ssh->queueing = FALSE; ssh2_pkt_queuesend(ssh); /* * Expect SSH2_MSG_NEWKEYS from server. */ crWaitUntilV(pktin); if (pktin->type != SSH2_MSG_NEWKEYS) { bombout(("expected new-keys packet from server")); crStopV; } ssh->incoming_data_size = 0; /* start counting from here */ /* * We've seen server NEWKEYS, so create and initialise * server-to-client session keys. */ |
︙ | ︙ | |||
6301 6302 6303 6304 6305 6306 6307 | assert(ssh->sccipher->blksize <= ssh->kex->hash->hlen * SSH2_MKKEY_ITERS); ssh->sccipher->setiv(ssh->sc_cipher_ctx, keyspace); ssh2_mkkey(ssh,s->K,s->exchange_hash,'F',keyspace); assert(ssh->scmac->len <= ssh->kex->hash->hlen * SSH2_MKKEY_ITERS); ssh->scmac->setkey(ssh->sc_mac_ctx, keyspace); | | | 6832 6833 6834 6835 6836 6837 6838 6839 6840 6841 6842 6843 6844 6845 6846 | assert(ssh->sccipher->blksize <= ssh->kex->hash->hlen * SSH2_MKKEY_ITERS); ssh->sccipher->setiv(ssh->sc_cipher_ctx, keyspace); ssh2_mkkey(ssh,s->K,s->exchange_hash,'F',keyspace); assert(ssh->scmac->len <= ssh->kex->hash->hlen * SSH2_MKKEY_ITERS); ssh->scmac->setkey(ssh->sc_mac_ctx, keyspace); smemclr(keyspace, sizeof(keyspace)); } logeventf(ssh, "Initialised %.200s server->client encryption", ssh->sccipher->text_name); logeventf(ssh, "Initialised %.200s server->client MAC algorithm", ssh->scmac->text_name); if (ssh->sccomp->text_name) logeventf(ssh, "Initialised %s decompression", |
︙ | ︙ | |||
6332 6333 6334 6335 6336 6337 6338 | } /* * Otherwise, schedule a timer for our next rekey. */ ssh->kex_in_progress = FALSE; ssh->last_rekey = GETTICKCOUNT(); | | | < < < < < < < < < < < < < > > > > > > > | | 6863 6864 6865 6866 6867 6868 6869 6870 6871 6872 6873 6874 6875 6876 6877 6878 6879 6880 6881 6882 6883 6884 6885 6886 6887 6888 6889 6890 6891 6892 6893 6894 6895 6896 6897 6898 6899 6900 6901 6902 6903 6904 6905 6906 | } /* * Otherwise, schedule a timer for our next rekey. */ ssh->kex_in_progress = FALSE; ssh->last_rekey = GETTICKCOUNT(); if (conf_get_int(ssh->conf, CONF_ssh_rekey_time) != 0) ssh->next_rekey = schedule_timer(conf_get_int(ssh->conf, CONF_ssh_rekey_time)*60*TICKSPERSEC, ssh2_timer, ssh); /* * Now we're encrypting. Begin returning 1 to the protocol main * function so that other things can run on top of the * transport. If we ever see a KEXINIT, we must go back to the * start. * * We _also_ go back to the start if we see pktin==NULL and * inlen negative, because this is a special signal meaning * `initiate client-driven rekey', and `in' contains a message * giving the reason for the rekey. * * inlen==-1 means always initiate a rekey; * inlen==-2 means that userauth has completed successfully and * we should consider rekeying (for delayed compression). */ while (!((pktin && pktin->type == SSH2_MSG_KEXINIT) || (!pktin && inlen < 0))) { wait_for_rekey: if (!ssh->protocol_initial_phase_done) { ssh->protocol_initial_phase_done = TRUE; /* * Allow authconn to initialise itself. */ do_ssh2_authconn(ssh, NULL, 0, NULL); } crReturnV; } if (pktin) { logevent("Server initiated key re-exchange"); } else { if (inlen == -2) { /* * authconn has seen a USERAUTH_SUCCEEDED. Time to enable |
︙ | ︙ | |||
6414 6415 6416 6417 6418 6419 6420 | if ((ssh->remote_bugs & BUG_SSH2_REKEY)) { logeventf(ssh, "Server bug prevents key re-exchange (%s)", (char *)in); /* Reset the counters, so that at least this message doesn't * hit the event log _too_ often. */ ssh->outgoing_data_size = 0; ssh->incoming_data_size = 0; | | | | > < < | > > > > > > > > > | | | | | > > > < > > > > | < < < < < > | < < | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | > | > > > > > > > > < | < < < < < | | < > | | < < < < < < < < < < < < < | 6939 6940 6941 6942 6943 6944 6945 6946 6947 6948 6949 6950 6951 6952 6953 6954 6955 6956 6957 6958 6959 6960 6961 6962 6963 6964 6965 6966 6967 6968 6969 6970 6971 6972 6973 6974 6975 6976 6977 6978 6979 6980 6981 6982 6983 6984 6985 6986 6987 6988 6989 6990 6991 6992 6993 6994 6995 6996 6997 6998 6999 7000 7001 7002 7003 7004 7005 7006 7007 7008 7009 7010 7011 7012 7013 7014 7015 7016 7017 7018 7019 7020 7021 7022 7023 7024 7025 7026 7027 7028 7029 7030 7031 7032 7033 7034 7035 7036 7037 7038 7039 7040 7041 7042 7043 7044 7045 7046 7047 7048 7049 7050 7051 7052 7053 7054 7055 7056 7057 7058 7059 7060 7061 7062 7063 7064 7065 7066 7067 7068 7069 7070 7071 7072 7073 7074 7075 7076 7077 7078 7079 7080 7081 7082 7083 7084 7085 7086 7087 7088 7089 7090 7091 7092 7093 7094 7095 7096 7097 7098 7099 7100 7101 7102 7103 7104 7105 7106 7107 7108 7109 7110 7111 7112 7113 7114 7115 7116 7117 7118 7119 7120 7121 7122 7123 7124 7125 7126 7127 7128 7129 7130 7131 7132 7133 7134 7135 7136 7137 7138 7139 7140 7141 7142 7143 7144 7145 7146 7147 7148 7149 7150 7151 7152 7153 7154 7155 7156 7157 7158 7159 7160 7161 7162 7163 7164 7165 7166 7167 7168 7169 7170 7171 7172 7173 7174 7175 7176 7177 7178 7179 7180 7181 7182 7183 7184 7185 7186 7187 7188 7189 7190 7191 7192 7193 7194 7195 7196 7197 7198 7199 7200 7201 | if ((ssh->remote_bugs & BUG_SSH2_REKEY)) { logeventf(ssh, "Server bug prevents key re-exchange (%s)", (char *)in); /* Reset the counters, so that at least this message doesn't * hit the event log _too_ often. */ ssh->outgoing_data_size = 0; ssh->incoming_data_size = 0; if (conf_get_int(ssh->conf, CONF_ssh_rekey_time) != 0) { ssh->next_rekey = schedule_timer(conf_get_int(ssh->conf, CONF_ssh_rekey_time)*60*TICKSPERSEC, ssh2_timer, ssh); } goto wait_for_rekey; /* this is still utterly horrid */ } else { logeventf(ssh, "Initiating key re-exchange (%s)", (char *)in); } } goto begin_key_exchange; crFinishV; } /* * Add data to an SSH-2 channel output buffer. */ static void ssh2_add_channel_data(struct ssh_channel *c, char *buf, int len) { bufchain_add(&c->v.v2.outbuffer, buf, len); } /* * Attempt to send data on an SSH-2 channel. */ static int ssh2_try_send(struct ssh_channel *c) { Ssh ssh = c->ssh; struct Packet *pktout; int ret; while (c->v.v2.remwindow > 0 && bufchain_size(&c->v.v2.outbuffer) > 0) { int len; void *data; bufchain_prefix(&c->v.v2.outbuffer, &data, &len); if ((unsigned)len > c->v.v2.remwindow) len = c->v.v2.remwindow; if ((unsigned)len > c->v.v2.remmaxpkt) len = c->v.v2.remmaxpkt; pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_DATA); ssh2_pkt_adduint32(pktout, c->remoteid); ssh2_pkt_addstring_start(pktout); ssh2_pkt_addstring_data(pktout, data, len); ssh2_pkt_send(ssh, pktout); bufchain_consume(&c->v.v2.outbuffer, len); c->v.v2.remwindow -= len; } /* * After having sent as much data as we can, return the amount * still buffered. */ ret = bufchain_size(&c->v.v2.outbuffer); /* * And if there's no data pending but we need to send an EOF, send * it. */ if (!ret && c->pending_eof) ssh_channel_try_eof(c); return ret; } static void ssh2_try_send_and_unthrottle(Ssh ssh, struct ssh_channel *c) { int bufsize; if (c->closes & CLOSES_SENT_EOF) return; /* don't send on channels we've EOFed */ bufsize = ssh2_try_send(c); if (bufsize == 0) { switch (c->type) { case CHAN_MAINSESSION: /* stdin need not receive an unthrottle * notification since it will be polled */ break; case CHAN_X11: x11_unthrottle(c->u.x11.xconn); break; case CHAN_AGENT: /* agent sockets are request/response and need no * buffer management */ break; case CHAN_SOCKDATA: pfd_unthrottle(c->u.pfd.pf); break; } } } static int ssh_is_simple(Ssh ssh) { /* * We use the 'simple' variant of the SSH protocol if we're asked * to, except not if we're also doing connection-sharing (either * tunnelling our packets over an upstream or expecting to be * tunnelled over ourselves), since then the assumption that we * have only one channel to worry about is not true after all. */ return (conf_get_int(ssh->conf, CONF_ssh_simple) && !ssh->bare_connection && !ssh->connshare); } /* * 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->pending_eof = FALSE; c->throttling_conn = FALSE; c->v.v2.locwindow = c->v.v2.locmaxwin = c->v.v2.remlocwin = ssh_is_simple(ssh) ? OUR_V2_BIGWIN : OUR_V2_WINSIZE; c->v.v2.chanreq_head = NULL; c->v.v2.throttle_state = UNTHROTTLED; bufchain_init(&c->v.v2.outbuffer); } /* * Construct the common parts of a CHANNEL_OPEN. */ static struct Packet *ssh2_chanopen_init(struct ssh_channel *c, char *type) { struct Packet *pktout; pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_OPEN); ssh2_pkt_addstring(pktout, type); ssh2_pkt_adduint32(pktout, c->localid); ssh2_pkt_adduint32(pktout, c->v.v2.locwindow);/* our window size */ ssh2_pkt_adduint32(pktout, OUR_V2_MAXPKT); /* our max pkt size */ return 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. */ static void ssh2_queue_chanreq_handler(struct ssh_channel *c, cchandler_fn_t handler, void *ctx) { struct outstanding_channel_request *ocr = snew(struct outstanding_channel_request); assert(!(c->closes & (CLOSES_SENT_CLOSE | CLOSES_RCVD_CLOSE))); ocr->handler = handler; ocr->ctx = ctx; ocr->next = NULL; if (!c->v.v2.chanreq_head) c->v.v2.chanreq_head = ocr; else c->v.v2.chanreq_tail->next = ocr; c->v.v2.chanreq_tail = ocr; } /* * Construct the common parts of a CHANNEL_REQUEST. If handler is not * NULL then a reply will be requested and the handler will be called * when it arrives. The returned packet is ready to have any * request-specific data added and be sent. Note that if a handler is * provided, it's essential that the request actually be sent. * * The handler will usually be passed the response packet in pktin. * If pktin is NULL, this means that no reply will ever be forthcoming * (e.g. because the entire connection is being destroyed) and the * handler should free any storage it's holding. */ static struct Packet *ssh2_chanreq_init(struct ssh_channel *c, char *type, cchandler_fn_t handler, void *ctx) { struct Packet *pktout; assert(!(c->closes & (CLOSES_SENT_CLOSE | CLOSES_RCVD_CLOSE))); pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_REQUEST); ssh2_pkt_adduint32(pktout, c->remoteid); ssh2_pkt_addstring(pktout, type); ssh2_pkt_addbool(pktout, handler != NULL); if (handler != NULL) ssh2_queue_chanreq_handler(c, handler, ctx); return pktout; } /* * Potentially enlarge the window on an SSH-2 channel. */ static void ssh2_handle_winadj_response(struct ssh_channel *, struct Packet *, void *); static void ssh2_set_window(struct ssh_channel *c, int newwin) { Ssh ssh = c->ssh; /* * Never send WINDOW_ADJUST for a channel that the remote side has * already sent EOF on; there's no point, since it won't be * sending any more data anyway. Ditto if _we've_ already sent * CLOSE. */ if (c->closes & (CLOSES_RCVD_EOF | CLOSES_SENT_CLOSE)) return; /* * Also, never widen the window for an X11 channel when we're * still waiting to see its initial auth and may yet hand it off * to a downstream. */ if (c->type == CHAN_X11 && c->u.x11.initial) 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 * sending a WINDOW_ADJUST for every character in a shell session. * * "Significant" is arbitrarily defined as half the window size. */ if (newwin / 2 >= c->v.v2.locwindow) { struct Packet *pktout; unsigned *up; /* * 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. */ if (newwin == c->v.v2.locmaxwin && !(ssh->remote_bugs & BUG_CHOKES_ON_WINADJ)) { up = snew(unsigned); *up = newwin - c->v.v2.locwindow; pktout = ssh2_chanreq_init(c, "winadj@putty.projects.tartarus.org", ssh2_handle_winadj_response, up); ssh2_pkt_send(ssh, pktout); 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; } |
︙ | ︙ | |||
6625 6626 6627 6628 6629 6630 6631 | 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 || | > | | > | | > > | > > > > | | | < | < < < < < < < < < < < < | > < | > > > | < < < < | > > > > | | < > > < | | | < < | < < < < < < > > > > > | > > > > > | | | 7214 7215 7216 7217 7218 7219 7220 7221 7222 7223 7224 7225 7226 7227 7228 7229 7230 7231 7232 7233 7234 7235 7236 7237 7238 7239 7240 7241 7242 7243 7244 7245 7246 7247 7248 7249 7250 7251 7252 7253 7254 7255 7256 7257 7258 7259 7260 7261 7262 7263 7264 7265 7266 7267 7268 7269 7270 7271 7272 7273 7274 7275 7276 7277 7278 7279 7280 7281 7282 7283 7284 7285 7286 7287 7288 7289 7290 7291 7292 7293 7294 7295 7296 7297 7298 7299 7300 7301 7302 7303 7304 7305 7306 7307 7308 7309 7310 7311 7312 7313 7314 7315 7316 7317 7318 7319 7320 7321 7322 7323 7324 7325 7326 7327 7328 7329 7330 7331 7332 7333 7334 7335 7336 7337 7338 7339 7340 7341 7342 | 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->type != CHAN_SHARING && 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_handle_winadj_response(struct ssh_channel *c, struct Packet *pktin, void *ctx) { unsigned *sizep = ctx; /* * Winadj responses should always be failures. However, at least * one server ("boks_sshd") is known to return SUCCESS for channel * requests it's never heard of, such as "winadj@putty". Raised * with foxt.com as bug 090916-090424, but for the sake of a quiet * life, we don't worry about what kind of response we got. */ c->v.v2.remlocwin += *sizep; sfree(sizep); /* * 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_response(Ssh ssh, struct Packet *pktin) { struct ssh_channel *c = ssh2_channel_msg(ssh, pktin); struct outstanding_channel_request *ocr; if (!c) return; if (c->type == CHAN_SHARING) { share_got_pkt_from_server(c->u.sharing.ctx, pktin->type, pktin->body, pktin->length); return; } ocr = c->v.v2.chanreq_head; if (!ocr) { ssh2_msg_unexpected(ssh, pktin); return; } ocr->handler(c, pktin, ocr->ctx); c->v.v2.chanreq_head = ocr->next; sfree(ocr); /* * We may now initiate channel-closing procedures, if that * CHANNEL_REQUEST was the last thing outstanding before we send * CHANNEL_CLOSE. */ ssh2_channel_check_close(c); } static void ssh2_msg_channel_window_adjust(Ssh ssh, struct Packet *pktin) { struct ssh_channel *c; c = ssh2_channel_msg(ssh, pktin); if (!c) return; if (c->type == CHAN_SHARING) { share_got_pkt_from_server(c->u.sharing.ctx, pktin->type, pktin->body, pktin->length); return; } if (!(c->closes & CLOSES_SENT_EOF)) { c->v.v2.remwindow += ssh_pkt_getuint32(pktin); ssh2_try_send_and_unthrottle(ssh, c); } } static void ssh2_msg_channel_data(Ssh ssh, struct Packet *pktin) { char *data; int length; struct ssh_channel *c; c = ssh2_channel_msg(ssh, pktin); if (!c) return; if (c->type == CHAN_SHARING) { share_got_pkt_from_server(c->u.sharing.ctx, pktin->type, pktin->body, pktin->length); return; } if (pktin->type == SSH2_MSG_CHANNEL_EXTENDED_DATA && ssh_pkt_getuint32(pktin) != SSH2_EXTENDED_DATA_STDERR) return; /* extended but not stderr */ ssh_pkt_getstring(pktin, &data, &length); if (data) { int bufsize = 0; c->v.v2.locwindow -= length; c->v.v2.remlocwin -= length; switch (c->type) { case CHAN_MAINSESSION: bufsize = from_backend(ssh->frontend, pktin->type == SSH2_MSG_CHANNEL_EXTENDED_DATA, data, length); break; case CHAN_X11: bufsize = x11_send(c->u.x11.xconn, data, length); break; case CHAN_SOCKDATA: bufsize = pfd_send(c->u.pfd.pf, data, length); break; case CHAN_AGENT: while (length > 0) { if (c->u.a.lensofar < 4) { unsigned int l = min(4 - c->u.a.lensofar, (unsigned)length); memcpy(c->u.a.msglen + c->u.a.lensofar, |
︙ | ︙ | |||
6772 6773 6774 6775 6776 6777 6778 6779 6780 6781 6782 6783 6784 6785 6786 6787 6788 6789 6790 6791 | data += l; length -= l; c->u.a.lensofar += l; } if (c->u.a.lensofar == c->u.a.totallen) { void *reply; int replylen; if (agent_query(c->u.a.message, c->u.a.totallen, &reply, &replylen, ssh_agentf_callback, c)) ssh_agentf_callback(c, reply, replylen); sfree(c->u.a.message); c->u.a.lensofar = 0; } } bufsize = 0; break; } /* | > > | 7361 7362 7363 7364 7365 7366 7367 7368 7369 7370 7371 7372 7373 7374 7375 7376 7377 7378 7379 7380 7381 7382 | data += l; length -= l; c->u.a.lensofar += l; } if (c->u.a.lensofar == c->u.a.totallen) { void *reply; int replylen; c->u.a.outstanding_requests++; if (agent_query(c->u.a.message, c->u.a.totallen, &reply, &replylen, ssh_agentf_callback, c)) ssh_agentf_callback(c, reply, replylen); sfree(c->u.a.message); c->u.a.message = NULL; c->u.a.lensofar = 0; } } bufsize = 0; break; } /* |
︙ | ︙ | |||
6806 6807 6808 6809 6810 6811 6812 | 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. */ | | < | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > < | < < < < | < < < < < < < < > > > < > > > > > | > > > > > > > > > > > > > > > > > > | | | < | | < | < | < < < | | < < | | < < < < | > > > > | > > | < | < < | | > > | | < < < < < | < < < | > > | < | > > | > > < > > > | | | | > > | > | > | | > > > > > > > > > > | | < < > > > > | > > | | > > > | | | | | | | > > > > > > > > > > > > > > > > > > > > > > > > > | 7397 7398 7399 7400 7401 7402 7403 7404 7405 7406 7407 7408 7409 7410 7411 7412 7413 7414 7415 7416 7417 7418 7419 7420 7421 7422 7423 7424 7425 7426 7427 7428 7429 7430 7431 7432 7433 7434 7435 7436 7437 7438 7439 7440 7441 7442 7443 7444 7445 7446 7447 7448 7449 7450 7451 7452 7453 7454 7455 7456 7457 7458 7459 7460 7461 7462 7463 7464 7465 7466 7467 7468 7469 7470 7471 7472 7473 7474 7475 7476 7477 7478 7479 7480 7481 7482 7483 7484 7485 7486 7487 7488 7489 7490 7491 7492 7493 7494 7495 7496 7497 7498 7499 7500 7501 7502 7503 7504 7505 7506 7507 7508 7509 7510 7511 7512 7513 7514 7515 7516 7517 7518 7519 7520 7521 7522 7523 7524 7525 7526 7527 7528 7529 7530 7531 7532 7533 7534 7535 7536 7537 7538 7539 7540 7541 7542 7543 7544 7545 7546 7547 7548 7549 7550 7551 7552 7553 7554 7555 7556 7557 7558 7559 7560 7561 7562 7563 7564 7565 7566 7567 7568 7569 7570 7571 7572 7573 7574 7575 7576 7577 7578 7579 7580 7581 7582 7583 7584 7585 7586 7587 7588 7589 7590 7591 7592 7593 7594 7595 7596 7597 7598 7599 7600 7601 7602 7603 7604 7605 7606 7607 7608 7609 7610 7611 7612 7613 7614 7615 7616 7617 7618 7619 7620 7621 7622 7623 7624 7625 7626 7627 7628 7629 7630 7631 7632 7633 7634 7635 7636 7637 7638 7639 7640 7641 7642 7643 7644 7645 7646 7647 7648 7649 7650 7651 7652 7653 7654 7655 7656 7657 7658 7659 7660 7661 7662 7663 7664 7665 7666 7667 7668 7669 7670 7671 7672 7673 7674 7675 7676 7677 7678 7679 7680 7681 7682 7683 7684 7685 7686 7687 7688 7689 7690 7691 7692 7693 7694 7695 7696 7697 7698 7699 7700 7701 7702 7703 7704 7705 7706 7707 7708 7709 7710 7711 7712 7713 7714 7715 7716 7717 7718 7719 7720 7721 7722 7723 7724 7725 7726 7727 7728 7729 7730 7731 7732 7733 7734 7735 7736 7737 7738 7739 7740 7741 7742 7743 7744 7745 7746 7747 7748 7749 7750 7751 7752 7753 7754 7755 7756 7757 7758 7759 7760 7761 7762 7763 7764 7765 7766 7767 7768 7769 7770 7771 7772 7773 7774 7775 7776 7777 7778 7779 7780 7781 7782 7783 | 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_is_simple(ssh) && bufsize>0)) && !c->throttling_conn) { c->throttling_conn = 1; ssh_throttle_conn(ssh, +1); } } } static void ssh_check_termination(Ssh ssh) { if (ssh->version == 2 && !conf_get_int(ssh->conf, CONF_ssh_no_shell) && count234(ssh->channels) == 0 && !(ssh->connshare && share_ndownstreams(ssh->connshare) > 0)) { /* * We used to send SSH_MSG_DISCONNECT here, because I'd * believed that _every_ conforming SSH-2 connection had to * end with a disconnect being sent by at least one side; * apparently I was wrong and it's perfectly OK to * unceremoniously slam the connection shut when you're done, * and indeed OpenSSH feels this is more polite than sending a * DISCONNECT. So now we don't. */ ssh_disconnect(ssh, "All channels closed", NULL, 0, TRUE); } } void ssh_sharing_downstream_connected(Ssh ssh, unsigned id) { logeventf(ssh, "Connection sharing downstream #%u connected", id); } void ssh_sharing_downstream_disconnected(Ssh ssh, unsigned id) { logeventf(ssh, "Connection sharing downstream #%u disconnected", id); ssh_check_termination(ssh); } void ssh_sharing_logf(Ssh ssh, unsigned id, const char *logfmt, ...) { va_list ap; char *buf; va_start(ap, logfmt); buf = dupvprintf(logfmt, ap); va_end(ap); if (id) logeventf(ssh, "Connection sharing downstream #%u: %s", id, buf); else logeventf(ssh, "Connection sharing: %s", buf); sfree(buf); } static void ssh_channel_destroy(struct ssh_channel *c) { Ssh ssh = c->ssh; switch (c->type) { case CHAN_MAINSESSION: ssh->mainchan = NULL; update_specials_menu(ssh->frontend); break; case CHAN_X11: if (c->u.x11.xconn != NULL) x11_close(c->u.x11.xconn); logevent("Forwarded X11 connection terminated"); break; case CHAN_AGENT: sfree(c->u.a.message); break; case CHAN_SOCKDATA: if (c->u.pfd.pf != NULL) pfd_close(c->u.pfd.pf); logevent("Forwarded port closed"); break; } del234(ssh->channels, c); if (ssh->version == 2) { bufchain_clear(&c->v.v2.outbuffer); assert(c->v.v2.chanreq_head == NULL); } sfree(c); /* * If that was the last channel left open, we might need to * terminate. */ ssh_check_termination(ssh); } static void ssh2_channel_check_close(struct ssh_channel *c) { Ssh ssh = c->ssh; struct Packet *pktout; if (c->halfopen) { /* * If we've sent out our own CHANNEL_OPEN but not yet seen * either OPEN_CONFIRMATION or OPEN_FAILURE in response, then * it's too early to be sending close messages of any kind. */ return; } if ((!((CLOSES_SENT_EOF | CLOSES_RCVD_EOF) & ~c->closes) || c->type == CHAN_ZOMBIE) && !c->v.v2.chanreq_head && !(c->closes & CLOSES_SENT_CLOSE)) { /* * We have both sent and received EOF (or the channel is a * zombie), and we have no outstanding channel requests, which * means the channel is in final wind-up. But we haven't sent * CLOSE, so let's do so now. */ pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_CLOSE); ssh2_pkt_adduint32(pktout, c->remoteid); ssh2_pkt_send(ssh, pktout); c->closes |= CLOSES_SENT_EOF | CLOSES_SENT_CLOSE; } if (!((CLOSES_SENT_CLOSE | CLOSES_RCVD_CLOSE) & ~c->closes)) { assert(c->v.v2.chanreq_head == NULL); /* * We have both sent and received CLOSE, which means we're * completely done with the channel. */ ssh_channel_destroy(c); } } static void ssh2_channel_got_eof(struct ssh_channel *c) { if (c->closes & CLOSES_RCVD_EOF) return; /* already seen EOF */ c->closes |= CLOSES_RCVD_EOF; if (c->type == CHAN_X11) { x11_send_eof(c->u.x11.xconn); } else if (c->type == CHAN_AGENT) { if (c->u.a.outstanding_requests == 0) { /* Manufacture an outgoing EOF in response to the incoming one. */ sshfwd_write_eof(c); } } else if (c->type == CHAN_SOCKDATA) { pfd_send_eof(c->u.pfd.pf); } else if (c->type == CHAN_MAINSESSION) { Ssh ssh = c->ssh; if (!ssh->sent_console_eof && (from_backend_eof(ssh->frontend) || ssh->got_pty)) { /* * Either from_backend_eof told us that the front end * wants us to close the outgoing side of the connection * as soon as we see EOF from the far end, or else we've * unilaterally decided to do that because we've allocated * a remote pty and hence EOF isn't a particularly * meaningful concept. */ sshfwd_write_eof(c); } ssh->sent_console_eof = TRUE; } ssh2_channel_check_close(c); } static void ssh2_msg_channel_eof(Ssh ssh, struct Packet *pktin) { struct ssh_channel *c; c = ssh2_channel_msg(ssh, pktin); if (!c) return; if (c->type == CHAN_SHARING) { share_got_pkt_from_server(c->u.sharing.ctx, pktin->type, pktin->body, pktin->length); return; } ssh2_channel_got_eof(c); } static void ssh2_msg_channel_close(Ssh ssh, struct Packet *pktin) { struct ssh_channel *c; c = ssh2_channel_msg(ssh, pktin); if (!c) return; if (c->type == CHAN_SHARING) { share_got_pkt_from_server(c->u.sharing.ctx, pktin->type, pktin->body, pktin->length); return; } /* * When we receive CLOSE on a channel, we assume it comes with an * implied EOF if we haven't seen EOF yet. */ ssh2_channel_got_eof(c); /* * And we also send an outgoing EOF, if we haven't already, on the * assumption that CLOSE is a pretty forceful announcement that * the remote side is doing away with the entire channel. (If it * had wanted to send us EOF and continue receiving data from us, * it would have just sent CHANNEL_EOF.) */ if (!(c->closes & CLOSES_SENT_EOF)) { /* * Make sure we don't read any more from whatever our local * data source is for this channel. */ switch (c->type) { case CHAN_MAINSESSION: ssh->send_ok = 0; /* stop trying to read from stdin */ break; case CHAN_X11: x11_override_throttle(c->u.x11.xconn, 1); break; case CHAN_SOCKDATA: pfd_override_throttle(c->u.pfd.pf, 1); break; } /* * Abandon any buffered data we still wanted to send to this * channel. Receiving a CHANNEL_CLOSE is an indication that * the server really wants to get on and _destroy_ this * channel, and it isn't going to send us any further * WINDOW_ADJUSTs to permit us to send pending stuff. */ bufchain_clear(&c->v.v2.outbuffer); /* * Send outgoing EOF. */ sshfwd_write_eof(c); } /* * Now process the actual close. */ if (!(c->closes & CLOSES_RCVD_CLOSE)) { c->closes |= CLOSES_RCVD_CLOSE; ssh2_channel_check_close(c); } } static void ssh2_msg_channel_open_confirmation(Ssh ssh, struct Packet *pktin) { struct ssh_channel *c; c = ssh2_channel_msg(ssh, pktin); if (!c) return; if (c->type == CHAN_SHARING) { share_got_pkt_from_server(c->u.sharing.ctx, pktin->type, pktin->body, pktin->length); return; } assert(c->halfopen); /* ssh2_channel_msg will have enforced this */ c->remoteid = ssh_pkt_getuint32(pktin); c->halfopen = FALSE; c->v.v2.remwindow = ssh_pkt_getuint32(pktin); c->v.v2.remmaxpkt = ssh_pkt_getuint32(pktin); if (c->type == CHAN_SOCKDATA_DORMANT) { c->type = CHAN_SOCKDATA; if (c->u.pfd.pf) pfd_confirm(c->u.pfd.pf); } else if (c->type == CHAN_ZOMBIE) { /* * This case can occur if a local socket error occurred * between us sending out CHANNEL_OPEN and receiving * OPEN_CONFIRMATION. In this case, all we can do is * immediately initiate close proceedings now that we know the * server's id to put in the close message. */ ssh2_channel_check_close(c); } else { /* * We never expect to receive OPEN_CONFIRMATION for any * *other* channel type (since only local-to-remote port * forwardings cause us to send CHANNEL_OPEN after the main * channel is live - all other auxiliary channel types are * initiated from the server end). It's safe to enforce this * by assertion rather than by ssh_disconnect, because the * real point is that we never constructed a half-open channel * structure in the first place with any type other than the * above. */ assert(!"Funny channel type in ssh2_msg_channel_open_confirmation"); } if (c->pending_eof) ssh_channel_try_eof(c); /* in case we had a pending EOF */ } static void ssh2_msg_channel_open_failure(Ssh ssh, struct Packet *pktin) { static const char *const reasons[] = { "<unknown reason code>", "Administratively prohibited", "Connect failed", "Unknown channel type", "Resource shortage", }; unsigned reason_code; char *reason_string; int reason_length; struct ssh_channel *c; c = ssh2_channel_msg(ssh, pktin); if (!c) return; if (c->type == CHAN_SHARING) { share_got_pkt_from_server(c->u.sharing.ctx, pktin->type, pktin->body, pktin->length); return; } assert(c->halfopen); /* ssh2_channel_msg will have enforced this */ if (c->type == CHAN_SOCKDATA_DORMANT) { reason_code = ssh_pkt_getuint32(pktin); if (reason_code >= lenof(reasons)) reason_code = 0; /* ensure reasons[reason_code] in range */ ssh_pkt_getstring(pktin, &reason_string, &reason_length); logeventf(ssh, "Forwarded connection refused by server: %s [%.*s]", reasons[reason_code], reason_length, reason_string); pfd_close(c->u.pfd.pf); } else if (c->type == CHAN_ZOMBIE) { /* * This case can occur if a local socket error occurred * between us sending out CHANNEL_OPEN and receiving * OPEN_FAILURE. In this case, we need do nothing except allow * the code below to throw the half-open channel away. */ } else { /* * We never expect to receive OPEN_FAILURE for any *other* * channel type (since only local-to-remote port forwardings * cause us to send CHANNEL_OPEN after the main channel is * live - all other auxiliary channel types are initiated from * the server end). It's safe to enforce this by assertion * rather than by ssh_disconnect, because the real point is * that we never constructed a half-open channel structure in * the first place with any type other than the above. */ assert(!"Funny channel type in ssh2_msg_channel_open_failure"); } del234(ssh->channels, c); sfree(c); } static void ssh2_msg_channel_request(Ssh ssh, struct Packet *pktin) { char *type; int typelen, want_reply; int reply = SSH2_MSG_CHANNEL_FAILURE; /* default */ struct ssh_channel *c; struct Packet *pktout; c = ssh2_channel_msg(ssh, pktin); if (!c) return; if (c->type == CHAN_SHARING) { share_got_pkt_from_server(c->u.sharing.ctx, pktin->type, pktin->body, pktin->length); return; } ssh_pkt_getstring(pktin, &type, &typelen); want_reply = ssh2_pkt_getbool(pktin); /* * Having got the channel number, we now look at * the request type string to see if it's something * we recognise. |
︙ | ︙ | |||
7014 7015 7016 7017 7018 7019 7020 | long len = pktin->length - pktin->savedpos; unsigned long num = GET_32BIT(p); /* what is it? */ /* If it's 0, it hardly matters; assume string */ if (num == 0) { is_int = FALSE; } else { int maybe_int = FALSE, maybe_str = FALSE; | | | > | | | | | > | < > | | 7813 7814 7815 7816 7817 7818 7819 7820 7821 7822 7823 7824 7825 7826 7827 7828 7829 7830 7831 7832 7833 7834 7835 7836 7837 7838 | long len = pktin->length - pktin->savedpos; unsigned long num = GET_32BIT(p); /* what is it? */ /* If it's 0, it hardly matters; assume string */ if (num == 0) { is_int = FALSE; } else { int maybe_int = FALSE, maybe_str = FALSE; #define CHECK_HYPOTHESIS(offset, result) \ do \ { \ int q = toint(offset); \ if (q >= 0 && q+4 <= len) { \ q = toint(q + 4 + GET_32BIT(p+q)); \ if (q >= 0 && q+4 <= len && \ ((q = toint(q + 4 + GET_32BIT(p+q))) != 0) && \ q == len) \ result = TRUE; \ } \ } while(0) CHECK_HYPOTHESIS(4+1, maybe_int); CHECK_HYPOTHESIS(4+num+1, maybe_str); #undef CHECK_HYPOTHESIS if (maybe_int && !maybe_str) is_int = TRUE; else if (!maybe_int && maybe_str) is_int = FALSE; |
︙ | ︙ | |||
7160 7161 7162 7163 7164 7165 7166 7167 7168 7169 7170 7171 7172 7173 7174 7175 7176 7177 7178 7179 7180 7181 7182 7183 7184 7185 7186 7187 7188 7189 | * want_reply. */ if (want_reply) { pktout = ssh2_pkt_init(SSH2_MSG_REQUEST_FAILURE); ssh2_pkt_send(ssh, pktout); } } static void ssh2_msg_channel_open(Ssh ssh, struct Packet *pktin) { char *type; int typelen; char *peeraddr; int peeraddrlen; int peerport; char *error = NULL; struct ssh_channel *c; unsigned remid, winsize, pktsize; struct Packet *pktout; ssh_pkt_getstring(pktin, &type, &typelen); c = snew(struct ssh_channel); c->ssh = ssh; remid = ssh_pkt_getuint32(pktin); winsize = ssh_pkt_getuint32(pktin); pktsize = ssh_pkt_getuint32(pktin); if (typelen == 3 && !memcmp(type, "x11", 3)) { char *addrstr; | > > > > > > > > > > > > > > > > > > > > > > > > > < | > | | > > | > > > > > | > > > > | | < | | | > | | > > | | | > > > > > > > > > > > | > | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | 7961 7962 7963 7964 7965 7966 7967 7968 7969 7970 7971 7972 7973 7974 7975 7976 7977 7978 7979 7980 7981 7982 7983 7984 7985 7986 7987 7988 7989 7990 7991 7992 7993 7994 7995 7996 7997 7998 7999 8000 8001 8002 8003 8004 8005 8006 8007 8008 8009 8010 8011 8012 8013 8014 8015 8016 8017 8018 8019 8020 8021 8022 8023 8024 8025 8026 8027 8028 8029 8030 8031 8032 8033 8034 8035 8036 8037 8038 8039 8040 8041 8042 8043 8044 8045 8046 8047 8048 8049 8050 8051 8052 8053 8054 8055 8056 8057 8058 8059 8060 8061 8062 8063 8064 8065 8066 8067 8068 8069 8070 8071 8072 8073 8074 8075 8076 8077 8078 8079 8080 8081 8082 8083 8084 8085 8086 8087 8088 8089 8090 8091 8092 8093 8094 8095 8096 8097 8098 8099 8100 8101 8102 8103 8104 8105 8106 8107 8108 8109 8110 8111 8112 8113 8114 8115 8116 8117 8118 8119 8120 8121 8122 8123 8124 8125 8126 8127 8128 8129 8130 8131 8132 8133 8134 8135 8136 8137 8138 8139 8140 8141 8142 8143 8144 8145 8146 8147 8148 8149 8150 8151 8152 8153 8154 8155 8156 8157 8158 8159 8160 8161 8162 8163 8164 8165 8166 8167 8168 8169 8170 8171 8172 8173 8174 8175 8176 8177 8178 8179 8180 8181 8182 8183 8184 8185 8186 8187 8188 8189 | * want_reply. */ if (want_reply) { pktout = ssh2_pkt_init(SSH2_MSG_REQUEST_FAILURE); ssh2_pkt_send(ssh, pktout); } } struct X11FakeAuth *ssh_sharing_add_x11_display(Ssh ssh, int authtype, void *share_cs, void *share_chan) { struct X11FakeAuth *auth; /* * Make up a new set of fake X11 auth data, and add it to the tree * of currently valid ones with an indication of the sharing * context that it's relevant to. */ auth = x11_invent_fake_auth(ssh->x11authtree, authtype); auth->share_cs = share_cs; auth->share_chan = share_chan; return auth; } void ssh_sharing_remove_x11_display(Ssh ssh, struct X11FakeAuth *auth) { del234(ssh->x11authtree, auth); x11_free_fake_auth(auth); } static void ssh2_msg_channel_open(Ssh ssh, struct Packet *pktin) { char *type; int typelen; char *peeraddr; int peeraddrlen; int peerport; char *error = NULL; struct ssh_channel *c; unsigned remid, winsize, pktsize; unsigned our_winsize_override = 0; struct Packet *pktout; ssh_pkt_getstring(pktin, &type, &typelen); c = snew(struct ssh_channel); c->ssh = ssh; remid = ssh_pkt_getuint32(pktin); winsize = ssh_pkt_getuint32(pktin); pktsize = ssh_pkt_getuint32(pktin); if (typelen == 3 && !memcmp(type, "x11", 3)) { char *addrstr; ssh_pkt_getstring(pktin, &peeraddr, &peeraddrlen); addrstr = snewn(peeraddrlen+1, char); memcpy(addrstr, peeraddr, peeraddrlen); addrstr[peeraddrlen] = '\0'; peerport = ssh_pkt_getuint32(pktin); logeventf(ssh, "Received X11 connect request from %s:%d", addrstr, peerport); if (!ssh->X11_fwd_enabled && !ssh->connshare) error = "X11 forwarding is not enabled"; else { c->u.x11.xconn = x11_init(ssh->x11authtree, c, addrstr, peerport); c->type = CHAN_X11; c->u.x11.initial = TRUE; /* * If we are a connection-sharing upstream, then we should * initially present a very small window, adequate to take * the X11 initial authorisation packet but not much more. * Downstream will then present us a larger window (by * fiat of the connection-sharing protocol) and we can * guarantee to send a positive-valued WINDOW_ADJUST. */ if (ssh->connshare) our_winsize_override = 128; logevent("Opened X11 forward channel"); } sfree(addrstr); } else if (typelen == 15 && !memcmp(type, "forwarded-tcpip", 15)) { struct ssh_rportfwd pf, *realpf; char *shost; int shostlen; ssh_pkt_getstring(pktin, &shost, &shostlen);/* skip address */ pf.shost = dupprintf("%.*s", shostlen, shost); pf.sport = ssh_pkt_getuint32(pktin); ssh_pkt_getstring(pktin, &peeraddr, &peeraddrlen); peerport = ssh_pkt_getuint32(pktin); realpf = find234(ssh->rportfwds, &pf, NULL); logeventf(ssh, "Received remote port %s:%d open request " "from %s:%d", pf.shost, pf.sport, peeraddr, peerport); sfree(pf.shost); if (realpf == NULL) { error = "Remote port is not recognised"; } else { char *err; if (realpf->share_ctx) { /* * This port forwarding is on behalf of a * connection-sharing downstream, so abandon our own * channel-open procedure and just pass the message on * to sshshare.c. */ share_got_pkt_from_server(realpf->share_ctx, pktin->type, pktin->body, pktin->length); sfree(c); return; } err = pfd_connect(&c->u.pfd.pf, realpf->dhost, realpf->dport, c, ssh->conf, realpf->pfrec->addressfamily); logeventf(ssh, "Attempting to forward remote port to " "%s:%d", realpf->dhost, realpf->dport); if (err != NULL) { logeventf(ssh, "Port open failed: %s", err); sfree(err); error = "Port open failed"; } else { logevent("Forwarded port opened successfully"); c->type = CHAN_SOCKDATA; } } } else if (typelen == 22 && !memcmp(type, "auth-agent@openssh.com", 22)) { if (!ssh->agentfwd_enabled) error = "Agent forwarding is not enabled"; else { c->type = CHAN_AGENT; /* identify channel type */ c->u.a.lensofar = 0; c->u.a.message = NULL; c->u.a.outstanding_requests = 0; } } else { error = "Unsupported channel type requested"; } c->remoteid = remid; c->halfopen = FALSE; if (error) { pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_OPEN_FAILURE); ssh2_pkt_adduint32(pktout, c->remoteid); ssh2_pkt_adduint32(pktout, SSH2_OPEN_CONNECT_FAILED); ssh2_pkt_addstring(pktout, error); ssh2_pkt_addstring(pktout, "en"); /* language tag */ ssh2_pkt_send(ssh, pktout); logeventf(ssh, "Rejected channel open: %s", error); sfree(c); } else { ssh2_channel_init(c); c->v.v2.remwindow = winsize; c->v.v2.remmaxpkt = pktsize; if (our_winsize_override) { c->v.v2.locwindow = c->v.v2.locmaxwin = c->v.v2.remlocwin = our_winsize_override; } add234(ssh->channels, c); pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_OPEN_CONFIRMATION); ssh2_pkt_adduint32(pktout, c->remoteid); ssh2_pkt_adduint32(pktout, c->localid); ssh2_pkt_adduint32(pktout, c->v.v2.locwindow); ssh2_pkt_adduint32(pktout, OUR_V2_MAXPKT); /* our max pkt size */ ssh2_pkt_send(ssh, pktout); } } void sshfwd_x11_sharing_handover(struct ssh_channel *c, void *share_cs, void *share_chan, const char *peer_addr, int peer_port, int endian, int protomajor, int protominor, const void *initial_data, int initial_len) { /* * This function is called when we've just discovered that an X * forwarding channel on which we'd been handling the initial auth * ourselves turns out to be destined for a connection-sharing * downstream. So we turn the channel into a CHAN_SHARING, meaning * that we completely stop tracking windows and buffering data and * just pass more or less unmodified SSH messages back and forth. */ c->type = CHAN_SHARING; c->u.sharing.ctx = share_cs; share_setup_x11_channel(share_cs, share_chan, c->localid, c->remoteid, c->v.v2.remwindow, c->v.v2.remmaxpkt, c->v.v2.locwindow, peer_addr, peer_port, endian, protomajor, protominor, initial_data, initial_len); } void sshfwd_x11_is_local(struct ssh_channel *c) { /* * This function is called when we've just discovered that an X * forwarding channel is _not_ destined for a connection-sharing * downstream but we're going to handle it ourselves. We stop * presenting a cautiously small window and go into ordinary data * exchange mode. */ c->u.x11.initial = FALSE; ssh2_set_window(c, ssh_is_simple(c->ssh) ? OUR_V2_BIGWIN : OUR_V2_WINSIZE); } /* * Buffer banner messages for later display at some convenient point, * if we're going to display them. */ static void ssh2_msg_userauth_banner(Ssh ssh, struct Packet *pktin) { /* Arbitrary limit to prevent unbounded inflation of buffer */ if (conf_get_int(ssh->conf, CONF_ssh_show_banner) && bufchain_size(&ssh->banner) <= 131072) { char *banner = NULL; int size = 0; ssh_pkt_getstring(pktin, &banner, &size); if (banner) bufchain_add(&ssh->banner, banner, size); } |
︙ | ︙ | |||
7309 7310 7311 7312 7313 7314 7315 7316 | case TTY_OP_BOOL: arg = ssh_tty_parse_boolean(val); break; } ssh2_pkt_addbyte(pktout, ssh_ttymodes[i].opcode); ssh2_pkt_adduint32(pktout, arg); } | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > < < < < < < < | < > | > > > > > > > > > > > > > > > > > > > > > > > < < < < < < < < < < < | | | | | | | | | | | | | | | | | | | | | | | | | | > > > > | | | | | | | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | | 8204 8205 8206 8207 8208 8209 8210 8211 8212 8213 8214 8215 8216 8217 8218 8219 8220 8221 8222 8223 8224 8225 8226 8227 8228 8229 8230 8231 8232 8233 8234 8235 8236 8237 8238 8239 8240 8241 8242 8243 8244 8245 8246 8247 8248 8249 8250 8251 8252 8253 8254 8255 8256 8257 8258 8259 8260 8261 8262 8263 8264 8265 8266 8267 8268 8269 8270 8271 8272 8273 8274 8275 8276 8277 8278 8279 8280 8281 8282 8283 8284 8285 8286 8287 8288 8289 8290 8291 8292 8293 8294 8295 8296 8297 8298 8299 8300 8301 8302 8303 8304 8305 8306 8307 8308 8309 8310 8311 8312 8313 8314 8315 8316 8317 8318 8319 8320 8321 8322 8323 8324 8325 8326 8327 8328 8329 8330 8331 8332 8333 8334 8335 8336 8337 8338 8339 8340 8341 8342 8343 8344 8345 8346 8347 8348 8349 8350 8351 8352 8353 8354 8355 8356 8357 8358 8359 8360 8361 8362 8363 8364 8365 8366 8367 8368 8369 8370 8371 8372 8373 8374 8375 8376 8377 8378 8379 8380 8381 8382 8383 8384 8385 8386 8387 8388 8389 8390 8391 8392 8393 8394 8395 8396 8397 8398 8399 8400 8401 8402 8403 8404 8405 8406 8407 8408 8409 8410 8411 8412 8413 8414 8415 8416 8417 8418 8419 8420 8421 8422 8423 8424 8425 8426 8427 8428 8429 8430 8431 8432 8433 8434 8435 8436 8437 8438 8439 8440 8441 8442 8443 8444 8445 8446 8447 8448 8449 8450 8451 8452 8453 8454 8455 8456 8457 8458 8459 8460 8461 8462 8463 8464 8465 8466 8467 8468 8469 8470 8471 8472 8473 8474 8475 8476 8477 8478 8479 8480 8481 8482 8483 8484 8485 8486 8487 8488 8489 8490 8491 8492 8493 8494 8495 8496 8497 8498 8499 8500 8501 8502 8503 8504 8505 8506 8507 8508 8509 8510 8511 8512 8513 8514 8515 8516 8517 8518 8519 8520 8521 8522 8523 8524 8525 8526 8527 8528 8529 8530 8531 8532 8533 8534 8535 8536 8537 8538 8539 8540 8541 8542 8543 8544 8545 8546 8547 8548 8549 8550 8551 8552 8553 8554 8555 8556 8557 8558 8559 8560 8561 8562 8563 8564 8565 8566 8567 8568 8569 8570 8571 8572 8573 8574 8575 8576 8577 8578 8579 8580 8581 8582 8583 8584 8585 8586 8587 8588 8589 8590 8591 8592 8593 8594 8595 8596 8597 8598 8599 8600 8601 | case TTY_OP_BOOL: arg = ssh_tty_parse_boolean(val); break; } ssh2_pkt_addbyte(pktout, ssh_ttymodes[i].opcode); ssh2_pkt_adduint32(pktout, arg); } static void ssh2_setup_x11(struct ssh_channel *c, struct Packet *pktin, void *ctx) { struct ssh2_setup_x11_state { int crLine; }; Ssh ssh = c->ssh; struct Packet *pktout; crStateP(ssh2_setup_x11_state, ctx); crBeginState; logevent("Requesting X11 forwarding"); pktout = ssh2_chanreq_init(ssh->mainchan, "x11-req", ssh2_setup_x11, s); ssh2_pkt_addbool(pktout, 0); /* many connections */ ssh2_pkt_addstring(pktout, ssh->x11auth->protoname); ssh2_pkt_addstring(pktout, ssh->x11auth->datastring); ssh2_pkt_adduint32(pktout, ssh->x11disp->screennum); ssh2_pkt_send(ssh, pktout); /* Wait to be called back with either a response packet, or NULL * meaning clean up and free our data */ crReturnV; if (pktin) { if (pktin->type == SSH2_MSG_CHANNEL_SUCCESS) { logevent("X11 forwarding enabled"); ssh->X11_fwd_enabled = TRUE; } else logevent("X11 forwarding refused"); } crFinishFreeV; } static void ssh2_setup_agent(struct ssh_channel *c, struct Packet *pktin, void *ctx) { struct ssh2_setup_agent_state { int crLine; }; Ssh ssh = c->ssh; struct Packet *pktout; crStateP(ssh2_setup_agent_state, ctx); crBeginState; logevent("Requesting OpenSSH-style agent forwarding"); pktout = ssh2_chanreq_init(ssh->mainchan, "auth-agent-req@openssh.com", ssh2_setup_agent, s); ssh2_pkt_send(ssh, pktout); /* Wait to be called back with either a response packet, or NULL * meaning clean up and free our data */ crReturnV; if (pktin) { if (pktin->type == SSH2_MSG_CHANNEL_SUCCESS) { logevent("Agent forwarding enabled"); ssh->agentfwd_enabled = TRUE; } else logevent("Agent forwarding refused"); } crFinishFreeV; } static void ssh2_setup_pty(struct ssh_channel *c, struct Packet *pktin, void *ctx) { struct ssh2_setup_pty_state { int crLine; }; Ssh ssh = c->ssh; struct Packet *pktout; crStateP(ssh2_setup_pty_state, ctx); crBeginState; /* Unpick the terminal-speed string. */ /* XXX perhaps we should allow no speeds to be sent. */ ssh->ospeed = 38400; ssh->ispeed = 38400; /* last-resort defaults */ sscanf(conf_get_str(ssh->conf, CONF_termspeed), "%d,%d", &ssh->ospeed, &ssh->ispeed); /* Build the pty request. */ pktout = ssh2_chanreq_init(ssh->mainchan, "pty-req", ssh2_setup_pty, s); ssh2_pkt_addstring(pktout, conf_get_str(ssh->conf, CONF_termtype)); ssh2_pkt_adduint32(pktout, ssh->term_width); ssh2_pkt_adduint32(pktout, ssh->term_height); ssh2_pkt_adduint32(pktout, 0); /* pixel width */ ssh2_pkt_adduint32(pktout, 0); /* pixel height */ ssh2_pkt_addstring_start(pktout); parse_ttymodes(ssh, ssh2_send_ttymode, (void *)pktout); ssh2_pkt_addbyte(pktout, SSH2_TTY_OP_ISPEED); ssh2_pkt_adduint32(pktout, ssh->ispeed); ssh2_pkt_addbyte(pktout, SSH2_TTY_OP_OSPEED); ssh2_pkt_adduint32(pktout, ssh->ospeed); ssh2_pkt_addstring_data(pktout, "\0", 1); /* TTY_OP_END */ ssh2_pkt_send(ssh, pktout); ssh->state = SSH_STATE_INTERMED; /* Wait to be called back with either a response packet, or NULL * meaning clean up and free our data */ crReturnV; if (pktin) { if (pktin->type == SSH2_MSG_CHANNEL_SUCCESS) { logeventf(ssh, "Allocated pty (ospeed %dbps, ispeed %dbps)", ssh->ospeed, ssh->ispeed); ssh->got_pty = TRUE; } else { c_write_str(ssh, "Server refused to allocate pty\r\n"); ssh->editing = ssh->echoing = 1; } } crFinishFreeV; } static void ssh2_setup_env(struct ssh_channel *c, struct Packet *pktin, void *ctx) { struct ssh2_setup_env_state { int crLine; int num_env, env_left, env_ok; }; Ssh ssh = c->ssh; struct Packet *pktout; crStateP(ssh2_setup_env_state, ctx); crBeginState; /* * Send environment variables. * * Simplest thing here is to send all the requests at once, and * then wait for a whole bunch of successes or failures. */ s->num_env = 0; { char *key, *val; for (val = conf_get_str_strs(ssh->conf, CONF_environmt, NULL, &key); val != NULL; val = conf_get_str_strs(ssh->conf, CONF_environmt, key, &key)) { pktout = ssh2_chanreq_init(ssh->mainchan, "env", ssh2_setup_env, s); ssh2_pkt_addstring(pktout, key); ssh2_pkt_addstring(pktout, val); ssh2_pkt_send(ssh, pktout); s->num_env++; } if (s->num_env) logeventf(ssh, "Sent %d environment variables", s->num_env); } if (s->num_env) { s->env_ok = 0; s->env_left = s->num_env; while (s->env_left > 0) { /* Wait to be called back with either a response packet, * or NULL meaning clean up and free our data */ crReturnV; if (!pktin) goto out; if (pktin->type == SSH2_MSG_CHANNEL_SUCCESS) s->env_ok++; s->env_left--; } if (s->env_ok == s->num_env) { logevent("All environment variables successfully set"); } else if (s->env_ok == 0) { logevent("All environment variables refused"); c_write_str(ssh, "Server refused to set environment variables\r\n"); } else { logeventf(ssh, "%d environment variables refused", s->num_env - s->env_ok); c_write_str(ssh, "Server refused to set all environment variables\r\n"); } } out:; crFinishFreeV; } /* * Handle the SSH-2 userauth and connection layers. */ static void ssh2_msg_authconn(Ssh ssh, struct Packet *pktin) { do_ssh2_authconn(ssh, NULL, 0, pktin); } static void ssh2_response_authconn(struct ssh_channel *c, struct Packet *pktin, void *ctx) { do_ssh2_authconn(c->ssh, NULL, 0, pktin); } static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, struct Packet *pktin) { struct do_ssh2_authconn_state { int crLine; enum { AUTH_TYPE_NONE, AUTH_TYPE_PUBLICKEY, AUTH_TYPE_PUBLICKEY_OFFER_LOUD, AUTH_TYPE_PUBLICKEY_OFFER_QUIET, AUTH_TYPE_PASSWORD, AUTH_TYPE_GSSAPI, /* always QUIET */ AUTH_TYPE_KEYBOARD_INTERACTIVE, AUTH_TYPE_KEYBOARD_INTERACTIVE_QUIET } type; int done_service_req; int gotit, need_pw, can_pubkey, can_passwd, can_keyb_inter; int tried_pubkey_config, done_agent; #ifndef NO_GSSAPI int can_gssapi; int tried_gssapi; #endif int kbd_inter_refused; int we_are_in, userauth_success; prompts_t *cur_prompt; int num_prompts; char *username; char *password; int got_username; void *publickey_blob; int publickey_bloblen; int publickey_encrypted; char *publickey_algorithm; char *publickey_comment; unsigned char agent_request[5], *agent_response, *agentp; int agent_responselen; unsigned char *pkblob_in_agent; int keyi, nkeys; char *pkblob, *alg, *commentp; int pklen, alglen, commentlen; int siglen, retlen, len; char *q, *agentreq, *ret; int try_send; struct Packet *pktout; Filename *keyfile; #ifndef NO_GSSAPI struct ssh_gss_library *gsslib; Ssh_gss_ctx gss_ctx; Ssh_gss_buf gss_buf; Ssh_gss_buf gss_rcvtok, gss_sndtok; Ssh_gss_name gss_srv_name; Ssh_gss_stat gss_stat; #endif }; crState(do_ssh2_authconn_state); crBeginState; /* Register as a handler for all the messages this coroutine handles. */ ssh->packet_dispatch[SSH2_MSG_SERVICE_ACCEPT] = ssh2_msg_authconn; ssh->packet_dispatch[SSH2_MSG_USERAUTH_REQUEST] = ssh2_msg_authconn; ssh->packet_dispatch[SSH2_MSG_USERAUTH_FAILURE] = ssh2_msg_authconn; ssh->packet_dispatch[SSH2_MSG_USERAUTH_SUCCESS] = ssh2_msg_authconn; ssh->packet_dispatch[SSH2_MSG_USERAUTH_BANNER] = ssh2_msg_authconn; ssh->packet_dispatch[SSH2_MSG_USERAUTH_PK_OK] = ssh2_msg_authconn; /* ssh->packet_dispatch[SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ] = ssh2_msg_authconn; duplicate case value */ /* ssh->packet_dispatch[SSH2_MSG_USERAUTH_INFO_REQUEST] = ssh2_msg_authconn; duplicate case value */ ssh->packet_dispatch[SSH2_MSG_USERAUTH_INFO_RESPONSE] = ssh2_msg_authconn; ssh->packet_dispatch[SSH2_MSG_GLOBAL_REQUEST] = ssh2_msg_authconn; ssh->packet_dispatch[SSH2_MSG_REQUEST_SUCCESS] = ssh2_msg_authconn; ssh->packet_dispatch[SSH2_MSG_REQUEST_FAILURE] = ssh2_msg_authconn; ssh->packet_dispatch[SSH2_MSG_CHANNEL_OPEN] = ssh2_msg_authconn; ssh->packet_dispatch[SSH2_MSG_CHANNEL_OPEN_CONFIRMATION] = ssh2_msg_authconn; ssh->packet_dispatch[SSH2_MSG_CHANNEL_OPEN_FAILURE] = ssh2_msg_authconn; ssh->packet_dispatch[SSH2_MSG_CHANNEL_WINDOW_ADJUST] = ssh2_msg_authconn; ssh->packet_dispatch[SSH2_MSG_CHANNEL_DATA] = ssh2_msg_authconn; ssh->packet_dispatch[SSH2_MSG_CHANNEL_EXTENDED_DATA] = ssh2_msg_authconn; ssh->packet_dispatch[SSH2_MSG_CHANNEL_EOF] = ssh2_msg_authconn; ssh->packet_dispatch[SSH2_MSG_CHANNEL_CLOSE] = ssh2_msg_authconn; s->done_service_req = FALSE; s->we_are_in = s->userauth_success = FALSE; s->agent_response = NULL; #ifndef NO_GSSAPI s->tried_gssapi = FALSE; #endif if (!ssh->bare_connection) { if (!conf_get_int(ssh->conf, CONF_ssh_no_userauth)) { /* * Request userauth protocol, and await a response to it. */ s->pktout = ssh2_pkt_init(SSH2_MSG_SERVICE_REQUEST); ssh2_pkt_addstring(s->pktout, "ssh-userauth"); ssh2_pkt_send(ssh, s->pktout); crWaitUntilV(pktin); if (pktin->type == SSH2_MSG_SERVICE_ACCEPT) s->done_service_req = TRUE; } if (!s->done_service_req) { /* * Request connection protocol directly, without authentication. */ s->pktout = ssh2_pkt_init(SSH2_MSG_SERVICE_REQUEST); ssh2_pkt_addstring(s->pktout, "ssh-connection"); ssh2_pkt_send(ssh, s->pktout); crWaitUntilV(pktin); if (pktin->type == SSH2_MSG_SERVICE_ACCEPT) { s->we_are_in = TRUE; /* no auth required */ } else { bombout(("Server refused service request")); crStopV; } } } else { s->we_are_in = TRUE; } /* Arrange to be able to deal with any BANNERs that come in. * (We do this now as packets may come in during the next bit.) */ bufchain_init(&ssh->banner); ssh->packet_dispatch[SSH2_MSG_USERAUTH_BANNER] = ssh2_msg_userauth_banner; /* * Misc one-time setup for authentication. */ s->publickey_blob = NULL; if (!s->we_are_in) { /* * Load the public half of any configured public key file * for later use. */ s->keyfile = conf_get_filename(ssh->conf, CONF_keyfile); if (!filename_is_null(s->keyfile)) { int keytype; logeventf(ssh, "Reading private key file \"%.150s\"", filename_to_str(s->keyfile)); keytype = key_type(s->keyfile); if (keytype == SSH_KEYTYPE_SSH2) { const char *error; s->publickey_blob = ssh2_userkey_loadpub(s->keyfile, &s->publickey_algorithm, &s->publickey_bloblen, &s->publickey_comment, &error); if (s->publickey_blob) { s->publickey_encrypted = ssh2_userkey_encrypted(s->keyfile, NULL); } else { char *msgbuf; logeventf(ssh, "Unable to load private key (%s)", error); msgbuf = dupprintf("Unable to load private key file " "\"%.150s\" (%s)\r\n", filename_to_str(s->keyfile), error); c_write_str(ssh, msgbuf); sfree(msgbuf); } } else { char *msgbuf; logeventf(ssh, "Unable to use this key file (%s)", key_type_to_str(keytype)); msgbuf = dupprintf("Unable to use key file \"%.150s\"" " (%s)\r\n", filename_to_str(s->keyfile), key_type_to_str(keytype)); c_write_str(ssh, msgbuf); sfree(msgbuf); s->publickey_blob = NULL; } } /* * Find out about any keys Pageant has (but if there's a * public key configured, filter out all others). */ s->nkeys = 0; s->agent_response = NULL; s->pkblob_in_agent = NULL; if (conf_get_int(ssh->conf, CONF_tryagent) && agent_exists()) { void *r; logevent("Pageant is running. Requesting keys."); /* Request the keys held by the agent. */ PUT_32BIT(s->agent_request, 1); |
︙ | ︙ | |||
7556 7557 7558 7559 7560 7561 7562 | } s->agent_response = (unsigned char *) r; if (s->agent_response && s->agent_responselen >= 5 && s->agent_response[4] == SSH2_AGENT_IDENTITIES_ANSWER) { int keyi; unsigned char *p; p = s->agent_response + 5; | > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | > | 8615 8616 8617 8618 8619 8620 8621 8622 8623 8624 8625 8626 8627 8628 8629 8630 8631 8632 8633 8634 8635 8636 8637 8638 8639 8640 8641 8642 8643 8644 8645 8646 8647 8648 8649 8650 8651 8652 8653 8654 8655 8656 8657 8658 8659 8660 8661 8662 8663 8664 8665 8666 8667 8668 8669 8670 8671 8672 8673 8674 8675 8676 8677 8678 8679 8680 8681 8682 8683 8684 8685 8686 8687 8688 8689 8690 8691 8692 8693 8694 8695 8696 | } s->agent_response = (unsigned char *) r; if (s->agent_response && s->agent_responselen >= 5 && s->agent_response[4] == SSH2_AGENT_IDENTITIES_ANSWER) { int keyi; unsigned char *p; p = s->agent_response + 5; s->nkeys = toint(GET_32BIT(p)); /* * Vet the Pageant response to ensure that the key * count and blob lengths make sense. */ if (s->nkeys < 0) { logeventf(ssh, "Pageant response contained a negative" " key count %d", s->nkeys); s->nkeys = 0; goto done_agent_query; } else { unsigned char *q = p + 4; int lenleft = s->agent_responselen - 5 - 4; for (keyi = 0; keyi < s->nkeys; keyi++) { int bloblen, commentlen; if (lenleft < 4) { logeventf(ssh, "Pageant response was truncated"); s->nkeys = 0; goto done_agent_query; } bloblen = toint(GET_32BIT(q)); if (bloblen < 0 || bloblen > lenleft) { logeventf(ssh, "Pageant response was truncated"); s->nkeys = 0; goto done_agent_query; } lenleft -= 4 + bloblen; q += 4 + bloblen; commentlen = toint(GET_32BIT(q)); if (commentlen < 0 || commentlen > lenleft) { logeventf(ssh, "Pageant response was truncated"); s->nkeys = 0; goto done_agent_query; } lenleft -= 4 + commentlen; q += 4 + commentlen; } } p += 4; logeventf(ssh, "Pageant has %d SSH-2 keys", s->nkeys); if (s->publickey_blob) { /* See if configured key is in agent. */ for (keyi = 0; keyi < s->nkeys; keyi++) { s->pklen = toint(GET_32BIT(p)); if (s->pklen == s->publickey_bloblen && !memcmp(p+4, s->publickey_blob, s->publickey_bloblen)) { logeventf(ssh, "Pageant key #%d matches " "configured key file", keyi); s->keyi = keyi; s->pkblob_in_agent = p; break; } p += 4 + s->pklen; p += toint(GET_32BIT(p)) + 4; /* comment */ } if (!s->pkblob_in_agent) { logevent("Configured key file not in Pageant"); s->nkeys = 0; } } } else { logevent("Failed to get reply from Pageant"); } done_agent_query:; } } /* * We repeat this whole loop, including the username prompt, * until we manage a successful authentication. If the user |
︙ | ︙ | |||
7611 7612 7613 7614 7615 7616 7617 | * - people with a key held in Pageant, who might not have * logged in to a particular machine before; so they want to * type a username, and then _either_ their key will be * accepted, _or_ they will type a password. If they mistype * the username they will want to be able to get back and * retype it! */ | < | | < | < | < | | | 8711 8712 8713 8714 8715 8716 8717 8718 8719 8720 8721 8722 8723 8724 8725 8726 8727 8728 8729 8730 8731 8732 8733 8734 8735 8736 8737 8738 8739 8740 8741 8742 8743 8744 8745 8746 8747 8748 8749 8750 8751 8752 8753 8754 8755 8756 8757 8758 8759 8760 8761 8762 8763 8764 8765 8766 8767 8768 8769 8770 8771 8772 8773 8774 8775 8776 8777 8778 | * - people with a key held in Pageant, who might not have * logged in to a particular machine before; so they want to * type a username, and then _either_ their key will be * accepted, _or_ they will type a password. If they mistype * the username they will want to be able to get back and * retype it! */ s->got_username = FALSE; while (!s->we_are_in) { /* * Get a username. */ if (s->got_username && !conf_get_int(ssh->conf, CONF_change_username)) { /* * We got a username last time round this loop, and * with change_username turned off we don't try to get * it again. */ } else if ((ssh->username = get_remote_username(ssh->conf)) == NULL) { int ret; /* need not be kept over crReturn */ s->cur_prompt = new_prompts(ssh->frontend); s->cur_prompt->to_server = TRUE; s->cur_prompt->name = dupstr("SSH login name"); add_prompt(s->cur_prompt, dupstr("login as: "), TRUE); ret = get_userpass_input(s->cur_prompt, NULL, 0); while (ret < 0) { ssh->send_ok = 1; crWaitUntilV(!pktin); ret = get_userpass_input(s->cur_prompt, in, inlen); ssh->send_ok = 0; } if (!ret) { /* * get_userpass_input() failed to get a username. * Terminate. */ free_prompts(s->cur_prompt); ssh_disconnect(ssh, "No username provided", NULL, 0, TRUE); crStopV; } ssh->username = dupstr(s->cur_prompt->prompts[0]->result); free_prompts(s->cur_prompt); } else { char *stuff; if ((flags & FLAG_VERBOSE) || (flags & FLAG_INTERACTIVE)) { stuff = dupprintf("Using username \"%s\".\r\n", ssh->username); c_write_str(ssh, stuff); sfree(stuff); } } s->got_username = TRUE; /* * Send an authentication request using method "none": (a) * just in case it succeeds, and (b) so that we know what * authentication methods we can usefully try next. */ ssh->pkt_actx = SSH2_PKTCTX_NOAUTH; s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST); ssh2_pkt_addstring(s->pktout, ssh->username); ssh2_pkt_addstring(s->pktout, "ssh-connection");/* service requested */ ssh2_pkt_addstring(s->pktout, "none"); /* method */ ssh2_pkt_send(ssh, s->pktout); s->type = AUTH_TYPE_NONE; s->gotit = FALSE; s->we_are_in = FALSE; |
︙ | ︙ | |||
7798 7799 7800 7801 7802 7803 7804 | logevent("Keyboard-interactive authentication failed"); c_write_str(ssh, "Access denied\r\n"); } else { assert(s->type == AUTH_TYPE_PASSWORD); logevent("Password authentication failed"); c_write_str(ssh, "Access denied\r\n"); | | < < < < < < | | | | | | | | 8894 8895 8896 8897 8898 8899 8900 8901 8902 8903 8904 8905 8906 8907 8908 8909 8910 8911 8912 8913 8914 8915 8916 8917 8918 8919 8920 8921 8922 8923 8924 8925 8926 8927 8928 8929 8930 8931 8932 8933 8934 8935 8936 8937 8938 8939 8940 8941 8942 8943 8944 8945 8946 8947 8948 8949 8950 8951 8952 8953 8954 8955 8956 8957 8958 8959 8960 8961 8962 | logevent("Keyboard-interactive authentication failed"); c_write_str(ssh, "Access denied\r\n"); } else { assert(s->type == AUTH_TYPE_PASSWORD); logevent("Password authentication failed"); c_write_str(ssh, "Access denied\r\n"); if (conf_get_int(ssh->conf, CONF_change_username)) { /* XXX perhaps we should allow * keyboard-interactive to do this too? */ s->we_are_in = FALSE; break; } } } else { c_write_str(ssh, "Further authentication required\r\n"); logevent("Further authentication required"); } s->can_pubkey = in_commasep_string("publickey", methods, methlen); s->can_passwd = in_commasep_string("password", methods, methlen); s->can_keyb_inter = conf_get_int(ssh->conf, CONF_try_ki_auth) && in_commasep_string("keyboard-interactive", methods, methlen); #ifndef NO_GSSAPI if (!ssh->gsslibs) ssh->gsslibs = ssh_gss_setup(ssh->conf); s->can_gssapi = conf_get_int(ssh->conf, CONF_try_gssapi_auth) && in_commasep_string("gssapi-with-mic", methods, methlen) && ssh->gsslibs->nlibraries > 0; #endif } ssh->pkt_actx = SSH2_PKTCTX_NOAUTH; if (s->can_pubkey && !s->done_agent && s->nkeys) { /* * Attempt public-key authentication using a key from Pageant. */ ssh->pkt_actx = SSH2_PKTCTX_PUBLICKEY; logeventf(ssh, "Trying Pageant key #%d", s->keyi); /* Unpack key from agent response */ s->pklen = toint(GET_32BIT(s->agentp)); s->agentp += 4; s->pkblob = (char *)s->agentp; s->agentp += s->pklen; s->alglen = toint(GET_32BIT(s->pkblob)); s->alg = s->pkblob + 4; s->commentlen = toint(GET_32BIT(s->agentp)); s->agentp += 4; s->commentp = (char *)s->agentp; s->agentp += s->commentlen; /* s->agentp now points at next key, if any */ /* See if server will accept it */ s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST); ssh2_pkt_addstring(s->pktout, ssh->username); ssh2_pkt_addstring(s->pktout, "ssh-connection"); /* service requested */ ssh2_pkt_addstring(s->pktout, "publickey"); /* method */ ssh2_pkt_addbool(s->pktout, FALSE); /* no signature included */ ssh2_pkt_addstring_start(s->pktout); ssh2_pkt_addstring_data(s->pktout, s->alg, s->alglen); |
︙ | ︙ | |||
7893 7894 7895 7896 7897 7898 7899 | } /* * Server is willing to accept the key. * Construct a SIGN_REQUEST. */ s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST); | | | 8983 8984 8985 8986 8987 8988 8989 8990 8991 8992 8993 8994 8995 8996 8997 | } /* * Server is willing to accept the key. * Construct a SIGN_REQUEST. */ s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST); ssh2_pkt_addstring(s->pktout, ssh->username); ssh2_pkt_addstring(s->pktout, "ssh-connection"); /* service requested */ ssh2_pkt_addstring(s->pktout, "publickey"); /* method */ ssh2_pkt_addbool(s->pktout, TRUE); /* signature included */ ssh2_pkt_addstring_start(s->pktout); ssh2_pkt_addstring_data(s->pktout, s->alg, s->alglen); |
︙ | ︙ | |||
7954 7955 7956 7957 7958 7959 7960 | } while (pktin || inlen > 0); vret = ssh->agent_response; s->retlen = ssh->agent_response_len; } s->ret = vret; sfree(s->agentreq); if (s->ret) { | > | > | 9044 9045 9046 9047 9048 9049 9050 9051 9052 9053 9054 9055 9056 9057 9058 9059 9060 | } while (pktin || inlen > 0); vret = ssh->agent_response; s->retlen = ssh->agent_response_len; } s->ret = vret; sfree(s->agentreq); if (s->ret) { if (s->retlen >= 9 && s->ret[4] == SSH2_AGENT_SIGN_RESPONSE && GET_32BIT(s->ret + 5) <= (unsigned)(s->retlen-9)) { logevent("Sending Pageant's response"); ssh2_add_sigblob(ssh, s->pktout, s->pkblob, s->pklen, s->ret + 9, GET_32BIT(s->ret + 5)); ssh2_pkt_send(ssh, s->pktout); s->type = AUTH_TYPE_PUBLICKEY; |
︙ | ︙ | |||
7980 7981 7982 7983 7984 7985 7986 | s->tried_pubkey_config = TRUE; } else { s->keyi++; if (s->keyi >= s->nkeys) s->done_agent = TRUE; } | < | | < < < < < < < < < < < < < < < < | | 9072 9073 9074 9075 9076 9077 9078 9079 9080 9081 9082 9083 9084 9085 9086 9087 9088 9089 9090 9091 9092 9093 9094 9095 9096 9097 9098 9099 9100 9101 9102 9103 | s->tried_pubkey_config = TRUE; } else { s->keyi++; if (s->keyi >= s->nkeys) s->done_agent = TRUE; } } else if (s->can_pubkey && s->publickey_blob && !s->tried_pubkey_config) { struct ssh2_userkey *key; /* not live over crReturn */ char *passphrase; /* not live over crReturn */ ssh->pkt_actx = SSH2_PKTCTX_PUBLICKEY; s->tried_pubkey_config = TRUE; /* * Try the public key supplied in the configuration. * * First, offer the public blob to see if the server is * willing to accept it. */ s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST); ssh2_pkt_addstring(s->pktout, ssh->username); ssh2_pkt_addstring(s->pktout, "ssh-connection"); /* service requested */ ssh2_pkt_addstring(s->pktout, "publickey"); /* method */ ssh2_pkt_addbool(s->pktout, FALSE); /* no signature included */ ssh2_pkt_addstring(s->pktout, s->publickey_algorithm); ssh2_pkt_addstring_start(s->pktout); |
︙ | ︙ | |||
8049 8050 8051 8052 8053 8054 8055 | c_write_str(ssh, "Authenticating with public key \""); c_write_str(ssh, s->publickey_comment); c_write_str(ssh, "\"\r\n"); } key = NULL; while (!key) { const char *error; /* not live over crReturn */ | < | < < < < < < < < | | 9124 9125 9126 9127 9128 9129 9130 9131 9132 9133 9134 9135 9136 9137 9138 9139 9140 9141 9142 9143 9144 9145 9146 9147 9148 9149 | c_write_str(ssh, "Authenticating with public key \""); c_write_str(ssh, s->publickey_comment); c_write_str(ssh, "\"\r\n"); } key = NULL; while (!key) { const char *error; /* not live over crReturn */ if (s->publickey_encrypted) { /* * Get a passphrase from the user. */ int ret; /* need not be kept over crReturn */ s->cur_prompt = new_prompts(ssh->frontend); s->cur_prompt->to_server = FALSE; s->cur_prompt->name = dupstr("SSH key passphrase"); add_prompt(s->cur_prompt, dupprintf("Passphrase for key \"%.100s\": ", s->publickey_comment), FALSE); ret = get_userpass_input(s->cur_prompt, NULL, 0); while (ret < 0) { ssh->send_ok = 1; crWaitUntilV(!pktin); ret = get_userpass_input(s->cur_prompt, in, inlen); ssh->send_ok = 0; |
︙ | ︙ | |||
8097 8098 8099 8100 8101 8102 8103 | } else { passphrase = NULL; /* no passphrase needed */ } /* * Try decrypting the key. */ | | < < < < < < < < < < < < < < < < < < < < < < | < | | 9163 9164 9165 9166 9167 9168 9169 9170 9171 9172 9173 9174 9175 9176 9177 9178 9179 9180 9181 | } else { passphrase = NULL; /* no passphrase needed */ } /* * Try decrypting the key. */ s->keyfile = conf_get_filename(ssh->conf, CONF_keyfile); key = ssh2_load_userkey(s->keyfile, passphrase, &error); if (passphrase) { /* burn the evidence */ smemclr(passphrase, strlen(passphrase)); sfree(passphrase); } if (key == SSH2_WRONG_PASSPHRASE || key == NULL) { if (passphrase && (key == SSH2_WRONG_PASSPHRASE)) { c_write_str(ssh, "Wrong passphrase\r\n"); key = NULL; |
︙ | ︙ | |||
8154 8155 8156 8157 8158 8159 8160 | /* * We have loaded the private key and the server * has announced that it's willing to accept it. * Hallelujah. Generate a signature and send it. */ s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST); | | < < < < < < < < < < < < < < | 9197 9198 9199 9200 9201 9202 9203 9204 9205 9206 9207 9208 9209 9210 9211 9212 9213 9214 9215 9216 9217 9218 9219 9220 | /* * We have loaded the private key and the server * has announced that it's willing to accept it. * Hallelujah. Generate a signature and send it. */ s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST); ssh2_pkt_addstring(s->pktout, ssh->username); ssh2_pkt_addstring(s->pktout, "ssh-connection"); /* service requested */ ssh2_pkt_addstring(s->pktout, "publickey"); /* method */ ssh2_pkt_addbool(s->pktout, TRUE); /* signature follows */ ssh2_pkt_addstring(s->pktout, key->alg->name); pkblob = key->alg->public_blob(key->data, &pkblob_len); ssh2_pkt_addstring_start(s->pktout); ssh2_pkt_addstring_data(s->pktout, (char *)pkblob, pkblob_len); /* * The data to be signed is: * |
︙ | ︙ | |||
8207 8208 8209 8210 8211 8212 8213 | memcpy(sigdata+p, ssh->v2_session_id, ssh->v2_session_id_len); p += ssh->v2_session_id_len; memcpy(sigdata+p, s->pktout->data + 5, s->pktout->length - 5); p += s->pktout->length - 5; assert(p == sigdata_len); | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 9236 9237 9238 9239 9240 9241 9242 9243 9244 9245 9246 9247 9248 9249 9250 9251 9252 9253 9254 9255 9256 9257 9258 9259 9260 | memcpy(sigdata+p, ssh->v2_session_id, ssh->v2_session_id_len); p += ssh->v2_session_id_len; memcpy(sigdata+p, s->pktout->data + 5, s->pktout->length - 5); p += s->pktout->length - 5; assert(p == sigdata_len); sigblob = key->alg->sign(key->data, (char *)sigdata, sigdata_len, &sigblob_len); ssh2_add_sigblob(ssh, s->pktout, pkblob, pkblob_len, sigblob, sigblob_len); sfree(pkblob); sfree(sigblob); sfree(sigdata); ssh2_pkt_send(ssh, s->pktout); logevent("Sent public key signature"); s->type = AUTH_TYPE_PUBLICKEY; key->alg->freekey(key->data); } #ifndef NO_GSSAPI } else if (s->can_gssapi && !s->tried_gssapi) { /* GSSAPI Authentication */ |
︙ | ︙ | |||
8282 8283 8284 8285 8286 8287 8288 | * Pick the highest GSS library on the preference * list. */ { int i, j; s->gsslib = NULL; for (i = 0; i < ngsslibs; i++) { | > | | 9271 9272 9273 9274 9275 9276 9277 9278 9279 9280 9281 9282 9283 9284 9285 9286 | * Pick the highest GSS library on the preference * list. */ { int i, j; s->gsslib = NULL; for (i = 0; i < ngsslibs; i++) { int want_id = conf_get_int_int(ssh->conf, CONF_ssh_gsslist, i); for (j = 0; j < ssh->gsslibs->nlibraries; j++) if (ssh->gsslibs->libraries[j].id == want_id) { s->gsslib = &ssh->gsslibs->libraries[j]; goto got_gsslib; /* double break */ } } got_gsslib: |
︙ | ︙ | |||
8305 8306 8307 8308 8309 8310 8311 | } if (s->gsslib->gsslogmsg) logevent(s->gsslib->gsslogmsg); /* Sending USERAUTH_REQUEST with "gssapi-with-mic" method */ s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST); | | | 9295 9296 9297 9298 9299 9300 9301 9302 9303 9304 9305 9306 9307 9308 9309 | } if (s->gsslib->gsslogmsg) logevent(s->gsslib->gsslogmsg); /* Sending USERAUTH_REQUEST with "gssapi-with-mic" method */ s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST); ssh2_pkt_addstring(s->pktout, ssh->username); ssh2_pkt_addstring(s->pktout, "ssh-connection"); ssh2_pkt_addstring(s->pktout, "gssapi-with-mic"); logevent("Attempting GSSAPI authentication"); /* add mechanism info */ s->gsslib->indicate_mech(s->gsslib, &s->gss_buf); |
︙ | ︙ | |||
8377 8378 8379 8380 8381 8382 8383 | /* now enter the loop */ do { s->gss_stat = s->gsslib->init_sec_context (s->gsslib, &s->gss_ctx, s->gss_srv_name, | | | 9367 9368 9369 9370 9371 9372 9373 9374 9375 9376 9377 9378 9379 9380 9381 | /* now enter the loop */ do { s->gss_stat = s->gsslib->init_sec_context (s->gsslib, &s->gss_ctx, s->gss_srv_name, conf_get_int(ssh->conf, CONF_gssapifwd), &s->gss_rcvtok, &s->gss_sndtok); if (s->gss_stat!=SSH_GSS_S_COMPLETE && s->gss_stat!=SSH_GSS_S_CONTINUE_NEEDED) { logevent("GSSAPI authentication initialisation failed"); |
︙ | ︙ | |||
8433 8434 8435 8436 8437 8438 8439 | /* Now send the MIC */ s->pktout = ssh2_pkt_init(0); micoffset = s->pktout->length; ssh_pkt_addstring_start(s->pktout); ssh_pkt_addstring_data(s->pktout, (char *)ssh->v2_session_id, ssh->v2_session_id_len); ssh_pkt_addbyte(s->pktout, SSH2_MSG_USERAUTH_REQUEST); | | | 9423 9424 9425 9426 9427 9428 9429 9430 9431 9432 9433 9434 9435 9436 9437 | /* Now send the MIC */ s->pktout = ssh2_pkt_init(0); micoffset = s->pktout->length; ssh_pkt_addstring_start(s->pktout); ssh_pkt_addstring_data(s->pktout, (char *)ssh->v2_session_id, ssh->v2_session_id_len); ssh_pkt_addbyte(s->pktout, SSH2_MSG_USERAUTH_REQUEST); ssh_pkt_addstring(s->pktout, ssh->username); ssh_pkt_addstring(s->pktout, "ssh-connection"); ssh_pkt_addstring(s->pktout, "gssapi-with-mic"); s->gss_buf.value = (char *)s->pktout->data + micoffset; s->gss_buf.length = s->pktout->length - micoffset; s->gsslib->get_mic(s->gsslib, s->gss_ctx, &s->gss_buf, &mic); |
︙ | ︙ | |||
8464 8465 8466 8467 8468 8469 8470 | */ s->type = AUTH_TYPE_KEYBOARD_INTERACTIVE; ssh->pkt_actx = SSH2_PKTCTX_KBDINTER; s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST); | | | 9454 9455 9456 9457 9458 9459 9460 9461 9462 9463 9464 9465 9466 9467 9468 | */ s->type = AUTH_TYPE_KEYBOARD_INTERACTIVE; ssh->pkt_actx = SSH2_PKTCTX_KBDINTER; s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST); ssh2_pkt_addstring(s->pktout, ssh->username); ssh2_pkt_addstring(s->pktout, "ssh-connection"); /* service requested */ ssh2_pkt_addstring(s->pktout, "keyboard-interactive"); /* method */ ssh2_pkt_addstring(s->pktout, ""); /* lang */ ssh2_pkt_addstring(s->pktout, ""); /* submethods */ ssh2_pkt_send(ssh, s->pktout); |
︙ | ︙ | |||
8525 8526 8527 8528 8529 8530 8531 | echo = ssh2_pkt_getbool(pktin); if (!prompt_len) { prompt = noprompt; prompt_len = lenof(noprompt)-1; } add_prompt(s->cur_prompt, dupprintf("%.*s", prompt_len, prompt), | | | 9515 9516 9517 9518 9519 9520 9521 9522 9523 9524 9525 9526 9527 9528 9529 | echo = ssh2_pkt_getbool(pktin); if (!prompt_len) { prompt = noprompt; prompt_len = lenof(noprompt)-1; } add_prompt(s->cur_prompt, dupprintf("%.*s", prompt_len, prompt), echo); } if (name_len) { /* FIXME: better prefix to distinguish from * local prompts? */ s->cur_prompt->name = dupprintf("SSH server: %.*s", name_len, name); |
︙ | ︙ | |||
8586 8587 8588 8589 8590 8591 8592 | /* * Send the response(s) to the server. */ s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_INFO_RESPONSE); ssh2_pkt_adduint32(s->pktout, s->num_prompts); for (i=0; i < s->num_prompts; i++) { | < < | 9576 9577 9578 9579 9580 9581 9582 9583 9584 9585 9586 9587 9588 9589 9590 9591 | /* * Send the response(s) to the server. */ s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_INFO_RESPONSE); ssh2_pkt_adduint32(s->pktout, s->num_prompts); for (i=0; i < s->num_prompts; i++) { ssh2_pkt_addstring(s->pktout, s->cur_prompt->prompts[i]->result); } ssh2_pkt_send_with_padding(ssh, s->pktout, 256); /* * Free the prompts structure from this iteration. * If there's another, a new one will be allocated * when we return to the top of this while loop. |
︙ | ︙ | |||
8626 8627 8628 8629 8630 8631 8632 | int changereq_first_time; /* not live over crReturn */ ssh->pkt_actx = SSH2_PKTCTX_PASSWORD; s->cur_prompt = new_prompts(ssh->frontend); s->cur_prompt->to_server = TRUE; s->cur_prompt->name = dupstr("SSH password"); | | | | | 9614 9615 9616 9617 9618 9619 9620 9621 9622 9623 9624 9625 9626 9627 9628 9629 9630 9631 | int changereq_first_time; /* not live over crReturn */ ssh->pkt_actx = SSH2_PKTCTX_PASSWORD; s->cur_prompt = new_prompts(ssh->frontend); s->cur_prompt->to_server = TRUE; s->cur_prompt->name = dupstr("SSH password"); add_prompt(s->cur_prompt, dupprintf("%s@%s's password: ", ssh->username, ssh->savedhost), FALSE); ret = get_userpass_input(s->cur_prompt, NULL, 0); while (ret < 0) { ssh->send_ok = 1; crWaitUntilV(!pktin); ret = get_userpass_input(s->cur_prompt, in, inlen); ssh->send_ok = 0; |
︙ | ︙ | |||
8667 8668 8669 8670 8671 8672 8673 | * user's password. * * Anyone using a password longer than 256 bytes * probably doesn't have much to worry about from * people who find out how long their password is! */ s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST); | | < < | 9655 9656 9657 9658 9659 9660 9661 9662 9663 9664 9665 9666 9667 9668 9669 9670 9671 9672 9673 9674 | * user's password. * * Anyone using a password longer than 256 bytes * probably doesn't have much to worry about from * people who find out how long their password is! */ s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST); ssh2_pkt_addstring(s->pktout, ssh->username); ssh2_pkt_addstring(s->pktout, "ssh-connection"); /* service requested */ ssh2_pkt_addstring(s->pktout, "password"); ssh2_pkt_addbool(s->pktout, FALSE); ssh2_pkt_addstring(s->pktout, s->password); ssh2_pkt_send_with_padding(ssh, s->pktout, 256); logevent("Sent password"); s->type = AUTH_TYPE_PASSWORD; /* * Wait for next packet, in case it's a password change * request. |
︙ | ︙ | |||
8731 8732 8733 8734 8735 8736 8737 | * reluctantly, we prompt for the old password again. * * (On the other hand, some servers don't even bother * to check this field.) */ add_prompt(s->cur_prompt, dupstr("Current password (blank for previously entered password): "), | | | | | | | 9717 9718 9719 9720 9721 9722 9723 9724 9725 9726 9727 9728 9729 9730 9731 9732 9733 9734 9735 9736 9737 9738 9739 9740 9741 9742 9743 9744 9745 9746 9747 9748 9749 9750 9751 9752 9753 9754 9755 9756 9757 9758 9759 9760 9761 9762 9763 9764 9765 9766 9767 9768 9769 9770 9771 9772 | * reluctantly, we prompt for the old password again. * * (On the other hand, some servers don't even bother * to check this field.) */ add_prompt(s->cur_prompt, dupstr("Current password (blank for previously entered password): "), FALSE); add_prompt(s->cur_prompt, dupstr("Enter new password: "), FALSE); add_prompt(s->cur_prompt, dupstr("Confirm new password: "), FALSE); /* * Loop until the user manages to enter the same * password twice. */ while (!got_new) { ret = get_userpass_input(s->cur_prompt, NULL, 0); while (ret < 0) { ssh->send_ok = 1; crWaitUntilV(!pktin); ret = get_userpass_input(s->cur_prompt, in, inlen); ssh->send_ok = 0; } if (!ret) { /* * Failed to get responses. Terminate. */ /* burn the evidence */ free_prompts(s->cur_prompt); smemclr(s->password, strlen(s->password)); sfree(s->password); ssh_disconnect(ssh, NULL, "Unable to authenticate", SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER, TRUE); crStopV; } /* * If the user specified a new original password * (IYSWIM), overwrite any previously specified * one. * (A side effect is that the user doesn't have to * re-enter it if they louse up the new password.) */ if (s->cur_prompt->prompts[0]->result[0]) { smemclr(s->password, strlen(s->password)); /* burn the evidence */ sfree(s->password); s->password = dupstr(s->cur_prompt->prompts[0]->result); } /* |
︙ | ︙ | |||
8796 8797 8798 8799 8800 8801 8802 | } /* * Send the new password (along with the old one). * (see above for padding rationale) */ s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST); | | < < | 9782 9783 9784 9785 9786 9787 9788 9789 9790 9791 9792 9793 9794 9795 9796 9797 9798 9799 9800 9801 9802 9803 9804 | } /* * Send the new password (along with the old one). * (see above for padding rationale) */ s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST); ssh2_pkt_addstring(s->pktout, ssh->username); ssh2_pkt_addstring(s->pktout, "ssh-connection"); /* service requested */ ssh2_pkt_addstring(s->pktout, "password"); ssh2_pkt_addbool(s->pktout, TRUE); ssh2_pkt_addstring(s->pktout, s->password); ssh2_pkt_addstring(s->pktout, s->cur_prompt->prompts[1]->result); free_prompts(s->cur_prompt); ssh2_pkt_send_with_padding(ssh, s->pktout, 256); logevent("Sent new password"); /* * Now see what the server has to say about it. * (If it's CHANGEREQ again, it's not happy with the * new password.) |
︙ | ︙ | |||
8839 8840 8841 8842 8843 8844 8845 | */ s->gotit = TRUE; /* * We don't need the old password any more, in any * case. Burn the evidence. */ | | | 9823 9824 9825 9826 9827 9828 9829 9830 9831 9832 9833 9834 9835 9836 9837 | */ s->gotit = TRUE; /* * We don't need the old password any more, in any * case. Burn the evidence. */ smemclr(s->password, strlen(s->password)); sfree(s->password); } else { char *str = dupprintf("No supported authentication methods available" " (server sent: %.*s)", methlen, methods); |
︙ | ︙ | |||
8869 8870 8871 8872 8873 8874 8875 | if (s->publickey_blob) { sfree(s->publickey_blob); sfree(s->publickey_comment); } if (s->agent_response) sfree(s->agent_response); | | < < < < | | < < < < | < | < < < < < < < | < < > | | > > > > | > | > | > | | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | < > > > > > > > > > > | | < | < < < | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | < < < | < < < < < < < < < < < < < < < < < < < < < < | | > > | < < < < | | < < < < | | < < < < > | < < < < < < < | | < | < < < < < < < < > | < | > > | | < | | < < | < < < < < < | < | < < < < < < < < | < < < < < < < < < < | < | < < | < < < < < < < < < < < | < < | < < < < < < < < < < < | | | | | | | | | | | | | | < | < < | | | | | | | | | | | | | | | | | | | | | | | | | | | > | | | | | | | | | | > > > < < < < < < < | 9853 9854 9855 9856 9857 9858 9859 9860 9861 9862 9863 9864 9865 9866 9867 9868 9869 9870 9871 9872 9873 9874 9875 9876 9877 9878 9879 9880 9881 9882 9883 9884 9885 9886 9887 9888 9889 9890 9891 9892 9893 9894 9895 9896 9897 9898 9899 9900 9901 9902 9903 9904 9905 9906 9907 9908 9909 9910 9911 9912 9913 9914 9915 9916 9917 9918 9919 9920 9921 9922 9923 9924 9925 9926 9927 9928 9929 9930 9931 9932 9933 9934 9935 9936 9937 9938 9939 9940 9941 9942 9943 9944 9945 9946 9947 9948 9949 9950 9951 9952 9953 9954 9955 9956 9957 9958 9959 9960 9961 9962 9963 9964 9965 9966 9967 9968 9969 9970 9971 9972 9973 9974 9975 9976 9977 9978 9979 9980 9981 9982 9983 9984 9985 9986 9987 9988 9989 9990 9991 9992 9993 9994 9995 9996 9997 9998 9999 10000 10001 10002 10003 10004 10005 10006 10007 10008 10009 10010 10011 10012 10013 10014 10015 10016 10017 10018 10019 10020 10021 10022 10023 10024 10025 10026 10027 10028 10029 10030 10031 10032 10033 10034 10035 10036 10037 10038 10039 10040 10041 10042 10043 10044 10045 10046 10047 10048 10049 10050 10051 10052 10053 10054 10055 10056 10057 10058 10059 10060 10061 10062 10063 10064 10065 10066 10067 10068 10069 10070 10071 10072 10073 10074 10075 10076 10077 10078 10079 10080 10081 10082 10083 10084 10085 10086 10087 | if (s->publickey_blob) { sfree(s->publickey_blob); sfree(s->publickey_comment); } if (s->agent_response) sfree(s->agent_response); if (s->userauth_success && !ssh->bare_connection) { /* * We've just received USERAUTH_SUCCESS, and we haven't sent any * packets since. Signal the transport layer to consider enacting * delayed compression. * * (Relying on we_are_in is not sufficient, as * draft-miller-secsh-compression-delayed is quite clear that it * triggers on USERAUTH_SUCCESS specifically, and we_are_in can * become set for other reasons.) */ do_ssh2_transport(ssh, "enabling delayed compression", -2, NULL); } ssh->channels = newtree234(ssh_channelcmp); /* * Set up handlers for some connection protocol messages, so we * don't have to handle them repeatedly in this coroutine. */ ssh->packet_dispatch[SSH2_MSG_CHANNEL_WINDOW_ADJUST] = ssh2_msg_channel_window_adjust; ssh->packet_dispatch[SSH2_MSG_GLOBAL_REQUEST] = ssh2_msg_global_request; /* * Create the main session channel. */ if (conf_get_int(ssh->conf, CONF_ssh_no_shell)) { ssh->mainchan = NULL; } else { ssh->mainchan = snew(struct ssh_channel); ssh->mainchan->ssh = ssh; ssh2_channel_init(ssh->mainchan); if (*conf_get_str(ssh->conf, CONF_ssh_nc_host)) { /* * Just start a direct-tcpip channel and use it as the main * channel. */ ssh_send_port_open(ssh->mainchan, conf_get_str(ssh->conf, CONF_ssh_nc_host), conf_get_int(ssh->conf, CONF_ssh_nc_port), "main channel"); ssh->ncmode = TRUE; } else { s->pktout = ssh2_chanopen_init(ssh->mainchan, "session"); logevent("Opening session as main channel"); ssh2_pkt_send(ssh, s->pktout); ssh->ncmode = FALSE; } crWaitUntilV(pktin); if (pktin->type != SSH2_MSG_CHANNEL_OPEN_CONFIRMATION) { bombout(("Server refused to open channel")); crStopV; /* FIXME: error data comes back in FAILURE packet */ } if (ssh_pkt_getuint32(pktin) != ssh->mainchan->localid) { bombout(("Server's channel confirmation cited wrong channel")); crStopV; } ssh->mainchan->remoteid = ssh_pkt_getuint32(pktin); ssh->mainchan->halfopen = FALSE; ssh->mainchan->type = CHAN_MAINSESSION; ssh->mainchan->v.v2.remwindow = ssh_pkt_getuint32(pktin); ssh->mainchan->v.v2.remmaxpkt = ssh_pkt_getuint32(pktin); add234(ssh->channels, ssh->mainchan); update_specials_menu(ssh->frontend); logevent("Opened main channel"); } /* * Now we have a channel, make dispatch table entries for * general channel-based messages. */ ssh->packet_dispatch[SSH2_MSG_CHANNEL_DATA] = ssh->packet_dispatch[SSH2_MSG_CHANNEL_EXTENDED_DATA] = ssh2_msg_channel_data; ssh->packet_dispatch[SSH2_MSG_CHANNEL_EOF] = ssh2_msg_channel_eof; ssh->packet_dispatch[SSH2_MSG_CHANNEL_CLOSE] = ssh2_msg_channel_close; ssh->packet_dispatch[SSH2_MSG_CHANNEL_OPEN_CONFIRMATION] = ssh2_msg_channel_open_confirmation; ssh->packet_dispatch[SSH2_MSG_CHANNEL_OPEN_FAILURE] = ssh2_msg_channel_open_failure; ssh->packet_dispatch[SSH2_MSG_CHANNEL_REQUEST] = ssh2_msg_channel_request; ssh->packet_dispatch[SSH2_MSG_CHANNEL_OPEN] = ssh2_msg_channel_open; ssh->packet_dispatch[SSH2_MSG_CHANNEL_SUCCESS] = ssh2_msg_channel_response; ssh->packet_dispatch[SSH2_MSG_CHANNEL_FAILURE] = ssh2_msg_channel_response; /* * Now the connection protocol is properly up and running, with * all those dispatch table entries, so it's safe to let * downstreams start trying to open extra channels through us. */ if (ssh->connshare) share_activate(ssh->connshare, ssh->v_s); if (ssh->mainchan && ssh_is_simple(ssh)) { /* * 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_chanreq_init(ssh->mainchan, "simple@putty.projects.tartarus.org", NULL, NULL); ssh2_pkt_send(ssh, s->pktout); } /* * Enable port forwardings. */ ssh_setup_portfwd(ssh, ssh->conf); if (ssh->mainchan && !ssh->ncmode) { /* * Send the CHANNEL_REQUESTS for the main session channel. * Each one is handled by its own little asynchronous * co-routine. */ /* Potentially enable X11 forwarding. */ if (conf_get_int(ssh->conf, CONF_x11_forward)) { ssh->x11disp = x11_setup_display(conf_get_str(ssh->conf, CONF_x11_display), ssh->conf); if (!ssh->x11disp) { /* FIXME: return an error message from x11_setup_display */ logevent("X11 forwarding not enabled: unable to" " initialise X display"); } else { ssh->x11auth = x11_invent_fake_auth (ssh->x11authtree, conf_get_int(ssh->conf, CONF_x11_auth)); ssh->x11auth->disp = ssh->x11disp; ssh2_setup_x11(ssh->mainchan, NULL, NULL); } } /* Potentially enable agent forwarding. */ if (ssh_agent_forwarding_permitted(ssh)) ssh2_setup_agent(ssh->mainchan, NULL, NULL); /* Now allocate a pty for the session. */ if (!conf_get_int(ssh->conf, CONF_nopty)) ssh2_setup_pty(ssh->mainchan, NULL, NULL); /* Send environment variables. */ ssh2_setup_env(ssh->mainchan, NULL, NULL); /* * Start a shell or a remote command. We may have to attempt * this twice if the config data has provided a second choice * of command. */ while (1) { int subsys; char *cmd; if (ssh->fallback_cmd) { subsys = conf_get_int(ssh->conf, CONF_ssh_subsys2); cmd = conf_get_str(ssh->conf, CONF_remote_cmd2); } else { subsys = conf_get_int(ssh->conf, CONF_ssh_subsys); cmd = conf_get_str(ssh->conf, CONF_remote_cmd); } if (subsys) { s->pktout = ssh2_chanreq_init(ssh->mainchan, "subsystem", ssh2_response_authconn, NULL); ssh2_pkt_addstring(s->pktout, cmd); } else if (*cmd) { s->pktout = ssh2_chanreq_init(ssh->mainchan, "exec", ssh2_response_authconn, NULL); ssh2_pkt_addstring(s->pktout, cmd); } else { s->pktout = ssh2_chanreq_init(ssh->mainchan, "shell", ssh2_response_authconn, NULL); } ssh2_pkt_send(ssh, s->pktout); crWaitUntilV(pktin); if (pktin->type != SSH2_MSG_CHANNEL_SUCCESS) { if (pktin->type != SSH2_MSG_CHANNEL_FAILURE) { bombout(("Unexpected response to shell/command request:" " packet type %d", pktin->type)); crStopV; } /* * We failed to start the command. If this is the * fallback command, we really are finished; if it's * not, and if the fallback command exists, try falling * back to it before complaining. */ if (!ssh->fallback_cmd && *conf_get_str(ssh->conf, CONF_remote_cmd2)) { logevent("Primary command failed; attempting fallback"); ssh->fallback_cmd = TRUE; continue; } bombout(("Server refused to start a shell/command")); crStopV; } else { logevent("Started a shell/command"); } break; } } else { ssh->editing = ssh->echoing = TRUE; } ssh->state = SSH_STATE_SESSION; if (ssh->size_needed) ssh_size(ssh, ssh->term_width, ssh->term_height); if (ssh->eof_needed) ssh_special(ssh, TS_EOF); /* * Transfer data! */ if (ssh->ldisc) ldisc_send(ssh->ldisc, NULL, 0, 0);/* cause ldisc to notice changes */ if (ssh->mainchan) ssh->send_ok = 1; |
︙ | ︙ | |||
9356 9357 9358 9359 9360 9361 9362 9363 9364 9365 9366 9367 9368 9369 | /* XXX maybe we should actually take notice of the return value */ ssh2_pkt_getbool(pktin); ssh_pkt_getstring(pktin, &msg, &msglen); logeventf(ssh, "Remote debug message: %.*s", msglen, msg); } static void ssh2_msg_something_unimplemented(Ssh ssh, struct Packet *pktin) { struct Packet *pktout; pktout = ssh2_pkt_init(SSH2_MSG_UNIMPLEMENTED); ssh2_pkt_adduint32(pktout, pktin->sequence); /* | > > > > > > > > > > > > > > > > > > > | 10159 10160 10161 10162 10163 10164 10165 10166 10167 10168 10169 10170 10171 10172 10173 10174 10175 10176 10177 10178 10179 10180 10181 10182 10183 10184 10185 10186 10187 10188 10189 10190 10191 | /* XXX maybe we should actually take notice of the return value */ ssh2_pkt_getbool(pktin); ssh_pkt_getstring(pktin, &msg, &msglen); logeventf(ssh, "Remote debug message: %.*s", msglen, msg); } static void ssh2_msg_transport(Ssh ssh, struct Packet *pktin) { do_ssh2_transport(ssh, NULL, 0, pktin); } /* * Called if we receive a packet that isn't allowed by the protocol. * This only applies to packets whose meaning PuTTY understands. * Entirely unknown packets are handled below. */ static void ssh2_msg_unexpected(Ssh ssh, struct Packet *pktin) { char *buf = dupprintf("Server protocol violation: unexpected %s packet", ssh2_pkt_type(ssh->pkt_kctx, ssh->pkt_actx, pktin->type)); ssh_disconnect(ssh, NULL, buf, SSH2_DISCONNECT_PROTOCOL_ERROR, FALSE); sfree(buf); } static void ssh2_msg_something_unimplemented(Ssh ssh, struct Packet *pktin) { struct Packet *pktout; pktout = ssh2_pkt_init(SSH2_MSG_UNIMPLEMENTED); ssh2_pkt_adduint32(pktout, pktin->sequence); /* |
︙ | ︙ | |||
9383 9384 9385 9386 9387 9388 9389 | /* * Most messages cause SSH2_MSG_UNIMPLEMENTED. */ for (i = 0; i < 256; i++) ssh->packet_dispatch[i] = ssh2_msg_something_unimplemented; /* | | | > > | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | > | | > > | > | | < | > > | < < < < | | > > | | | > > > > < | | | > > > > | < < < > > > > > > | | | > > | 10205 10206 10207 10208 10209 10210 10211 10212 10213 10214 10215 10216 10217 10218 10219 10220 10221 10222 10223 10224 10225 10226 10227 10228 10229 10230 10231 10232 10233 10234 10235 10236 10237 10238 10239 10240 10241 10242 10243 10244 10245 10246 10247 10248 10249 10250 10251 10252 10253 10254 10255 10256 10257 10258 10259 10260 10261 10262 10263 10264 10265 10266 10267 10268 10269 10270 10271 10272 10273 10274 10275 10276 10277 10278 10279 10280 10281 10282 10283 10284 10285 10286 10287 10288 10289 10290 10291 10292 10293 10294 10295 10296 10297 10298 10299 10300 10301 10302 10303 10304 10305 10306 10307 10308 10309 10310 10311 10312 10313 10314 10315 10316 10317 10318 10319 10320 10321 10322 10323 10324 10325 10326 10327 10328 10329 10330 10331 10332 10333 10334 10335 10336 10337 10338 10339 10340 10341 10342 10343 10344 10345 10346 10347 10348 10349 10350 10351 10352 10353 10354 10355 10356 10357 10358 10359 10360 10361 10362 10363 10364 10365 10366 10367 10368 10369 10370 10371 10372 10373 10374 10375 10376 10377 10378 10379 10380 10381 10382 10383 10384 10385 10386 10387 10388 10389 10390 10391 10392 10393 10394 10395 10396 10397 10398 10399 10400 10401 10402 10403 10404 10405 10406 10407 10408 10409 10410 10411 10412 10413 10414 10415 10416 10417 10418 10419 10420 10421 10422 10423 10424 10425 10426 10427 10428 10429 10430 10431 10432 10433 10434 10435 10436 10437 10438 10439 10440 10441 10442 10443 10444 10445 10446 10447 10448 10449 10450 10451 10452 10453 10454 10455 10456 10457 10458 10459 10460 10461 10462 10463 10464 10465 10466 10467 10468 10469 10470 10471 10472 10473 10474 10475 10476 10477 10478 10479 10480 10481 10482 10483 10484 10485 10486 10487 10488 10489 10490 10491 10492 10493 10494 10495 10496 10497 10498 | /* * Most messages cause SSH2_MSG_UNIMPLEMENTED. */ for (i = 0; i < 256; i++) ssh->packet_dispatch[i] = ssh2_msg_something_unimplemented; /* * Initially, we only accept transport messages (and a few generic * ones). do_ssh2_authconn will add more when it starts. * Messages that are understood but not currently acceptable go to * ssh2_msg_unexpected. */ ssh->packet_dispatch[SSH2_MSG_UNIMPLEMENTED] = ssh2_msg_unexpected; ssh->packet_dispatch[SSH2_MSG_SERVICE_REQUEST] = ssh2_msg_unexpected; ssh->packet_dispatch[SSH2_MSG_SERVICE_ACCEPT] = ssh2_msg_unexpected; ssh->packet_dispatch[SSH2_MSG_KEXINIT] = ssh2_msg_transport; ssh->packet_dispatch[SSH2_MSG_NEWKEYS] = ssh2_msg_transport; ssh->packet_dispatch[SSH2_MSG_KEXDH_INIT] = ssh2_msg_transport; ssh->packet_dispatch[SSH2_MSG_KEXDH_REPLY] = ssh2_msg_transport; /* ssh->packet_dispatch[SSH2_MSG_KEX_DH_GEX_REQUEST] = ssh2_msg_transport; duplicate case value */ /* ssh->packet_dispatch[SSH2_MSG_KEX_DH_GEX_GROUP] = ssh2_msg_transport; duplicate case value */ ssh->packet_dispatch[SSH2_MSG_KEX_DH_GEX_INIT] = ssh2_msg_transport; ssh->packet_dispatch[SSH2_MSG_KEX_DH_GEX_REPLY] = ssh2_msg_transport; ssh->packet_dispatch[SSH2_MSG_USERAUTH_REQUEST] = ssh2_msg_unexpected; ssh->packet_dispatch[SSH2_MSG_USERAUTH_FAILURE] = ssh2_msg_unexpected; ssh->packet_dispatch[SSH2_MSG_USERAUTH_SUCCESS] = ssh2_msg_unexpected; ssh->packet_dispatch[SSH2_MSG_USERAUTH_BANNER] = ssh2_msg_unexpected; ssh->packet_dispatch[SSH2_MSG_USERAUTH_PK_OK] = ssh2_msg_unexpected; /* ssh->packet_dispatch[SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ] = ssh2_msg_unexpected; duplicate case value */ /* ssh->packet_dispatch[SSH2_MSG_USERAUTH_INFO_REQUEST] = ssh2_msg_unexpected; duplicate case value */ ssh->packet_dispatch[SSH2_MSG_USERAUTH_INFO_RESPONSE] = ssh2_msg_unexpected; ssh->packet_dispatch[SSH2_MSG_GLOBAL_REQUEST] = ssh2_msg_unexpected; ssh->packet_dispatch[SSH2_MSG_REQUEST_SUCCESS] = ssh2_msg_unexpected; ssh->packet_dispatch[SSH2_MSG_REQUEST_FAILURE] = ssh2_msg_unexpected; ssh->packet_dispatch[SSH2_MSG_CHANNEL_OPEN] = ssh2_msg_unexpected; ssh->packet_dispatch[SSH2_MSG_CHANNEL_OPEN_CONFIRMATION] = ssh2_msg_unexpected; ssh->packet_dispatch[SSH2_MSG_CHANNEL_OPEN_FAILURE] = ssh2_msg_unexpected; ssh->packet_dispatch[SSH2_MSG_CHANNEL_WINDOW_ADJUST] = ssh2_msg_unexpected; ssh->packet_dispatch[SSH2_MSG_CHANNEL_DATA] = ssh2_msg_unexpected; ssh->packet_dispatch[SSH2_MSG_CHANNEL_EXTENDED_DATA] = ssh2_msg_unexpected; ssh->packet_dispatch[SSH2_MSG_CHANNEL_EOF] = ssh2_msg_unexpected; ssh->packet_dispatch[SSH2_MSG_CHANNEL_CLOSE] = ssh2_msg_unexpected; ssh->packet_dispatch[SSH2_MSG_CHANNEL_REQUEST] = ssh2_msg_unexpected; ssh->packet_dispatch[SSH2_MSG_CHANNEL_SUCCESS] = ssh2_msg_unexpected; ssh->packet_dispatch[SSH2_MSG_CHANNEL_FAILURE] = ssh2_msg_unexpected; /* * These messages have a special handler from the start. */ ssh->packet_dispatch[SSH2_MSG_DISCONNECT] = ssh2_msg_disconnect; ssh->packet_dispatch[SSH2_MSG_IGNORE] = ssh_msg_ignore; /* shared with SSH-1 */ ssh->packet_dispatch[SSH2_MSG_DEBUG] = ssh2_msg_debug; } static void ssh2_bare_connection_protocol_setup(Ssh ssh) { int i; /* * Most messages cause SSH2_MSG_UNIMPLEMENTED. */ for (i = 0; i < 256; i++) ssh->packet_dispatch[i] = ssh2_msg_something_unimplemented; /* * Initially, we set all ssh-connection messages to 'unexpected'; * do_ssh2_authconn will fill things in properly. We also handle a * couple of messages from the transport protocol which aren't * related to key exchange (UNIMPLEMENTED, IGNORE, DEBUG, * DISCONNECT). */ ssh->packet_dispatch[SSH2_MSG_GLOBAL_REQUEST] = ssh2_msg_unexpected; ssh->packet_dispatch[SSH2_MSG_REQUEST_SUCCESS] = ssh2_msg_unexpected; ssh->packet_dispatch[SSH2_MSG_REQUEST_FAILURE] = ssh2_msg_unexpected; ssh->packet_dispatch[SSH2_MSG_CHANNEL_OPEN] = ssh2_msg_unexpected; ssh->packet_dispatch[SSH2_MSG_CHANNEL_OPEN_CONFIRMATION] = ssh2_msg_unexpected; ssh->packet_dispatch[SSH2_MSG_CHANNEL_OPEN_FAILURE] = ssh2_msg_unexpected; ssh->packet_dispatch[SSH2_MSG_CHANNEL_WINDOW_ADJUST] = ssh2_msg_unexpected; ssh->packet_dispatch[SSH2_MSG_CHANNEL_DATA] = ssh2_msg_unexpected; ssh->packet_dispatch[SSH2_MSG_CHANNEL_EXTENDED_DATA] = ssh2_msg_unexpected; ssh->packet_dispatch[SSH2_MSG_CHANNEL_EOF] = ssh2_msg_unexpected; ssh->packet_dispatch[SSH2_MSG_CHANNEL_CLOSE] = ssh2_msg_unexpected; ssh->packet_dispatch[SSH2_MSG_CHANNEL_REQUEST] = ssh2_msg_unexpected; ssh->packet_dispatch[SSH2_MSG_CHANNEL_SUCCESS] = ssh2_msg_unexpected; ssh->packet_dispatch[SSH2_MSG_CHANNEL_FAILURE] = ssh2_msg_unexpected; ssh->packet_dispatch[SSH2_MSG_UNIMPLEMENTED] = ssh2_msg_unexpected; /* * These messages have a special handler from the start. */ ssh->packet_dispatch[SSH2_MSG_DISCONNECT] = ssh2_msg_disconnect; ssh->packet_dispatch[SSH2_MSG_IGNORE] = ssh_msg_ignore; ssh->packet_dispatch[SSH2_MSG_DEBUG] = ssh2_msg_debug; } static void ssh2_timer(void *ctx, unsigned long now) { Ssh ssh = (Ssh)ctx; if (ssh->state == SSH_STATE_CLOSED) return; if (!ssh->kex_in_progress && !ssh->bare_connection && conf_get_int(ssh->conf, CONF_ssh_rekey_time) != 0 && now == ssh->next_rekey) { do_ssh2_transport(ssh, "timeout", -1, NULL); } } static void ssh2_protocol(Ssh ssh, void *vin, int inlen, struct Packet *pktin) { unsigned char *in = (unsigned char *)vin; if (ssh->state == SSH_STATE_CLOSED) return; if (pktin) { ssh->incoming_data_size += pktin->encrypted_len; if (!ssh->kex_in_progress && ssh->max_data_size != 0 && ssh->incoming_data_size > ssh->max_data_size) do_ssh2_transport(ssh, "too much data received", -1, NULL); } if (pktin) ssh->packet_dispatch[pktin->type](ssh, pktin); else if (!ssh->protocol_initial_phase_done) do_ssh2_transport(ssh, in, inlen, pktin); else do_ssh2_authconn(ssh, in, inlen, pktin); } static void ssh2_bare_connection_protocol(Ssh ssh, void *vin, int inlen, struct Packet *pktin) { unsigned char *in = (unsigned char *)vin; if (ssh->state == SSH_STATE_CLOSED) return; if (pktin) ssh->packet_dispatch[pktin->type](ssh, pktin); else do_ssh2_authconn(ssh, in, inlen, pktin); } static void ssh_cache_conf_values(Ssh ssh) { ssh->logomitdata = conf_get_int(ssh->conf, CONF_logomitdata); } /* * Called to set up the connection. * * Returns an error message, or NULL on success. */ static const char *ssh_init(void *frontend_handle, void **backend_handle, Conf *conf, char *host, int port, char **realhost, int nodelay, int keepalive) { const char *p; Ssh ssh; ssh = snew(struct ssh_tag); ssh->conf = conf_copy(conf); ssh_cache_conf_values(ssh); ssh->version = 0; /* when not ready yet */ ssh->s = NULL; ssh->cipher = NULL; ssh->v1_cipher_ctx = NULL; ssh->crcda_ctx = NULL; ssh->cscipher = NULL; ssh->cs_cipher_ctx = NULL; ssh->sccipher = NULL; ssh->sc_cipher_ctx = NULL; ssh->csmac = NULL; ssh->cs_mac_ctx = NULL; ssh->scmac = NULL; ssh->sc_mac_ctx = NULL; ssh->cscomp = NULL; ssh->cs_comp_ctx = NULL; ssh->sccomp = NULL; ssh->sc_comp_ctx = NULL; ssh->kex = NULL; ssh->kex_ctx = NULL; ssh->hostkey = NULL; ssh->hostkey_str = NULL; ssh->exitcode = -1; ssh->close_expected = FALSE; ssh->clean_exit = FALSE; ssh->state = SSH_STATE_PREPACKET; ssh->size_needed = FALSE; ssh->eof_needed = FALSE; ssh->ldisc = NULL; ssh->logctx = NULL; ssh->deferred_send_data = NULL; ssh->deferred_len = 0; ssh->deferred_size = 0; ssh->fallback_cmd = 0; ssh->pkt_kctx = SSH2_PKTCTX_NOKEX; ssh->pkt_actx = SSH2_PKTCTX_NOAUTH; ssh->x11disp = NULL; ssh->x11auth = NULL; ssh->x11authtree = newtree234(x11_authcmp); ssh->v1_compressing = FALSE; ssh->v2_outgoing_sequence = 0; ssh->ssh1_rdpkt_crstate = 0; ssh->ssh2_rdpkt_crstate = 0; ssh->ssh2_bare_rdpkt_crstate = 0; ssh->ssh_gotdata_crstate = 0; ssh->do_ssh1_connection_crstate = 0; ssh->do_ssh_init_state = NULL; ssh->do_ssh_connection_init_state = NULL; ssh->do_ssh1_login_state = NULL; ssh->do_ssh2_transport_state = NULL; ssh->do_ssh2_authconn_state = NULL; ssh->v_c = NULL; ssh->v_s = NULL; ssh->mainchan = NULL; ssh->throttled_all = 0; ssh->v1_stdout_throttling = 0; ssh->queue = NULL; ssh->queuelen = ssh->queuesize = 0; ssh->queueing = FALSE; ssh->qhead = ssh->qtail = NULL; ssh->deferred_rekey_reason = NULL; bufchain_init(&ssh->queued_incoming_data); ssh->frozen = FALSE; ssh->username = NULL; ssh->sent_console_eof = FALSE; ssh->got_pty = FALSE; ssh->bare_connection = FALSE; ssh->attempting_connshare = FALSE; *backend_handle = ssh; #ifdef MSCRYPTOAPI if (crypto_startup() == 0) return "Microsoft high encryption pack not installed!"; #endif ssh->frontend = frontend_handle; ssh->term_width = conf_get_int(ssh->conf, CONF_width); ssh->term_height = conf_get_int(ssh->conf, CONF_height); ssh->channels = NULL; ssh->rportfwds = NULL; ssh->portfwds = NULL; ssh->send_ok = 0; ssh->editing = 0; ssh->echoing = 0; ssh->conn_throttle_count = 0; ssh->overall_bufsize = 0; ssh->fallback_cmd = 0; ssh->protocol = NULL; ssh->protocol_initial_phase_done = FALSE; ssh->pinger = NULL; ssh->incoming_data_size = ssh->outgoing_data_size = ssh->deferred_data_size = 0L; ssh->max_data_size = parse_blocksize(conf_get_str(ssh->conf, CONF_ssh_rekey_data)); ssh->kex_in_progress = FALSE; #ifndef NO_GSSAPI ssh->gsslibs = NULL; #endif p = connect_to_host(ssh, host, port, realhost, nodelay, keepalive); if (p != NULL) return p; random_ref(); return NULL; } static void ssh_free(void *handle) { Ssh ssh = (Ssh) handle; struct ssh_channel *c; struct ssh_rportfwd *pf; struct X11FakeAuth *auth; if (ssh->v1_cipher_ctx) ssh->cipher->free_context(ssh->v1_cipher_ctx); if (ssh->cs_cipher_ctx) ssh->cscipher->free_context(ssh->cs_cipher_ctx); if (ssh->sc_cipher_ctx) ssh->sccipher->free_context(ssh->sc_cipher_ctx); |
︙ | ︙ | |||
9638 9639 9640 9641 9642 9643 9644 | while (ssh->queuelen-- > 0) ssh_free_packet(ssh->queue[ssh->queuelen]); sfree(ssh->queue); while (ssh->qhead) { struct queued_handler *qh = ssh->qhead; ssh->qhead = qh->next; | | | | | | > > > > > > > > > > > > > > > > > > > > | > | | | > | | | | | > > | > | | | > > > > | > > | | 10519 10520 10521 10522 10523 10524 10525 10526 10527 10528 10529 10530 10531 10532 10533 10534 10535 10536 10537 10538 10539 10540 10541 10542 10543 10544 10545 10546 10547 10548 10549 10550 10551 10552 10553 10554 10555 10556 10557 10558 10559 10560 10561 10562 10563 10564 10565 10566 10567 10568 10569 10570 10571 10572 10573 10574 10575 10576 10577 10578 10579 10580 10581 10582 10583 10584 10585 10586 10587 10588 10589 10590 10591 10592 10593 10594 10595 10596 10597 10598 10599 10600 10601 10602 10603 10604 10605 10606 10607 10608 10609 10610 10611 10612 10613 10614 10615 10616 10617 10618 10619 10620 10621 10622 10623 10624 10625 10626 10627 10628 10629 10630 10631 10632 10633 10634 10635 10636 10637 10638 10639 10640 10641 10642 10643 10644 10645 10646 10647 10648 10649 10650 10651 10652 10653 10654 10655 10656 10657 10658 10659 10660 10661 10662 10663 10664 10665 10666 10667 10668 10669 10670 | while (ssh->queuelen-- > 0) ssh_free_packet(ssh->queue[ssh->queuelen]); sfree(ssh->queue); while (ssh->qhead) { struct queued_handler *qh = ssh->qhead; ssh->qhead = qh->next; sfree(qh); } ssh->qhead = ssh->qtail = NULL; if (ssh->channels) { while ((c = delpos234(ssh->channels, 0)) != NULL) { switch (c->type) { case CHAN_X11: if (c->u.x11.xconn != NULL) x11_close(c->u.x11.xconn); break; case CHAN_SOCKDATA: case CHAN_SOCKDATA_DORMANT: if (c->u.pfd.pf != NULL) pfd_close(c->u.pfd.pf); break; } if (ssh->version == 2) { struct outstanding_channel_request *ocr, *nocr; ocr = c->v.v2.chanreq_head; while (ocr) { ocr->handler(c, NULL, ocr->ctx); nocr = ocr->next; sfree(ocr); ocr = nocr; } bufchain_clear(&c->v.v2.outbuffer); } sfree(c); } freetree234(ssh->channels); ssh->channels = NULL; } if (ssh->connshare) sharestate_free(ssh->connshare); if (ssh->rportfwds) { while ((pf = delpos234(ssh->rportfwds, 0)) != NULL) free_rportfwd(pf); freetree234(ssh->rportfwds); ssh->rportfwds = NULL; } sfree(ssh->deferred_send_data); if (ssh->x11disp) x11_free_display(ssh->x11disp); while ((auth = delpos234(ssh->x11authtree, 0)) != NULL) x11_free_fake_auth(auth); freetree234(ssh->x11authtree); sfree(ssh->do_ssh_init_state); sfree(ssh->do_ssh1_login_state); sfree(ssh->do_ssh2_transport_state); sfree(ssh->do_ssh2_authconn_state); sfree(ssh->v_c); sfree(ssh->v_s); sfree(ssh->fullhostname); sfree(ssh->hostkey_str); if (ssh->crcda_ctx) { crcda_free_context(ssh->crcda_ctx); ssh->crcda_ctx = NULL; } if (ssh->s) ssh_do_close(ssh, TRUE); expire_timer_context(ssh); if (ssh->pinger) pinger_free(ssh->pinger); bufchain_clear(&ssh->queued_incoming_data); sfree(ssh->username); conf_free(ssh->conf); #ifndef NO_GSSAPI if (ssh->gsslibs) ssh_gss_cleanup(ssh->gsslibs); #endif sfree(ssh); random_unref(); } /* * Reconfigure the SSH backend. */ static void ssh_reconfig(void *handle, Conf *conf) { Ssh ssh = (Ssh) handle; char *rekeying = NULL, rekey_mandatory = FALSE; unsigned long old_max_data_size; int i, rekey_time; pinger_reconfig(ssh->pinger, ssh->conf, conf); if (ssh->portfwds) ssh_setup_portfwd(ssh, conf); rekey_time = conf_get_int(conf, CONF_ssh_rekey_time); if (conf_get_int(ssh->conf, CONF_ssh_rekey_time) != rekey_time && rekey_time != 0) { unsigned long new_next = ssh->last_rekey + rekey_time*60*TICKSPERSEC; unsigned long now = GETTICKCOUNT(); if (now - ssh->last_rekey > rekey_time*60*TICKSPERSEC) { rekeying = "timeout shortened"; } else { ssh->next_rekey = schedule_timer(new_next - now, ssh2_timer, ssh); } } old_max_data_size = ssh->max_data_size; ssh->max_data_size = parse_blocksize(conf_get_str(ssh->conf, CONF_ssh_rekey_data)); if (old_max_data_size != ssh->max_data_size && ssh->max_data_size != 0) { if (ssh->outgoing_data_size > ssh->max_data_size || ssh->incoming_data_size > ssh->max_data_size) rekeying = "data limit lowered"; } if (conf_get_int(ssh->conf, CONF_compression) != conf_get_int(conf, CONF_compression)) { rekeying = "compression setting changed"; rekey_mandatory = TRUE; } for (i = 0; i < CIPHER_MAX; i++) if (conf_get_int_int(ssh->conf, CONF_ssh_cipherlist, i) != conf_get_int_int(conf, CONF_ssh_cipherlist, i)) { rekeying = "cipher settings changed"; rekey_mandatory = TRUE; } if (conf_get_int(ssh->conf, CONF_ssh2_des_cbc) != conf_get_int(conf, CONF_ssh2_des_cbc)) { rekeying = "cipher settings changed"; rekey_mandatory = TRUE; } conf_free(ssh->conf); ssh->conf = conf_copy(conf); ssh_cache_conf_values(ssh); if (!ssh->bare_connection && rekeying) { if (!ssh->kex_in_progress) { do_ssh2_transport(ssh, rekeying, -1, NULL); } else if (rekey_mandatory) { ssh->deferred_rekey_reason = rekeying; } } } |
︙ | ︙ | |||
9790 9791 9792 9793 9794 9795 9796 | override_value = 0; if (ssh->throttled_all) override_value = ssh->overall_bufsize; if (ssh->version == 1) { return override_value; } else if (ssh->version == 2) { | | | 10702 10703 10704 10705 10706 10707 10708 10709 10710 10711 10712 10713 10714 10715 10716 | override_value = 0; if (ssh->throttled_all) override_value = ssh->overall_bufsize; if (ssh->version == 1) { return override_value; } else if (ssh->version == 2) { if (!ssh->mainchan) return override_value; else return (override_value + bufchain_size(&ssh->mainchan->v.v2.outbuffer)); } return 0; |
︙ | ︙ | |||
9820 9821 9822 9823 9824 9825 9826 | case SSH_STATE_PREPACKET: case SSH_STATE_CLOSED: break; /* do nothing */ case SSH_STATE_INTERMED: ssh->size_needed = TRUE; /* buffer for later */ break; case SSH_STATE_SESSION: | | < < | | | 10732 10733 10734 10735 10736 10737 10738 10739 10740 10741 10742 10743 10744 10745 10746 10747 10748 10749 10750 10751 10752 10753 10754 | case SSH_STATE_PREPACKET: case SSH_STATE_CLOSED: break; /* do nothing */ case SSH_STATE_INTERMED: ssh->size_needed = TRUE; /* buffer for later */ break; case SSH_STATE_SESSION: if (!conf_get_int(ssh->conf, CONF_nopty)) { if (ssh->version == 1) { send_packet(ssh, SSH1_CMSG_WINDOW_SIZE, PKT_INT, ssh->term_height, PKT_INT, ssh->term_width, PKT_INT, 0, PKT_INT, 0, PKT_END); } else if (ssh->mainchan) { pktout = ssh2_chanreq_init(ssh->mainchan, "window-change", NULL, NULL); ssh2_pkt_adduint32(pktout, ssh->term_width); ssh2_pkt_adduint32(pktout, ssh->term_height); ssh2_pkt_adduint32(pktout, 0); ssh2_pkt_adduint32(pktout, 0); ssh2_pkt_send(ssh, pktout); } } |
︙ | ︙ | |||
9901 9902 9903 9904 9905 9906 9907 | * won't cope with it, since we wouldn't bother sending it if * asked anyway. */ if (!(ssh->remote_bugs & BUG_CHOKES_ON_SSH1_IGNORE)) ADD_SPECIALS(ssh1_ignore_special); } else if (ssh->version == 2) { if (!(ssh->remote_bugs & BUG_CHOKES_ON_SSH2_IGNORE)) ADD_SPECIALS(ssh2_ignore_special); | | | 10811 10812 10813 10814 10815 10816 10817 10818 10819 10820 10821 10822 10823 10824 10825 | * won't cope with it, since we wouldn't bother sending it if * asked anyway. */ if (!(ssh->remote_bugs & BUG_CHOKES_ON_SSH1_IGNORE)) ADD_SPECIALS(ssh1_ignore_special); } else if (ssh->version == 2) { if (!(ssh->remote_bugs & BUG_CHOKES_ON_SSH2_IGNORE)) ADD_SPECIALS(ssh2_ignore_special); if (!(ssh->remote_bugs & BUG_SSH2_REKEY) && !ssh->bare_connection) ADD_SPECIALS(ssh2_rekey_special); if (ssh->mainchan) ADD_SPECIALS(ssh2_session_specials); } /* else we're not ready yet */ if (i) { ADD_SPECIALS(specials_end); |
︙ | ︙ | |||
9939 9940 9941 9942 9943 9944 9945 | if (code == TS_EOF) ssh->eof_needed = TRUE; return; } if (ssh->version == 1) { send_packet(ssh, SSH1_CMSG_EOF, PKT_END); } else if (ssh->mainchan) { | < | < | > | < < < | 10849 10850 10851 10852 10853 10854 10855 10856 10857 10858 10859 10860 10861 10862 10863 10864 10865 10866 10867 10868 10869 10870 10871 10872 10873 10874 10875 10876 10877 10878 10879 10880 10881 10882 10883 10884 10885 10886 10887 10888 10889 10890 10891 | if (code == TS_EOF) ssh->eof_needed = TRUE; return; } if (ssh->version == 1) { send_packet(ssh, SSH1_CMSG_EOF, PKT_END); } else if (ssh->mainchan) { sshfwd_write_eof(ssh->mainchan); ssh->send_ok = 0; /* now stop trying to read from stdin */ } logevent("Sent EOF message"); } else if (code == TS_PING || code == TS_NOP) { if (ssh->state == SSH_STATE_CLOSED || ssh->state == SSH_STATE_PREPACKET) return; if (ssh->version == 1) { if (!(ssh->remote_bugs & BUG_CHOKES_ON_SSH1_IGNORE)) send_packet(ssh, SSH1_MSG_IGNORE, PKT_STR, "", PKT_END); } else { if (!(ssh->remote_bugs & BUG_CHOKES_ON_SSH2_IGNORE)) { pktout = ssh2_pkt_init(SSH2_MSG_IGNORE); ssh2_pkt_addstring_start(pktout); ssh2_pkt_send_noqueue(ssh, pktout); } } } else if (code == TS_REKEY) { if (!ssh->kex_in_progress && !ssh->bare_connection && ssh->version == 2) { do_ssh2_transport(ssh, "at user request", -1, NULL); } } else if (code == TS_BRK) { if (ssh->state == SSH_STATE_CLOSED || ssh->state == SSH_STATE_PREPACKET) return; if (ssh->version == 1) { logevent("Unable to send BREAK signal in SSH-1"); } else if (ssh->mainchan) { pktout = ssh2_chanreq_init(ssh->mainchan, "break", NULL, NULL); ssh2_pkt_adduint32(pktout, 0); /* default break length */ ssh2_pkt_send(ssh, pktout); } } else { /* Is is a POSIX signal? */ char *signame = NULL; if (code == TS_SIGABRT) signame = "ABRT"; |
︙ | ︙ | |||
9996 9997 9998 9999 10000 10001 10002 | if (code == TS_SIGUSR1) signame = "USR1"; if (code == TS_SIGUSR2) signame = "USR2"; /* The SSH-2 protocol does in principle support arbitrary named * signals, including signame@domain, but we don't support those. */ if (signame) { /* It's a signal. */ if (ssh->version == 2 && ssh->mainchan) { | | < < < | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > | < | < < < | 10902 10903 10904 10905 10906 10907 10908 10909 10910 10911 10912 10913 10914 10915 10916 10917 10918 10919 10920 10921 10922 10923 10924 10925 10926 10927 10928 10929 10930 10931 10932 10933 10934 10935 10936 10937 10938 10939 10940 10941 10942 10943 10944 10945 10946 10947 10948 10949 10950 10951 10952 10953 10954 10955 10956 10957 10958 10959 10960 10961 10962 10963 10964 10965 10966 10967 10968 10969 10970 10971 10972 10973 10974 10975 10976 10977 10978 10979 10980 10981 10982 10983 10984 10985 10986 10987 10988 10989 10990 10991 10992 10993 10994 10995 10996 10997 10998 10999 11000 11001 11002 11003 11004 11005 11006 11007 11008 11009 11010 11011 11012 11013 11014 11015 11016 11017 11018 11019 11020 11021 11022 11023 11024 11025 11026 11027 11028 11029 11030 | if (code == TS_SIGUSR1) signame = "USR1"; if (code == TS_SIGUSR2) signame = "USR2"; /* The SSH-2 protocol does in principle support arbitrary named * signals, including signame@domain, but we don't support those. */ if (signame) { /* It's a signal. */ if (ssh->version == 2 && ssh->mainchan) { pktout = ssh2_chanreq_init(ssh->mainchan, "signal", NULL, NULL); ssh2_pkt_addstring(pktout, signame); ssh2_pkt_send(ssh, pktout); logeventf(ssh, "Sent signal SIG%s", signame); } } else { /* Never heard of it. Do nothing */ } } } void *new_sock_channel(void *handle, struct PortForwarding *pf) { Ssh ssh = (Ssh) handle; struct ssh_channel *c; c = snew(struct ssh_channel); c->ssh = ssh; ssh2_channel_init(c); c->halfopen = TRUE; c->type = CHAN_SOCKDATA_DORMANT;/* identify channel type */ c->u.pfd.pf = pf; add234(ssh->channels, c); return c; } unsigned ssh_alloc_sharing_channel(Ssh ssh, void *sharing_ctx) { struct ssh_channel *c; c = snew(struct ssh_channel); c->ssh = ssh; ssh2_channel_init(c); c->type = CHAN_SHARING; c->u.sharing.ctx = sharing_ctx; add234(ssh->channels, c); return c->localid; } void ssh_delete_sharing_channel(Ssh ssh, unsigned localid) { struct ssh_channel *c; c = find234(ssh->channels, &localid, ssh_channelfind); if (c) ssh_channel_destroy(c); } void ssh_send_packet_from_downstream(Ssh ssh, unsigned id, int type, const void *data, int datalen, const char *additional_log_text) { struct Packet *pkt; pkt = ssh2_pkt_init(type); pkt->downstream_id = id; pkt->additional_log_text = additional_log_text; ssh2_pkt_adddata(pkt, data, datalen); ssh2_pkt_send(ssh, pkt); } /* * This is called when stdout/stderr (the entity to which * from_backend sends data) manages to clear some backlog. */ 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; ssh_throttle_conn(ssh, -1); } } else { if (ssh->mainchan) { ssh2_set_window(ssh->mainchan, bufsize < ssh->mainchan->v.v2.locmaxwin ? ssh->mainchan->v.v2.locmaxwin - bufsize : 0); if (ssh_is_simple(ssh)) 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); } } } /* * Now process any SSH connection data that was stashed in our * queue while we were frozen. */ ssh_process_queued_incoming_data(ssh); } void ssh_send_port_open(void *channel, char *hostname, int port, char *org) { struct ssh_channel *c = (struct ssh_channel *)channel; Ssh ssh = c->ssh; struct Packet *pktout; logeventf(ssh, "Opening connection to %s:%d for %s", hostname, port, org); if (ssh->version == 1) { send_packet(ssh, SSH1_MSG_PORT_OPEN, PKT_INT, c->localid, PKT_STR, hostname, PKT_INT, port, /* PKT_STR, <org:orgport>, */ PKT_END); } else { pktout = ssh2_chanopen_init(c, "direct-tcpip"); ssh2_pkt_addstring(pktout, hostname); ssh2_pkt_adduint32(pktout, port); /* * We make up values for the originator data; partly it's * too much hassle to keep track, and partly I'm not * convinced the server should be told details like that * about my local network configuration. |
︙ | ︙ |
Changes to ssh.h.
1 2 3 4 5 6 7 8 9 | #include <stdio.h> #include <string.h> #include "puttymem.h" #include "tree234.h" #include "network.h" #include "int64.h" #include "misc.h" | < < < < > < > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 | #include <stdio.h> #include <string.h> #include "puttymem.h" #include "tree234.h" #include "network.h" #include "int64.h" #include "misc.h" struct ssh_channel; typedef struct ssh_tag *Ssh; extern int sshfwd_write(struct ssh_channel *c, char *, int); extern void sshfwd_write_eof(struct ssh_channel *c); extern void sshfwd_unclean_close(struct ssh_channel *c, const char *err); extern void sshfwd_unthrottle(struct ssh_channel *c, int bufsize); Conf *sshfwd_get_conf(struct ssh_channel *c); void sshfwd_x11_sharing_handover(struct ssh_channel *c, void *share_cs, void *share_chan, const char *peer_addr, int peer_port, int endian, int protomajor, int protominor, const void *initial_data, int initial_len); void sshfwd_x11_is_local(struct ssh_channel *c); extern Socket ssh_connection_sharing_init(const char *host, int port, Conf *conf, Ssh ssh, void **state); void share_got_pkt_from_server(void *ctx, int type, unsigned char *pkt, int pktlen); void share_activate(void *state, const char *server_verstring); void sharestate_free(void *state); int share_ndownstreams(void *state); void ssh_connshare_log(Ssh ssh, int event, const char *logtext, const char *ds_err, const char *us_err); unsigned ssh_alloc_sharing_channel(Ssh ssh, void *sharing_ctx); void ssh_delete_sharing_channel(Ssh ssh, unsigned localid); int ssh_alloc_sharing_rportfwd(Ssh ssh, const char *shost, int sport, void *share_ctx); void ssh_sharing_queue_global_request(Ssh ssh, void *share_ctx); struct X11FakeAuth *ssh_sharing_add_x11_display(Ssh ssh, int authtype, void *share_cs, void *share_chan); void ssh_sharing_remove_x11_display(Ssh ssh, struct X11FakeAuth *auth); void ssh_send_packet_from_downstream(Ssh ssh, unsigned id, int type, const void *pkt, int pktlen, const char *additional_log_text); void ssh_sharing_downstream_connected(Ssh ssh, unsigned id); void ssh_sharing_downstream_disconnected(Ssh ssh, unsigned id); void ssh_sharing_logf(Ssh ssh, unsigned id, const char *logfmt, ...); int ssh_agent_forwarding_permitted(Ssh ssh); void share_setup_x11_channel(void *csv, void *chanv, unsigned upstream_id, unsigned server_id, unsigned server_currwin, unsigned server_maxpkt, unsigned client_adjusted_window, const char *peer_addr, int peer_port, int endian, int protomajor, int protominor, const void *initial_data, int initial_len); /* * Useful thing. */ #ifndef lenof #define lenof(x) ( (sizeof((x))) / (sizeof(*(x)))) #endif |
︙ | ︙ | |||
132 133 134 135 136 137 138 | typedef struct { uint32 h[5]; unsigned char block[64]; int blkused; uint32 lenhi, lenlo; } SHA_State; void SHA_Init(SHA_State * s); | | | | 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 | typedef struct { uint32 h[5]; unsigned char block[64]; int blkused; uint32 lenhi, lenlo; } SHA_State; void SHA_Init(SHA_State * s); void SHA_Bytes(SHA_State * s, const void *p, int len); void SHA_Final(SHA_State * s, unsigned char *output); void SHA_Simple(const void *p, int len, unsigned char *output); void hmac_sha1_simple(void *key, int keylen, void *data, int datalen, unsigned char *output); typedef struct { uint32 h[8]; unsigned char block[64]; int blkused; |
︙ | ︙ | |||
296 297 298 299 300 301 302 303 304 305 306 307 308 309 | extern const struct ssh_signkey ssh_dss; extern const struct ssh_signkey ssh_rsa; extern const struct ssh_mac ssh_hmac_md5; extern const struct ssh_mac ssh_hmac_sha1; extern const struct ssh_mac ssh_hmac_sha1_buggy; extern const struct ssh_mac ssh_hmac_sha1_96; extern const struct ssh_mac ssh_hmac_sha1_96_buggy; void *aes_make_context(void); void aes_free_context(void *handle); void aes128_key(void *handle, unsigned char *key); void aes192_key(void *handle, unsigned char *key); void aes256_key(void *handle, unsigned char *key); void aes_iv(void *handle, unsigned char *iv); | > | 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 | extern const struct ssh_signkey ssh_dss; extern const struct ssh_signkey ssh_rsa; extern const struct ssh_mac ssh_hmac_md5; extern const struct ssh_mac ssh_hmac_sha1; extern const struct ssh_mac ssh_hmac_sha1_buggy; extern const struct ssh_mac ssh_hmac_sha1_96; extern const struct ssh_mac ssh_hmac_sha1_96_buggy; extern const struct ssh_mac ssh_hmac_sha256; void *aes_make_context(void); void aes_free_context(void *handle); void aes128_key(void *handle, unsigned char *key); void aes192_key(void *handle, unsigned char *key); void aes256_key(void *handle, unsigned char *key); void aes_iv(void *handle, unsigned char *iv); |
︙ | ︙ | |||
327 328 329 330 331 332 333 334 335 | #endif int random_byte(void); void random_add_noise(void *noise, int length); void random_add_heavynoise(void *noise, int length); void logevent(void *, const char *); /* Allocate and register a new channel for port forwarding */ | > > | | < | < < < < < | < | > | | | > > > > > > < < < < < < < > > > > > > > > > > > > > > > > > > > > > > > > | < > | > | | | > | | | < | 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 | #endif int random_byte(void); void random_add_noise(void *noise, int length); void random_add_heavynoise(void *noise, int length); void logevent(void *, const char *); struct PortForwarding; /* Allocate and register a new channel for port forwarding */ void *new_sock_channel(void *handle, struct PortForwarding *pf); void ssh_send_port_open(void *channel, char *hostname, int port, char *org); /* Exports from portfwd.c */ extern char *pfd_connect(struct PortForwarding **pf, char *hostname, int port, void *c, Conf *conf, int addressfamily); extern void pfd_close(struct PortForwarding *); extern int pfd_send(struct PortForwarding *, char *data, int len); extern void pfd_send_eof(struct PortForwarding *); extern void pfd_confirm(struct PortForwarding *); extern void pfd_unthrottle(struct PortForwarding *); extern void pfd_override_throttle(struct PortForwarding *, int enable); struct PortListener; /* desthost == NULL indicates dynamic (SOCKS) port forwarding */ extern char *pfl_listen(char *desthost, int destport, char *srcaddr, int port, void *backhandle, Conf *conf, struct PortListener **pl, int address_family); extern void pfl_terminate(struct PortListener *); /* Exports from x11fwd.c */ enum { X11_TRANS_IPV4 = 0, X11_TRANS_IPV6 = 6, X11_TRANS_UNIX = 256 }; struct X11Display { /* Broken-down components of the display name itself */ int unixdomain; char *hostname; int displaynum; int screennum; /* OSX sometimes replaces all the above with a full Unix-socket pathname */ char *unixsocketpath; /* PuTTY networking SockAddr to connect to the display, and associated * gubbins */ SockAddr addr; int port; char *realhost; /* Our local auth details for talking to the real X display. */ int localauthproto; unsigned char *localauthdata; int localauthdatalen; }; struct X11FakeAuth { /* Auth details we invented for a virtual display on the SSH server. */ int proto; unsigned char *data; int datalen; char *protoname; char *datastring; /* The encrypted form of the first block, in XDM-AUTHORIZATION-1. * Used as part of the key when these structures are organised * into a tree. See x11_invent_fake_auth for explanation. */ unsigned char *xa1_firstblock; /* * Used inside x11fwd.c to remember recently seen * XDM-AUTHORIZATION-1 strings, to avoid replay attacks. */ tree234 *xdmseen; /* * What to do with an X connection matching this auth data. */ struct X11Display *disp; void *share_cs, *share_chan; }; void *x11_make_greeting(int endian, int protomajor, int protominor, int auth_proto, const void *auth_data, int auth_len, const char *peer_ip, int peer_port, int *outlen); int x11_authcmp(void *av, void *bv); /* for putting X11FakeAuth in a tree234 */ /* * x11_setup_display() parses the display variable and fills in an * X11Display structure. Some remote auth details are invented; * the supplied authtype parameter configures the preferred * authorisation protocol to use at the remote end. The local auth * details are looked up by calling platform_get_x11_auth. */ extern struct X11Display *x11_setup_display(char *display, Conf *); void x11_free_display(struct X11Display *disp); struct X11FakeAuth *x11_invent_fake_auth(tree234 *t, int authtype); void x11_free_fake_auth(struct X11FakeAuth *auth); struct X11Connection; /* opaque outside x11fwd.c */ struct X11Connection *x11_init(tree234 *authtree, void *, const char *, int); extern void x11_close(struct X11Connection *); extern int x11_send(struct X11Connection *, char *, int); extern void x11_send_eof(struct X11Connection *s); extern void x11_unthrottle(struct X11Connection *s); extern void x11_override_throttle(struct X11Connection *s, int enable); char *x11_display(const char *display); /* Platform-dependent X11 functions */ extern void platform_get_x11_auth(struct X11Display *display, Conf *); /* examine a mostly-filled-in X11Display and fill in localauth* */ extern const int platform_uses_x11_unix_by_default; /* choose default X transport in the absence of a specified one */ SockAddr platform_get_x11_unix_address(const char *path, int displaynum); /* make up a SockAddr naming the address for displaynum */ char *platform_get_x_display(void); /* allocated local X display string, if any */ |
︙ | ︙ | |||
428 429 430 431 432 433 434 435 436 437 438 439 440 441 | * platform_get_x11_auth() will work by finding their system's * .Xauthority file, adjusting the display details if necessary * for local oddities like Unix-domain socket transport, and * calling this function to do the rest of the work. */ void x11_get_auth_from_authfile(struct X11Display *display, const char *authfilename); Bignum copybn(Bignum b); Bignum bn_power_2(int n); void bn_restore_invariant(Bignum b); Bignum bignum_from_long(unsigned long n); void freebn(Bignum b); Bignum modpow(Bignum base, Bignum exp, Bignum mod); | > > | 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 | * platform_get_x11_auth() will work by finding their system's * .Xauthority file, adjusting the display details if necessary * for local oddities like Unix-domain socket transport, and * calling this function to do the rest of the work. */ void x11_get_auth_from_authfile(struct X11Display *display, const char *authfilename); int x11_identify_auth_proto(const char *proto); void *x11_dehexify(const char *hex, int *outlen); Bignum copybn(Bignum b); Bignum bn_power_2(int n); void bn_restore_invariant(Bignum b); Bignum bignum_from_long(unsigned long n); void freebn(Bignum b); Bignum modpow(Bignum base, Bignum exp, Bignum mod); |
︙ | ︙ | |||
531 532 533 534 535 536 537 | void des3_encrypt_pubkey_ossh(unsigned char *key, unsigned char *iv, unsigned char *blk, int len); void aes256_encrypt_pubkey(unsigned char *key, unsigned char *blk, int len); void aes256_decrypt_pubkey(unsigned char *key, unsigned char *blk, int len); | > | > | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 | void des3_encrypt_pubkey_ossh(unsigned char *key, unsigned char *iv, unsigned char *blk, int len); void aes256_encrypt_pubkey(unsigned char *key, unsigned char *blk, int len); void aes256_decrypt_pubkey(unsigned char *key, unsigned char *blk, int len); void des_encrypt_xdmauth(const unsigned char *key, unsigned char *blk, int len); void des_decrypt_xdmauth(const unsigned char *key, unsigned char *blk, int len); /* * For progress updates in the key generation utility. */ #define PROGFN_INITIALISE 1 #define PROGFN_LIN_PHASE 2 #define PROGFN_EXP_PHASE 3 #define PROGFN_PHASE_EXTENT 4 #define PROGFN_READY 5 #define PROGFN_PROGRESS 6 typedef void (*progfn_t) (void *param, int action, int phase, int progress); int rsa_generate(struct RSAKey *key, int bits, progfn_t pfn, void *pfnparam); int dsa_generate(struct dss_key *key, int bits, progfn_t pfn, void *pfnparam); Bignum primegen(int bits, int modulus, int residue, Bignum factor, int phase, progfn_t pfn, void *pfnparam, unsigned firstbits); void invent_firstbits(unsigned *one, unsigned *two); /* * zlib compression. */ void *zlib_compress_init(void); void zlib_compress_cleanup(void *); void *zlib_decompress_init(void); void zlib_decompress_cleanup(void *); int zlib_compress_block(void *, unsigned char *block, int len, unsigned char **outblock, int *outlen); int zlib_decompress_block(void *, unsigned char *block, int len, unsigned char **outblock, int *outlen); /* * Connection-sharing API provided by platforms. This function must * either: * - return SHARE_NONE and do nothing * - return SHARE_DOWNSTREAM and set *sock to a Socket connected to * downplug * - return SHARE_UPSTREAM and set *sock to a Socket connected to * upplug. */ enum { SHARE_NONE, SHARE_DOWNSTREAM, SHARE_UPSTREAM }; int platform_ssh_share(const char *name, Conf *conf, Plug downplug, Plug upplug, Socket *sock, char **logtext, char **ds_err, char **us_err, int can_upstream, int can_downstream); void platform_ssh_share_cleanup(const char *name); /* * SSH-1 message type codes. */ #define SSH1_MSG_DISCONNECT 1 /* 0x1 */ #define SSH1_SMSG_PUBLIC_KEY 2 /* 0x2 */ #define SSH1_CMSG_SESSION_KEY 3 /* 0x3 */ #define SSH1_CMSG_USER 4 /* 0x4 */ #define SSH1_CMSG_AUTH_RSA 6 /* 0x6 */ #define SSH1_SMSG_AUTH_RSA_CHALLENGE 7 /* 0x7 */ #define SSH1_CMSG_AUTH_RSA_RESPONSE 8 /* 0x8 */ #define SSH1_CMSG_AUTH_PASSWORD 9 /* 0x9 */ #define SSH1_CMSG_REQUEST_PTY 10 /* 0xa */ #define SSH1_CMSG_WINDOW_SIZE 11 /* 0xb */ #define SSH1_CMSG_EXEC_SHELL 12 /* 0xc */ #define SSH1_CMSG_EXEC_CMD 13 /* 0xd */ #define SSH1_SMSG_SUCCESS 14 /* 0xe */ #define SSH1_SMSG_FAILURE 15 /* 0xf */ #define SSH1_CMSG_STDIN_DATA 16 /* 0x10 */ #define SSH1_SMSG_STDOUT_DATA 17 /* 0x11 */ #define SSH1_SMSG_STDERR_DATA 18 /* 0x12 */ #define SSH1_CMSG_EOF 19 /* 0x13 */ #define SSH1_SMSG_EXIT_STATUS 20 /* 0x14 */ #define SSH1_MSG_CHANNEL_OPEN_CONFIRMATION 21 /* 0x15 */ #define SSH1_MSG_CHANNEL_OPEN_FAILURE 22 /* 0x16 */ #define SSH1_MSG_CHANNEL_DATA 23 /* 0x17 */ #define SSH1_MSG_CHANNEL_CLOSE 24 /* 0x18 */ #define SSH1_MSG_CHANNEL_CLOSE_CONFIRMATION 25 /* 0x19 */ #define SSH1_SMSG_X11_OPEN 27 /* 0x1b */ #define SSH1_CMSG_PORT_FORWARD_REQUEST 28 /* 0x1c */ #define SSH1_MSG_PORT_OPEN 29 /* 0x1d */ #define SSH1_CMSG_AGENT_REQUEST_FORWARDING 30 /* 0x1e */ #define SSH1_SMSG_AGENT_OPEN 31 /* 0x1f */ #define SSH1_MSG_IGNORE 32 /* 0x20 */ #define SSH1_CMSG_EXIT_CONFIRMATION 33 /* 0x21 */ #define SSH1_CMSG_X11_REQUEST_FORWARDING 34 /* 0x22 */ #define SSH1_CMSG_AUTH_RHOSTS_RSA 35 /* 0x23 */ #define SSH1_MSG_DEBUG 36 /* 0x24 */ #define SSH1_CMSG_REQUEST_COMPRESSION 37 /* 0x25 */ #define SSH1_CMSG_AUTH_TIS 39 /* 0x27 */ #define SSH1_SMSG_AUTH_TIS_CHALLENGE 40 /* 0x28 */ #define SSH1_CMSG_AUTH_TIS_RESPONSE 41 /* 0x29 */ #define SSH1_CMSG_AUTH_CCARD 70 /* 0x46 */ #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 */ #define SSH1_PROTOFLAG_SCREEN_NUMBER 1 /* 0x1 */ /* Mask for protoflags we will echo back to server if seen */ #define SSH1_PROTOFLAGS_SUPPORTED 0 /* 0x1 */ /* * SSH-2 message type codes. */ #define SSH2_MSG_DISCONNECT 1 /* 0x1 */ #define SSH2_MSG_IGNORE 2 /* 0x2 */ #define SSH2_MSG_UNIMPLEMENTED 3 /* 0x3 */ #define SSH2_MSG_DEBUG 4 /* 0x4 */ #define SSH2_MSG_SERVICE_REQUEST 5 /* 0x5 */ #define SSH2_MSG_SERVICE_ACCEPT 6 /* 0x6 */ #define SSH2_MSG_KEXINIT 20 /* 0x14 */ #define SSH2_MSG_NEWKEYS 21 /* 0x15 */ #define SSH2_MSG_KEXDH_INIT 30 /* 0x1e */ #define SSH2_MSG_KEXDH_REPLY 31 /* 0x1f */ #define SSH2_MSG_KEX_DH_GEX_REQUEST 30 /* 0x1e */ #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 */ #define SSH2_MSG_USERAUTH_BANNER 53 /* 0x35 */ #define SSH2_MSG_USERAUTH_PK_OK 60 /* 0x3c */ #define SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ 60 /* 0x3c */ #define SSH2_MSG_USERAUTH_INFO_REQUEST 60 /* 0x3c */ #define SSH2_MSG_USERAUTH_INFO_RESPONSE 61 /* 0x3d */ #define SSH2_MSG_GLOBAL_REQUEST 80 /* 0x50 */ #define SSH2_MSG_REQUEST_SUCCESS 81 /* 0x51 */ #define SSH2_MSG_REQUEST_FAILURE 82 /* 0x52 */ #define SSH2_MSG_CHANNEL_OPEN 90 /* 0x5a */ #define SSH2_MSG_CHANNEL_OPEN_CONFIRMATION 91 /* 0x5b */ #define SSH2_MSG_CHANNEL_OPEN_FAILURE 92 /* 0x5c */ #define SSH2_MSG_CHANNEL_WINDOW_ADJUST 93 /* 0x5d */ #define SSH2_MSG_CHANNEL_DATA 94 /* 0x5e */ #define SSH2_MSG_CHANNEL_EXTENDED_DATA 95 /* 0x5f */ #define SSH2_MSG_CHANNEL_EOF 96 /* 0x60 */ #define SSH2_MSG_CHANNEL_CLOSE 97 /* 0x61 */ #define SSH2_MSG_CHANNEL_REQUEST 98 /* 0x62 */ #define SSH2_MSG_CHANNEL_SUCCESS 99 /* 0x63 */ #define SSH2_MSG_CHANNEL_FAILURE 100 /* 0x64 */ #define SSH2_MSG_USERAUTH_GSSAPI_RESPONSE 60 #define SSH2_MSG_USERAUTH_GSSAPI_TOKEN 61 #define SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE 63 #define SSH2_MSG_USERAUTH_GSSAPI_ERROR 64 #define SSH2_MSG_USERAUTH_GSSAPI_ERRTOK 65 #define SSH2_MSG_USERAUTH_GSSAPI_MIC 66 /* * SSH-1 agent messages. */ #define SSH1_AGENTC_REQUEST_RSA_IDENTITIES 1 #define SSH1_AGENT_RSA_IDENTITIES_ANSWER 2 #define SSH1_AGENTC_RSA_CHALLENGE 3 #define SSH1_AGENT_RSA_RESPONSE 4 |
︙ | ︙ | |||
593 594 595 596 597 598 599 600 601 602 603 604 | #define SSH2_AGENT_IDENTITIES_ANSWER 12 #define SSH2_AGENTC_SIGN_REQUEST 13 #define SSH2_AGENT_SIGN_RESPONSE 14 #define SSH2_AGENTC_ADD_IDENTITY 17 #define SSH2_AGENTC_REMOVE_IDENTITY 18 #define SSH2_AGENTC_REMOVE_ALL_IDENTITIES 19 /* * Need this to warn about support for the original SSH-2 keyfile * format. */ void old_keyfile_warning(void); | > > > > > > > > > > > > > > > > > > > > > > > > > > | 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 | #define SSH2_AGENT_IDENTITIES_ANSWER 12 #define SSH2_AGENTC_SIGN_REQUEST 13 #define SSH2_AGENT_SIGN_RESPONSE 14 #define SSH2_AGENTC_ADD_IDENTITY 17 #define SSH2_AGENTC_REMOVE_IDENTITY 18 #define SSH2_AGENTC_REMOVE_ALL_IDENTITIES 19 /* * Assorted other SSH-related enumerations. */ #define SSH2_DISCONNECT_HOST_NOT_ALLOWED_TO_CONNECT 1 /* 0x1 */ #define SSH2_DISCONNECT_PROTOCOL_ERROR 2 /* 0x2 */ #define SSH2_DISCONNECT_KEY_EXCHANGE_FAILED 3 /* 0x3 */ #define SSH2_DISCONNECT_HOST_AUTHENTICATION_FAILED 4 /* 0x4 */ #define SSH2_DISCONNECT_MAC_ERROR 5 /* 0x5 */ #define SSH2_DISCONNECT_COMPRESSION_ERROR 6 /* 0x6 */ #define SSH2_DISCONNECT_SERVICE_NOT_AVAILABLE 7 /* 0x7 */ #define SSH2_DISCONNECT_PROTOCOL_VERSION_NOT_SUPPORTED 8 /* 0x8 */ #define SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE 9 /* 0x9 */ #define SSH2_DISCONNECT_CONNECTION_LOST 10 /* 0xa */ #define SSH2_DISCONNECT_BY_APPLICATION 11 /* 0xb */ #define SSH2_DISCONNECT_TOO_MANY_CONNECTIONS 12 /* 0xc */ #define SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER 13 /* 0xd */ #define SSH2_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE 14 /* 0xe */ #define SSH2_DISCONNECT_ILLEGAL_USER_NAME 15 /* 0xf */ #define SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED 1 /* 0x1 */ #define SSH2_OPEN_CONNECT_FAILED 2 /* 0x2 */ #define SSH2_OPEN_UNKNOWN_CHANNEL_TYPE 3 /* 0x3 */ #define SSH2_OPEN_RESOURCE_SHORTAGE 4 /* 0x4 */ #define SSH2_EXTENDED_DATA_STDERR 1 /* 0x1 */ /* * Need this to warn about support for the original SSH-2 keyfile * format. */ void old_keyfile_warning(void); |
Changes to sshaes.c.
︙ | ︙ | |||
1153 1154 1155 1156 1157 1158 1159 | void aes256_encrypt_pubkey(unsigned char *key, unsigned char *blk, int len) { AESContext ctx; aes_setup(&ctx, 16, key, 32); memset(ctx.iv, 0, sizeof(ctx.iv)); aes_encrypt_cbc(blk, len, &ctx); | | | | 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 | void aes256_encrypt_pubkey(unsigned char *key, unsigned char *blk, int len) { AESContext ctx; aes_setup(&ctx, 16, key, 32); memset(ctx.iv, 0, sizeof(ctx.iv)); aes_encrypt_cbc(blk, len, &ctx); smemclr(&ctx, sizeof(ctx)); } void aes256_decrypt_pubkey(unsigned char *key, unsigned char *blk, int len) { AESContext ctx; aes_setup(&ctx, 16, key, 32); memset(ctx.iv, 0, sizeof(ctx.iv)); aes_decrypt_cbc(blk, len, &ctx); smemclr(&ctx, sizeof(ctx)); } static const struct ssh2_cipher ssh_aes128_ctr = { aes_make_context, aes_free_context, aes_iv, aes128_key, aes_ssh2_sdctr, aes_ssh2_sdctr, "aes128-ctr", 16, 128, 0, "AES-128 SDCTR" |
︙ | ︙ |
Changes to ssharcf.c.
︙ | ︙ | |||
71 72 73 74 75 76 77 | } static void arcfour_stir(ArcfourContext *ctx) { unsigned char *junk = snewn(1536, unsigned char); memset(junk, 0, 1536); arcfour_block(ctx, junk, 1536); | | | 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 | } static void arcfour_stir(ArcfourContext *ctx) { unsigned char *junk = snewn(1536, unsigned char); memset(junk, 0, 1536); arcfour_block(ctx, junk, 1536); smemclr(junk, 1536); sfree(junk); } static void arcfour128_key(void *handle, unsigned char *key) { ArcfourContext *ctx = (ArcfourContext *)handle; arcfour_setkey(ctx, key, 16); |
︙ | ︙ |
Changes to sshbn.c.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | /* * Bignum routines for RSA and DH and stuff. */ #include <stdio.h> #include <assert.h> #include <stdlib.h> #include <string.h> #include "misc.h" /* * Usage notes: * * Do not call the DIVMOD_WORD macro with expressions such as array * subscripts, as some implementations object to this (see below). | > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | /* * Bignum routines for RSA and DH and stuff. */ #include <stdio.h> #include <assert.h> #include <stdlib.h> #include <string.h> #include <limits.h> #include "misc.h" /* * Usage notes: * * Do not call the DIVMOD_WORD macro with expressions such as array * subscripts, as some implementations object to this (see below). |
︙ | ︙ | |||
116 117 118 119 120 121 122 | * nonzero. */ Bignum Zero = bnZero, One = bnOne; static Bignum newbn(int length) { | > > > > | | 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 | * nonzero. */ Bignum Zero = bnZero, One = bnOne; static Bignum newbn(int length) { Bignum b; assert(length >= 0 && length < INT_MAX / BIGNUM_INT_BITS); b = snewn(length + 1, BignumInt); if (!b) abort(); /* FIXME */ memset(b, 0, (length + 1) * sizeof(*b)); b[0] = length; return b; } |
︙ | ︙ | |||
144 145 146 147 148 149 150 | } void freebn(Bignum b) { /* * Burn the evidence, just in case. */ | | > > > > | | 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 | } void freebn(Bignum b) { /* * Burn the evidence, just in case. */ smemclr(b, sizeof(b[0]) * (b[0] + 1)); sfree(b); } Bignum bn_power_2(int n) { Bignum ret; assert(n >= 0); ret = newbn(n / BIGNUM_INT_BITS + 1); bignum_set_bit(ret, n, 1); return ret; } /* * Internal addition. Sets c = a - b, where 'a', 'b' and 'c' are all * big-endian arrays of 'len' BignumInts. Returns a BignumInt carried |
︙ | ︙ | |||
594 595 596 597 598 599 600 601 602 603 604 605 606 607 | int word = 1 + (shift / BIGNUM_INT_BITS); int bshift = shift % BIGNUM_INT_BITS; BignumDblInt addend; addend = (BignumDblInt)n << bshift; while (addend) { addend += number[word]; number[word] = (BignumInt) addend & BIGNUM_INT_MASK; addend >>= BIGNUM_INT_BITS; word++; } } | > | 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 | int word = 1 + (shift / BIGNUM_INT_BITS); int bshift = shift % BIGNUM_INT_BITS; BignumDblInt addend; addend = (BignumDblInt)n << bshift; while (addend) { assert(word <= number[0]); addend += number[word]; number[word] = (BignumInt) addend & BIGNUM_INT_MASK; addend >>= BIGNUM_INT_BITS; word++; } } |
︙ | ︙ | |||
620 621 622 623 624 625 626 627 628 629 630 631 632 633 | BignumInt *quot, int qshift) { BignumInt m0, m1; unsigned int h; int i, k; m0 = m[0]; if (mlen > 1) m1 = m[1]; else m1 = 0; for (i = 0; i <= alen - mlen; i++) { BignumDblInt t; | > | 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 | BignumInt *quot, int qshift) { BignumInt m0, m1; unsigned int h; int i, k; m0 = m[0]; assert(m0 >> (BIGNUM_INT_BITS-1) == 1); if (mlen > 1) m1 = m[1]; else m1 = 0; for (i = 0; i <= alen - mlen; i++) { BignumDblInt t; |
︙ | ︙ | |||
811 812 813 814 815 816 817 | result = newbn(mod[0]); for (i = 0; i < mlen; i++) result[result[0] - i] = a[i + mlen]; while (result[0] > 1 && result[result[0]] == 0) result[0]--; /* Free temporary arrays */ | | < | < | < | < | < | 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 | result = newbn(mod[0]); for (i = 0; i < mlen; i++) result[result[0] - i] = a[i + mlen]; while (result[0] > 1 && result[result[0]] == 0) result[0]--; /* Free temporary arrays */ smemclr(a, 2 * mlen * sizeof(*a)); sfree(a); smemclr(scratch, scratchlen * sizeof(*scratch)); sfree(scratch); smemclr(b, 2 * mlen * sizeof(*b)); sfree(b); smemclr(m, mlen * sizeof(*m)); sfree(m); smemclr(n, mlen * sizeof(*n)); sfree(n); freebn(base); return result; } |
︙ | ︙ | |||
869 870 871 872 873 874 875 876 877 878 879 880 881 882 | * Compute the inverse of n mod r, for monty_reduce. (In fact we * want the inverse of _minus_ n mod r, but we'll sort that out * below.) */ len = mod[0]; r = bn_power_2(BIGNUM_INT_BITS * len); inv = modinv(mod, r); /* * Multiply the base by r mod n, to get it into Montgomery * representation. */ base2 = modmul(base, r, mod); freebn(base); | > | 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 | * Compute the inverse of n mod r, for monty_reduce. (In fact we * want the inverse of _minus_ n mod r, but we'll sort that out * below.) */ len = mod[0]; r = bn_power_2(BIGNUM_INT_BITS * len); inv = modinv(mod, r); assert(inv); /* cannot fail, since mod is odd and r is a power of 2 */ /* * Multiply the base by r mod n, to get it into Montgomery * representation. */ base2 = modmul(base, r, mod); freebn(base); |
︙ | ︙ | |||
961 962 963 964 965 966 967 | result = newbn(mod[0]); for (i = 0; i < len; i++) result[result[0] - i] = a[i + len]; while (result[0] > 1 && result[result[0]] == 0) result[0]--; /* Free temporary arrays */ | | < | < | < < | | < | < > > > > > > > > > > > > > | 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 | result = newbn(mod[0]); for (i = 0; i < len; i++) result[result[0] - i] = a[i + len]; while (result[0] > 1 && result[result[0]] == 0) result[0]--; /* Free temporary arrays */ smemclr(scratch, scratchlen * sizeof(*scratch)); sfree(scratch); smemclr(a, 2 * len * sizeof(*a)); sfree(a); smemclr(b, 2 * len * sizeof(*b)); sfree(b); smemclr(mninv, len * sizeof(*mninv)); sfree(mninv); smemclr(n, len * sizeof(*n)); sfree(n); smemclr(x, len * sizeof(*x)); sfree(x); return result; } /* * Compute (p * q) % mod. * The most significant word of mod MUST be non-zero. * We assume that the result array is the same size as the mod array. */ Bignum modmul(Bignum p, Bignum q, Bignum mod) { BignumInt *a, *n, *m, *o, *scratch; int mshift, scratchlen; int pqlen, mlen, rlen, i, j; Bignum result; /* * The most significant word of mod needs to be non-zero. It * should already be, but let's make sure. */ assert(mod[mod[0]] != 0); /* Allocate m of size mlen, copy mod to m */ /* We use big endian internally */ mlen = mod[0]; m = snewn(mlen, BignumInt); for (j = 0; j < mlen; j++) m[j] = mod[mod[0] - j]; /* Shift m left to make msb bit set */ for (mshift = 0; mshift < BIGNUM_INT_BITS-1; mshift++) if ((m[0] << mshift) & BIGNUM_TOP_BIT) break; if (mshift) { for (i = 0; i < mlen - 1; i++) m[i] = (m[i] << mshift) | (m[i + 1] >> (BIGNUM_INT_BITS - mshift)); m[mlen - 1] = m[mlen - 1] << mshift; } pqlen = (p[0] > q[0] ? p[0] : q[0]); /* * Make sure that we're allowing enough space. The shifting below * will underflow the vectors we allocate if pqlen is too small. */ if (2*pqlen <= mlen) pqlen = mlen/2 + 1; /* Allocate n of size pqlen, copy p to n */ n = snewn(pqlen, BignumInt); i = pqlen - p[0]; for (j = 0; j < i; j++) n[j] = 0; for (j = 0; j < (int)p[0]; j++) n[i + j] = p[p[0] - j]; |
︙ | ︙ | |||
1060 1061 1062 1063 1064 1065 1066 | result = newbn(rlen); for (i = 0; i < rlen; i++) result[result[0] - i] = a[i + 2 * pqlen - rlen]; while (result[0] > 1 && result[result[0]] == 0) result[0]--; /* Free temporary arrays */ | | < | < | < | < | < > > > > > > | 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 | result = newbn(rlen); for (i = 0; i < rlen; i++) result[result[0] - i] = a[i + 2 * pqlen - rlen]; while (result[0] > 1 && result[result[0]] == 0) result[0]--; /* Free temporary arrays */ smemclr(scratch, scratchlen * sizeof(*scratch)); sfree(scratch); smemclr(a, 2 * pqlen * sizeof(*a)); sfree(a); smemclr(m, mlen * sizeof(*m)); sfree(m); smemclr(n, pqlen * sizeof(*n)); sfree(n); smemclr(o, pqlen * sizeof(*o)); sfree(o); return result; } /* * Compute p % mod. * The most significant word of mod MUST be non-zero. * We assume that the result array is the same size as the mod array. * We optionally write out a quotient if `quotient' is non-NULL. * We can avoid writing out the result if `result' is NULL. */ static void bigdivmod(Bignum p, Bignum mod, Bignum result, Bignum quotient) { BignumInt *n, *m; int mshift; int plen, mlen, i, j; /* * The most significant word of mod needs to be non-zero. It * should already be, but let's make sure. */ assert(mod[mod[0]] != 0); /* Allocate m of size mlen, copy mod to m */ /* We use big endian internally */ mlen = mod[0]; m = snewn(mlen, BignumInt); for (j = 0; j < mlen; j++) m[j] = mod[mod[0] - j]; |
︙ | ︙ | |||
1143 1144 1145 1146 1147 1148 1149 | for (i = 1; i <= (int)result[0]; i++) { int j = plen - i; result[i] = j >= 0 ? n[j] : 0; } } /* Free temporary arrays */ | | < | < > > | 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 | for (i = 1; i <= (int)result[0]; i++) { int j = plen - i; result[i] = j >= 0 ? n[j] : 0; } } /* Free temporary arrays */ smemclr(m, mlen * sizeof(*m)); sfree(m); smemclr(n, plen * sizeof(*n)); sfree(n); } /* * Decrement a number. */ void decbn(Bignum bn) { int i = 1; while (i < (int)bn[0] && bn[i] == 0) bn[i++] = BIGNUM_INT_MASK; bn[i]--; } Bignum bignum_from_bytes(const unsigned char *data, int nbytes) { Bignum result; int w, i; assert(nbytes >= 0 && nbytes < INT_MAX/8); w = (nbytes + BIGNUM_INT_BYTES - 1) / BIGNUM_INT_BYTES; /* bytes->words */ result = newbn(w); for (i = 1; i <= w; i++) result[i] = 0; for (i = nbytes; i--;) { |
︙ | ︙ | |||
1243 1244 1245 1246 1247 1248 1249 | } /* * Return a byte from a bignum; 0 is least significant, etc. */ int bignum_byte(Bignum bn, int i) { | | | | | 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 | } /* * Return a byte from a bignum; 0 is least significant, etc. */ int bignum_byte(Bignum bn, int i) { if (i < 0 || i >= (int)(BIGNUM_INT_BYTES * bn[0])) return 0; /* beyond the end */ else return (bn[i / BIGNUM_INT_BYTES + 1] >> ((i % BIGNUM_INT_BYTES)*8)) & 0xFF; } /* * Return a bit from a bignum; 0 is least significant, etc. */ int bignum_bit(Bignum bn, int i) { if (i < 0 || i >= (int)(BIGNUM_INT_BITS * bn[0])) return 0; /* beyond the end */ else return (bn[i / BIGNUM_INT_BITS + 1] >> (i % BIGNUM_INT_BITS)) & 1; } /* * Set a bit in a bignum; 0 is least significant, etc. */ void bignum_set_bit(Bignum bn, int bitnum, int value) { if (bitnum < 0 || bitnum >= (int)(BIGNUM_INT_BITS * bn[0])) abort(); /* beyond the end */ else { int v = bitnum / BIGNUM_INT_BITS + 1; int mask = 1 << (bitnum % BIGNUM_INT_BITS); if (value) bn[v] |= mask; else |
︙ | ︙ | |||
1302 1303 1304 1305 1306 1307 1308 | /* * Compare two bignums. Returns like strcmp. */ int bignum_cmp(Bignum a, Bignum b) { int amax = a[0], bmax = b[0]; | > > > > > > > > > > > | > > | 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 | /* * Compare two bignums. Returns like strcmp. */ int bignum_cmp(Bignum a, Bignum b) { int amax = a[0], bmax = b[0]; int i; /* Annoyingly we have two representations of zero */ if (amax == 1 && a[amax] == 0) amax = 0; if (bmax == 1 && b[bmax] == 0) bmax = 0; assert(amax == 0 || a[amax] != 0); assert(bmax == 0 || b[bmax] != 0); i = (amax > bmax ? amax : bmax); while (i) { BignumInt aval = (i > amax ? 0 : a[i]); BignumInt bval = (i > bmax ? 0 : b[i]); if (aval < bval) return -1; if (aval > bval) return +1; i--; } return 0; } /* * Right-shift one bignum to form another. */ Bignum bignum_rshift(Bignum a, int shift) { Bignum ret; int i, shiftw, shiftb, shiftbb, bits; BignumInt ai, ai1; assert(shift >= 0); bits = bignum_bitcount(a) - shift; ret = newbn((bits + BIGNUM_INT_BITS - 1) / BIGNUM_INT_BITS); if (ret) { shiftw = shift / BIGNUM_INT_BITS; shiftb = shift % BIGNUM_INT_BITS; |
︙ | ︙ | |||
1394 1395 1396 1397 1398 1399 1400 | carry >>= BIGNUM_INT_BITS; if (ret[i] != 0 && i > maxspot) maxspot = i; } } ret[0] = maxspot; | < | | 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 | carry >>= BIGNUM_INT_BITS; if (ret[i] != 0 && i > maxspot) maxspot = i; } } ret[0] = maxspot; smemclr(workspace, wslen * sizeof(*workspace)); sfree(workspace); return ret; } /* * Non-modular multiplication. */ |
︙ | ︙ | |||
1625 1626 1627 1628 1629 1630 1631 1632 | { Bignum a = copybn(modulus); Bignum b = copybn(number); Bignum xp = copybn(Zero); Bignum x = copybn(One); int sign = +1; while (bignum_cmp(b, One) != 0) { | > > > > > > > > > > > > > > > > > | | | 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 | { Bignum a = copybn(modulus); Bignum b = copybn(number); Bignum xp = copybn(Zero); Bignum x = copybn(One); int sign = +1; assert(number[number[0]] != 0); assert(modulus[modulus[0]] != 0); while (bignum_cmp(b, One) != 0) { Bignum t, q; if (bignum_cmp(b, Zero) == 0) { /* * Found a common factor between the inputs, so we cannot * return a modular inverse at all. */ freebn(b); freebn(a); freebn(xp); freebn(x); return NULL; } t = newbn(b[0]); q = newbn(a[0]); bigdivmod(a, b, t, q); while (t[0] > 1 && t[t[0]] == 0) t[0]--; freebn(a); a = b; b = t; t = xp; |
︙ | ︙ | |||
1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 | */ if (ndigit > 0) memmove(ret, ret + ndigit, ndigits - ndigit); /* * Done. */ sfree(workspace); return ret; } #ifdef TESTBN #include <stdio.h> #include <stdlib.h> #include <ctype.h> /* | > | | 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 | */ if (ndigit > 0) memmove(ret, ret + ndigit, ndigits - ndigit); /* * Done. */ smemclr(workspace, x[0] * sizeof(*workspace)); sfree(workspace); return ret; } #ifdef TESTBN #include <stdio.h> #include <stdlib.h> #include <ctype.h> /* * gcc -Wall -g -O0 -DTESTBN -o testbn sshbn.c misc.c conf.c tree234.c unix/uxmisc.c -I. -I unix -I charset * * Then feed to this program's standard input the output of * testdata/bignum.py . */ void modalfatalbox(char *p, ...) { |
︙ | ︙ | |||
1831 1832 1833 1834 1835 1836 1837 | ptrs[ptrnum] = q; } if (!strcmp(buf, "mul")) { Bignum a, b, c, p; if (ptrnum != 3) { | | | 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 | ptrs[ptrnum] = q; } if (!strcmp(buf, "mul")) { Bignum a, b, c, p; if (ptrnum != 3) { printf("%d: mul with %d parameters, expected 3\n", line, ptrnum); exit(1); } a = bignum_from_bytes(ptrs[0], ptrs[1]-ptrs[0]); b = bignum_from_bytes(ptrs[1], ptrs[2]-ptrs[1]); c = bignum_from_bytes(ptrs[2], ptrs[3]-ptrs[2]); p = bigmul(a, b); |
︙ | ︙ | |||
1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 | sfree(bs); sfree(cs); sfree(ps); } freebn(a); freebn(b); freebn(c); freebn(p); } else if (!strcmp(buf, "pow")) { Bignum base, expt, modulus, expected, answer; if (ptrnum != 4) { | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 | sfree(bs); sfree(cs); sfree(ps); } freebn(a); freebn(b); freebn(c); freebn(p); } else if (!strcmp(buf, "modmul")) { Bignum a, b, m, c, p; if (ptrnum != 4) { printf("%d: modmul with %d parameters, expected 4\n", line, ptrnum); exit(1); } a = bignum_from_bytes(ptrs[0], ptrs[1]-ptrs[0]); b = bignum_from_bytes(ptrs[1], ptrs[2]-ptrs[1]); m = bignum_from_bytes(ptrs[2], ptrs[3]-ptrs[2]); c = bignum_from_bytes(ptrs[3], ptrs[4]-ptrs[3]); p = modmul(a, b, m); if (bignum_cmp(c, p) == 0) { passes++; } else { char *as = bignum_decimal(a); char *bs = bignum_decimal(b); char *ms = bignum_decimal(m); char *cs = bignum_decimal(c); char *ps = bignum_decimal(p); printf("%d: fail: %s * %s mod %s gave %s expected %s\n", line, as, bs, ms, ps, cs); fails++; sfree(as); sfree(bs); sfree(ms); sfree(cs); sfree(ps); } freebn(a); freebn(b); freebn(m); freebn(c); freebn(p); } else if (!strcmp(buf, "pow")) { Bignum base, expt, modulus, expected, answer; if (ptrnum != 4) { printf("%d: mul with %d parameters, expected 4\n", line, ptrnum); exit(1); } base = bignum_from_bytes(ptrs[0], ptrs[1]-ptrs[0]); expt = bignum_from_bytes(ptrs[1], ptrs[2]-ptrs[1]); modulus = bignum_from_bytes(ptrs[2], ptrs[3]-ptrs[2]); expected = bignum_from_bytes(ptrs[3], ptrs[4]-ptrs[3]); |
︙ | ︙ |
Changes to sshdes.c.
︙ | ︙ | |||
854 855 856 857 858 859 860 | des_key_setup(GET_32BIT_MSB_FIRST(key), GET_32BIT_MSB_FIRST(key + 4), &ourkeys[0]); des_key_setup(GET_32BIT_MSB_FIRST(key + 8), GET_32BIT_MSB_FIRST(key + 12), &ourkeys[1]); des_key_setup(GET_32BIT_MSB_FIRST(key), GET_32BIT_MSB_FIRST(key + 4), &ourkeys[2]); des_3cbc_decrypt(blk, len, ourkeys); | | | | | | | 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 | des_key_setup(GET_32BIT_MSB_FIRST(key), GET_32BIT_MSB_FIRST(key + 4), &ourkeys[0]); des_key_setup(GET_32BIT_MSB_FIRST(key + 8), GET_32BIT_MSB_FIRST(key + 12), &ourkeys[1]); des_key_setup(GET_32BIT_MSB_FIRST(key), GET_32BIT_MSB_FIRST(key + 4), &ourkeys[2]); des_3cbc_decrypt(blk, len, ourkeys); smemclr(ourkeys, sizeof(ourkeys)); } void des3_encrypt_pubkey(unsigned char *key, unsigned char *blk, int len) { DESContext ourkeys[3]; des_key_setup(GET_32BIT_MSB_FIRST(key), GET_32BIT_MSB_FIRST(key + 4), &ourkeys[0]); des_key_setup(GET_32BIT_MSB_FIRST(key + 8), GET_32BIT_MSB_FIRST(key + 12), &ourkeys[1]); des_key_setup(GET_32BIT_MSB_FIRST(key), GET_32BIT_MSB_FIRST(key + 4), &ourkeys[2]); des_3cbc_encrypt(blk, len, ourkeys); smemclr(ourkeys, sizeof(ourkeys)); } void des3_decrypt_pubkey_ossh(unsigned char *key, unsigned char *iv, unsigned char *blk, int len) { DESContext ourkeys[3]; des_key_setup(GET_32BIT_MSB_FIRST(key), GET_32BIT_MSB_FIRST(key + 4), &ourkeys[0]); des_key_setup(GET_32BIT_MSB_FIRST(key + 8), GET_32BIT_MSB_FIRST(key + 12), &ourkeys[1]); des_key_setup(GET_32BIT_MSB_FIRST(key + 16), GET_32BIT_MSB_FIRST(key + 20), &ourkeys[2]); ourkeys[0].iv0 = GET_32BIT_MSB_FIRST(iv); ourkeys[0].iv1 = GET_32BIT_MSB_FIRST(iv+4); des_cbc3_decrypt(blk, len, ourkeys); smemclr(ourkeys, sizeof(ourkeys)); } void des3_encrypt_pubkey_ossh(unsigned char *key, unsigned char *iv, unsigned char *blk, int len) { DESContext ourkeys[3]; des_key_setup(GET_32BIT_MSB_FIRST(key), GET_32BIT_MSB_FIRST(key + 4), &ourkeys[0]); des_key_setup(GET_32BIT_MSB_FIRST(key + 8), GET_32BIT_MSB_FIRST(key + 12), &ourkeys[1]); des_key_setup(GET_32BIT_MSB_FIRST(key + 16), GET_32BIT_MSB_FIRST(key + 20), &ourkeys[2]); ourkeys[0].iv0 = GET_32BIT_MSB_FIRST(iv); ourkeys[0].iv1 = GET_32BIT_MSB_FIRST(iv+4); des_cbc3_encrypt(blk, len, ourkeys); smemclr(ourkeys, sizeof(ourkeys)); } static void des_keysetup_xdmauth(const unsigned char *keydata, DESContext *dc) { unsigned char key[8]; int i, nbits, j; unsigned int bits; bits = 0; nbits = 0; |
︙ | ︙ | |||
925 926 927 928 929 930 931 | bits &= ~(0x7F << (nbits - 7)); nbits -= 7; } des_key_setup(GET_32BIT_MSB_FIRST(key), GET_32BIT_MSB_FIRST(key + 4), dc); } | > | | > | | | 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 | bits &= ~(0x7F << (nbits - 7)); nbits -= 7; } des_key_setup(GET_32BIT_MSB_FIRST(key), GET_32BIT_MSB_FIRST(key + 4), dc); } void des_encrypt_xdmauth(const unsigned char *keydata, unsigned char *blk, int len) { DESContext dc; des_keysetup_xdmauth(keydata, &dc); des_cbc_encrypt(blk, len, &dc); } void des_decrypt_xdmauth(const unsigned char *keydata, unsigned char *blk, int len) { DESContext dc; des_keysetup_xdmauth(keydata, &dc); des_cbc_decrypt(blk, len, &dc); } static const struct ssh2_cipher ssh_3des_ssh2 = { des3_make_context, des3_free_context, des3_iv, des3_key, des3_ssh2_encrypt_blk, des3_ssh2_decrypt_blk, "3des-cbc", 8, 168, SSH_CIPHER_IS_CBC, "triple-DES CBC" |
︙ | ︙ | |||
1025 1026 1027 1028 1029 1030 1031 | } const struct ssh_cipher ssh_des = { des_ssh1_make_context, des3_free_context, des_sesskey, des_encrypt_blk, des_decrypt_blk, 8, "single-DES CBC" }; | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 | } const struct ssh_cipher ssh_des = { des_ssh1_make_context, des3_free_context, des_sesskey, des_encrypt_blk, des_decrypt_blk, 8, "single-DES CBC" }; #ifdef TEST_XDM_AUTH /* * Small standalone utility which allows encryption and decryption of * single cipher blocks in the XDM-AUTHORIZATION-1 style. Written * during the rework of X authorisation for connection sharing, to * check the corner case when xa1_firstblock matches but the rest of * the authorisation is bogus. * * Just compile this file on its own with the above ifdef symbol * predefined: gcc -DTEST_XDM_AUTH -o sshdes sshdes.c */ #include <stdlib.h> void *safemalloc(size_t n, size_t size) { return calloc(n, size); } void safefree(void *p) { return free(p); } void smemclr(void *p, size_t size) { memset(p, 0, size); } int main(int argc, char **argv) { unsigned char words[2][8]; unsigned char out[8]; int i, j; memset(words, 0, sizeof(words)); for (i = 0; i < 2; i++) { for (j = 0; j < 8 && argv[i+1][2*j]; j++) { char x[3]; unsigned u; x[0] = argv[i+1][2*j]; x[1] = argv[i+1][2*j+1]; x[2] = 0; sscanf(x, "%02x", &u); words[i][j] = u; } } memcpy(out, words[0], 8); des_decrypt_xdmauth(words[1], out, 8); printf("decrypt(%s,%s) = ", argv[1], argv[2]); for (i = 0; i < 8; i++) printf("%02x", out[i]); printf("\n"); memcpy(out, words[0], 8); des_encrypt_xdmauth(words[1], out, 8); printf("encrypt(%s,%s) = ", argv[1], argv[2]); for (i = 0; i < 8; i++) printf("%02x", out[i]); printf("\n"); } #endif |
Changes to sshdss.c.
︙ | ︙ | |||
16 17 18 19 20 21 22 | len = (bignum_bitcount(b) + 8) / 8; PUT_32BIT(lenbuf, len); SHA_Bytes(s, lenbuf, 4); while (len-- > 0) { lenbuf[0] = bignum_byte(b, len); SHA_Bytes(s, lenbuf, 1); } | | | | > > | 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 | len = (bignum_bitcount(b) + 8) / 8; PUT_32BIT(lenbuf, len); SHA_Bytes(s, lenbuf, 4); while (len-- > 0) { lenbuf[0] = bignum_byte(b, len); SHA_Bytes(s, lenbuf, 1); } smemclr(lenbuf, sizeof(lenbuf)); } static void sha512_mpint(SHA512_State * s, Bignum b) { unsigned char lenbuf[4]; int len; len = (bignum_bitcount(b) + 8) / 8; PUT_32BIT(lenbuf, len); SHA512_Bytes(s, lenbuf, 4); while (len-- > 0) { lenbuf[0] = bignum_byte(b, len); SHA512_Bytes(s, lenbuf, 1); } smemclr(lenbuf, sizeof(lenbuf)); } static void getstring(char **data, int *datalen, char **p, int *length) { *p = NULL; if (*datalen < 4) return; *length = toint(GET_32BIT(*data)); if (*length < 0) return; *datalen -= 4; *data += 4; if (*datalen < *length) return; *p = *data; *data += *length; *datalen -= *length; |
︙ | ︙ | |||
65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 | b = bignum_from_bytes((unsigned char *)p, length); return b; } static Bignum get160(char **data, int *datalen) { Bignum b; b = bignum_from_bytes((unsigned char *)*data, 20); *data += 20; *datalen -= 20; return b; } static void *dss_newkey(char *data, int len) { char *p; int slen; struct dss_key *dss; dss = snew(struct dss_key); | > > > > > < < | > > > > > > > > > | > | > | > | > > | 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 | b = bignum_from_bytes((unsigned char *)p, length); return b; } static Bignum get160(char **data, int *datalen) { Bignum b; if (*datalen < 20) return NULL; b = bignum_from_bytes((unsigned char *)*data, 20); *data += 20; *datalen -= 20; return b; } static void dss_freekey(void *key); /* forward reference */ static void *dss_newkey(char *data, int len) { char *p; int slen; struct dss_key *dss; dss = snew(struct dss_key); getstring(&data, &len, &p, &slen); #ifdef DEBUG_DSS { int i; printf("key:"); for (i = 0; i < len; i++) printf(" %02x", (unsigned char) (data[i])); printf("\n"); } #endif if (!p || slen != 7 || memcmp(p, "ssh-dss", 7)) { sfree(dss); return NULL; } dss->p = getmp(&data, &len); dss->q = getmp(&data, &len); dss->g = getmp(&data, &len); dss->y = getmp(&data, &len); dss->x = NULL; if (!dss->p || !dss->q || !dss->g || !dss->y || !bignum_cmp(dss->q, Zero) || !bignum_cmp(dss->p, Zero)) { /* Invalid key. */ dss_freekey(dss); return NULL; } return dss; } static void dss_freekey(void *key) { struct dss_key *dss = (struct dss_key *) key; if (dss->p) freebn(dss->p); if (dss->q) freebn(dss->q); if (dss->g) freebn(dss->g); if (dss->y) freebn(dss->y); if (dss->x) freebn(dss->x); sfree(dss); } static char *dss_fmtkey(void *key) { struct dss_key *dss = (struct dss_key *) key; char *p; |
︙ | ︙ | |||
245 246 247 248 249 250 251 | if (!p || slen != 7 || memcmp(p, "ssh-dss", 7)) { return 0; } sig += 4, siglen -= 4; /* skip yet another length field */ } r = get160(&sig, &siglen); s = get160(&sig, &siglen); | | > > > > > > > > > > > > > > > > | 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 | if (!p || slen != 7 || memcmp(p, "ssh-dss", 7)) { return 0; } sig += 4, siglen -= 4; /* skip yet another length field */ } r = get160(&sig, &siglen); s = get160(&sig, &siglen); if (!r || !s) { if (r) freebn(r); if (s) freebn(s); return 0; } if (!bignum_cmp(s, Zero)) { freebn(r); freebn(s); return 0; } /* * Step 1. w <- s^-1 mod q. */ w = modinv(s, dss->q); if (!w) { freebn(r); freebn(s); return 0; } /* * Step 2. u1 <- SHA(message) * w mod q. */ SHA_Simple(data, datalen, (unsigned char *)hash); p = hash; slen = 20; |
︙ | ︙ | |||
283 284 285 286 287 288 289 290 291 292 293 294 295 296 | * Step 5. v should now be equal to r. */ ret = !bignum_cmp(v, r); freebn(w); freebn(sha); freebn(gu1p); freebn(yu2p); freebn(gu1yu2p); freebn(v); freebn(r); freebn(s); | > > | 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 | * Step 5. v should now be equal to r. */ ret = !bignum_cmp(v, r); freebn(w); freebn(sha); freebn(u1); freebn(u2); freebn(gu1p); freebn(yu2p); freebn(gu1yu2p); freebn(v); freebn(r); freebn(s); |
︙ | ︙ | |||
373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 | char *hash; int hashlen; SHA_State s; unsigned char digest[20]; Bignum ytest; dss = dss_newkey((char *) pub_blob, pub_len); dss->x = getmp(&pb, &priv_len); /* * Check the obsolete hash in the old DSS key format. */ hashlen = -1; getstring(&pb, &priv_len, &hash, &hashlen); if (hashlen == 20) { | > > > > > > | 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 | char *hash; int hashlen; SHA_State s; unsigned char digest[20]; Bignum ytest; dss = dss_newkey((char *) pub_blob, pub_len); if (!dss) return NULL; dss->x = getmp(&pb, &priv_len); if (!dss->x) { dss_freekey(dss); return NULL; } /* * Check the obsolete hash in the old DSS key format. */ hashlen = -1; getstring(&pb, &priv_len, &hash, &hashlen); if (hashlen == 20) { |
︙ | ︙ | |||
398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 | /* * Now ensure g^x mod p really is y. */ ytest = modpow(dss->g, dss->x, dss->p); if (0 != bignum_cmp(ytest, dss->y)) { dss_freekey(dss); return NULL; } freebn(ytest); return dss; } static void *dss_openssh_createkey(unsigned char **blob, int *len) { char **b = (char **) blob; struct dss_key *dss; dss = snew(struct dss_key); | > < < | | | < < < | | | 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 | /* * Now ensure g^x mod p really is y. */ ytest = modpow(dss->g, dss->x, dss->p); if (0 != bignum_cmp(ytest, dss->y)) { dss_freekey(dss); freebn(ytest); return NULL; } freebn(ytest); return dss; } static void *dss_openssh_createkey(unsigned char **blob, int *len) { char **b = (char **) blob; struct dss_key *dss; dss = snew(struct dss_key); dss->p = getmp(b, len); dss->q = getmp(b, len); dss->g = getmp(b, len); dss->y = getmp(b, len); dss->x = getmp(b, len); if (!dss->p || !dss->q || !dss->g || !dss->y || !dss->x || !bignum_cmp(dss->q, Zero) || !bignum_cmp(dss->p, Zero)) { /* Invalid key. */ dss_freekey(dss); return NULL; } return dss; } static int dss_openssh_fmtkey(void *key, unsigned char *blob, int len) { |
︙ | ︙ | |||
467 468 469 470 471 472 473 474 475 476 477 478 479 480 | static int dss_pubkey_bits(void *blob, int len) { struct dss_key *dss; int ret; dss = dss_newkey((char *) blob, len); ret = bignum_bitcount(dss->p); dss_freekey(dss); return ret; } static unsigned char *dss_sign(void *key, char *data, int datalen, int *siglen) | > > | 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 | static int dss_pubkey_bits(void *blob, int len) { struct dss_key *dss; int ret; dss = dss_newkey((char *) blob, len); if (!dss) return -1; ret = bignum_bitcount(dss->p); dss_freekey(dss); return ret; } static unsigned char *dss_sign(void *key, char *data, int datalen, int *siglen) |
︙ | ︙ | |||
569 570 571 572 573 574 575 | /* * Now hash that digest plus the message hash. */ SHA512_Init(&ss); SHA512_Bytes(&ss, digest512, sizeof(digest512)); SHA512_Bytes(&ss, digest, sizeof(digest)); | > > > | | | | | | | | > > > > > > > | > > > > > > | < > | 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 | /* * Now hash that digest plus the message hash. */ SHA512_Init(&ss); SHA512_Bytes(&ss, digest512, sizeof(digest512)); SHA512_Bytes(&ss, digest, sizeof(digest)); while (1) { SHA512_State ss2 = ss; /* structure copy */ SHA512_Final(&ss2, digest512); smemclr(&ss2, sizeof(ss2)); /* * Now convert the result into a bignum, and reduce it mod q. */ proto_k = bignum_from_bytes(digest512, 64); k = bigmod(proto_k, dss->q); freebn(proto_k); kinv = modinv(k, dss->q); /* k^-1 mod q */ if (!kinv) { /* very unlikely */ freebn(k); /* Perturb the hash to think of a different k. */ SHA512_Bytes(&ss, "x", 1); /* Go round and try again. */ continue; } break; } smemclr(&ss, sizeof(ss)); smemclr(digest512, sizeof(digest512)); /* * Now we have k, so just go ahead and compute the signature. */ gkp = modpow(dss->g, k, dss->p); /* g^k mod p */ r = bigmod(gkp, dss->q); /* r = (g^k mod p) mod q */ freebn(gkp); hash = bignum_from_bytes(digest, 20); hxr = bigmuladd(dss->x, r, hash); /* hash + x*r */ s = modmul(kinv, hxr, dss->q); /* s = k^-1 * (hash + x*r) mod q */ freebn(hxr); freebn(kinv); freebn(k); freebn(hash); /* * Signature blob is * * string "ssh-dss" * string two 20-byte numbers r and s, end to end |
︙ | ︙ |
Changes to sshdssg.c.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | /* * DSS key generation. */ #include "misc.h" #include "ssh.h" int dsa_generate(struct dss_key *key, int bits, progfn_t pfn, void *pfnparam) { Bignum qm1, power, g, h, tmp; int progress; /* * Set up the phase limits for the progress report. We do this * by passing minus the phase number. * * For prime generation: our initial filter finds things | > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | /* * DSS key generation. */ #include "misc.h" #include "ssh.h" int dsa_generate(struct dss_key *key, int bits, progfn_t pfn, void *pfnparam) { Bignum qm1, power, g, h, tmp; unsigned pfirst, qfirst; int progress; /* * Set up the phase limits for the progress report. We do this * by passing minus the phase number. * * For prime generation: our initial filter finds things |
︙ | ︙ | |||
66 67 68 69 70 71 72 73 74 75 | * _after_ generating q, we'll just set it to 0.75. */ pfn(pfnparam, PROGFN_PHASE_EXTENT, 4, 0x2000); pfn(pfnparam, PROGFN_EXP_PHASE, 4, -49152); pfn(pfnparam, PROGFN_READY, 0, 0); /* * Generate q: a prime of length 160. */ | > | | | 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 | * _after_ generating q, we'll just set it to 0.75. */ pfn(pfnparam, PROGFN_PHASE_EXTENT, 4, 0x2000); pfn(pfnparam, PROGFN_EXP_PHASE, 4, -49152); pfn(pfnparam, PROGFN_READY, 0, 0); invent_firstbits(&pfirst, &qfirst); /* * Generate q: a prime of length 160. */ key->q = primegen(160, 2, 2, NULL, 1, pfn, pfnparam, qfirst); /* * Now generate p: a prime of length `bits', such that p-1 is * divisible by q. */ key->p = primegen(bits-160, 2, 2, key->q, 2, pfn, pfnparam, pfirst); /* * Next we need g. Raise 2 to the power (p-1)/q modulo p, and * if that comes out to one then try 3, then 4 and so on. As * soon as we hit a non-unit (and non-zero!) one, that'll do * for g. */ |
︙ | ︙ |
Changes to sshgss.h.
︙ | ︙ | |||
43 44 45 46 47 48 49 | * The free function cleans up the structure, and its associated * libraries (if any). */ struct ssh_gss_liblist { struct ssh_gss_library *libraries; int nlibraries; }; | | | 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 | * The free function cleans up the structure, and its associated * libraries (if any). */ struct ssh_gss_liblist { struct ssh_gss_library *libraries; int nlibraries; }; struct ssh_gss_liblist *ssh_gss_setup(Conf *conf); void ssh_gss_cleanup(struct ssh_gss_liblist *list); /* * Fills in buf with a string describing the GSSAPI mechanism in * use. buf->data is not dynamically allocated. */ typedef Ssh_gss_stat (*t_ssh_gss_indicate_mech)(struct ssh_gss_library *lib, |
︙ | ︙ |
Changes to sshmd5.c.
︙ | ︙ | |||
245 246 247 248 249 250 251 | memset(foo, 0x5C, 64); for (i = 0; i < len && i < 64; i++) foo[i] ^= key[i]; MD5Init(&keys[1]); MD5Update(&keys[1], foo, 64); | | | 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 | memset(foo, 0x5C, 64); for (i = 0; i < len && i < 64; i++) foo[i] ^= key[i]; MD5Init(&keys[1]); MD5Update(&keys[1], foo, 64); smemclr(foo, 64); /* burn the evidence */ } static void hmacmd5_key_16(void *handle, unsigned char *key) { hmacmd5_key(handle, key, 16); } |
︙ | ︙ | |||
308 309 310 311 312 313 314 | } static void hmacmd5_do_hmac_ssh(void *handle, unsigned char const *blk, int len, unsigned long seq, unsigned char *hmac) { unsigned char seqbuf[16]; | < < < < | | 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 | } static void hmacmd5_do_hmac_ssh(void *handle, unsigned char const *blk, int len, unsigned long seq, unsigned char *hmac) { unsigned char seqbuf[16]; PUT_32BIT_MSB_FIRST(seqbuf, seq); hmacmd5_do_hmac_internal(handle, seqbuf, 4, blk, len, hmac); } static void hmacmd5_generate(void *handle, unsigned char *blk, int len, unsigned long seq) { hmacmd5_do_hmac_ssh(handle, blk, len, seq, blk + len); |
︙ | ︙ |
Changes to sshnogss.c.
1 2 3 4 5 | #include "putty.h" #ifndef NO_GSSAPI /* For platforms not supporting GSSAPI */ | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 | #include "putty.h" #ifndef NO_GSSAPI /* For platforms not supporting GSSAPI */ struct ssh_gss_liblist *ssh_gss_setup(Conf *conf) { struct ssh_gss_liblist *list = snew(struct ssh_gss_liblist *); list->libraries = NULL; list->nlibraries = 0; return list; } |
︙ | ︙ |
Changes to sshprime.c.
︙ | ︙ | |||
119 120 121 122 123 124 125 | * list.append(i) * for j in range(i,n,i): z[j] = 0 * return list * list = sieve(65535) * for i in list[1:]: sys.stdout.write("%d," % i) */ static const unsigned short primes[] = { | | | | < | | < | | < | | | < | < | | | < | < | < | | | < | < | | | < | < | < | | | < | < | < | | | < | < | | | < | < | < | | | < | < | < | | | < | < | | | < | < | < | | | < | < | < | | | < | < | < | | | < | < | | | < | < | < | | | < | < | | | < | < | < | | | < | < | < | | | < | < | | | < | < | < | | | < | < | < | | | < | < | | | < | < < | | | < | < | | | < | | | < | < | < | | | < | < | | | < | < | | | < | < | | | < | < | | | < | < | | | < | < | | | < | < | | | < | < | | | < | < | | | < | < | < | | | < | | | < | < | | | < | < | | | < | < | | | < | < | | | < | < | | | < | < | | | < | < | | | < | < | | | < | < | | | < | < | | | < | < | | | < | < | | | < | < | | | < | < | | | < | < | | | < | < | | | < | < | | | < | < | | | < | < | | | < | < | | | < | < | | | < | < | | | < | < | | | < | < | | | < | < | | | < | < | | | < | < | | | < | < | | | < | < | | | < | < | | | < | < | | | < | < | | | < | < | | | < | < | | | < | < | | | < | < | < | | | < | | | < | < | | | < | < | | | < | < | | | < | < | | | < | < | | | < | < | | | < | < | | | < | < | | | < | < | | | < | < | | | < | < | | | < | < | | | < | < | | | < | < | | | < | < | | | < | < | | | < | < | | | < | < | | | < | < | | | < | < | | | < | < | | | < | < | | | < | < | | | < | < | | | < | < | | | < | < | | | < | < | | | < | < | | | < | < | | | < | < | | | < | < | | | < | < | | | < | < | | | < | < | | | < | < | | | < | < | | | < | < | | | < | < | | | < | < | | | < | < | | | < | < | | | < | < | | | < | < | | | < | < | | | < | < | < | | | < | | | < | < | | | < | < | | | < | < | | | < | < | | | < | < | | | < | < | | | < | < | | | < | < | | | < | < | | | < | < | | | < | < | | | < | < | | | < | < | < | | | < | | | < | < | | | < | < | | | < | < | | | < | < | | | < | < | | | < | < | | | < | < | | | < | < | | | < | < | | | < | < | | | < | < | | | < | < | | | < | < | | | < | < | | | < | < | | | < | < | | | < | < | | | < | < | | | < | < | | | < | < | | | < | < | | | < | < | < | | | < | | | < | < | | | < | < | | | < | < | | | < | < | | | < | < | | | < | < | | | < | < | | | < | < | | | < | < | | | < | < | | | < | < | | | < | < | | | < | < | | | > > > > > > > > | | > > > > | > > | | 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 | * list.append(i) * for j in range(i,n,i): z[j] = 0 * return list * list = sieve(65535) * for i in list[1:]: sys.stdout.write("%d," % i) */ static const unsigned short primes[] = { 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419, 421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509, 521, 523, 541, 547, 557, 563, 569, 571, 577, 587, 593, 599, 601, 607, 613, 617, 619, 631, 641, 643, 647, 653, 659, 661, 673, 677, 683, 691, 701, 709, 719, 727, 733, 739, 743, 751, 757, 761, 769, 773, 787, 797, 809, 811, 821, 823, 827, 829, 839, 853, 857, 859, 863, 877, 881, 883, 887, 907, 911, 919, 929, 937, 941, 947, 953, 967, 971, 977, 983, 991, 997, 1009, 1013, 1019, 1021, 1031, 1033, 1039, 1049, 1051, 1061, 1063, 1069, 1087, 1091, 1093, 1097, 1103, 1109, 1117, 1123, 1129, 1151, 1153, 1163, 1171, 1181, 1187, 1193, 1201, 1213, 1217, 1223, 1229, 1231, 1237, 1249, 1259, 1277, 1279, 1283, 1289, 1291, 1297, 1301, 1303, 1307, 1319, 1321, 1327, 1361, 1367, 1373, 1381, 1399, 1409, 1423, 1427, 1429, 1433, 1439, 1447, 1451, 1453, 1459, 1471, 1481, 1483, 1487, 1489, 1493, 1499, 1511, 1523, 1531, 1543, 1549, 1553, 1559, 1567, 1571, 1579, 1583, 1597, 1601, 1607, 1609, 1613, 1619, 1621, 1627, 1637, 1657, 1663, 1667, 1669, 1693, 1697, 1699, 1709, 1721, 1723, 1733, 1741, 1747, 1753, 1759, 1777, 1783, 1787, 1789, 1801, 1811, 1823, 1831, 1847, 1861, 1867, 1871, 1873, 1877, 1879, 1889, 1901, 1907, 1913, 1931, 1933, 1949, 1951, 1973, 1979, 1987, 1993, 1997, 1999, 2003, 2011, 2017, 2027, 2029, 2039, 2053, 2063, 2069, 2081, 2083, 2087, 2089, 2099, 2111, 2113, 2129, 2131, 2137, 2141, 2143, 2153, 2161, 2179, 2203, 2207, 2213, 2221, 2237, 2239, 2243, 2251, 2267, 2269, 2273, 2281, 2287, 2293, 2297, 2309, 2311, 2333, 2339, 2341, 2347, 2351, 2357, 2371, 2377, 2381, 2383, 2389, 2393, 2399, 2411, 2417, 2423, 2437, 2441, 2447, 2459, 2467, 2473, 2477, 2503, 2521, 2531, 2539, 2543, 2549, 2551, 2557, 2579, 2591, 2593, 2609, 2617, 2621, 2633, 2647, 2657, 2659, 2663, 2671, 2677, 2683, 2687, 2689, 2693, 2699, 2707, 2711, 2713, 2719, 2729, 2731, 2741, 2749, 2753, 2767, 2777, 2789, 2791, 2797, 2801, 2803, 2819, 2833, 2837, 2843, 2851, 2857, 2861, 2879, 2887, 2897, 2903, 2909, 2917, 2927, 2939, 2953, 2957, 2963, 2969, 2971, 2999, 3001, 3011, 3019, 3023, 3037, 3041, 3049, 3061, 3067, 3079, 3083, 3089, 3109, 3119, 3121, 3137, 3163, 3167, 3169, 3181, 3187, 3191, 3203, 3209, 3217, 3221, 3229, 3251, 3253, 3257, 3259, 3271, 3299, 3301, 3307, 3313, 3319, 3323, 3329, 3331, 3343, 3347, 3359, 3361, 3371, 3373, 3389, 3391, 3407, 3413, 3433, 3449, 3457, 3461, 3463, 3467, 3469, 3491, 3499, 3511, 3517, 3527, 3529, 3533, 3539, 3541, 3547, 3557, 3559, 3571, 3581, 3583, 3593, 3607, 3613, 3617, 3623, 3631, 3637, 3643, 3659, 3671, 3673, 3677, 3691, 3697, 3701, 3709, 3719, 3727, 3733, 3739, 3761, 3767, 3769, 3779, 3793, 3797, 3803, 3821, 3823, 3833, 3847, 3851, 3853, 3863, 3877, 3881, 3889, 3907, 3911, 3917, 3919, 3923, 3929, 3931, 3943, 3947, 3967, 3989, 4001, 4003, 4007, 4013, 4019, 4021, 4027, 4049, 4051, 4057, 4073, 4079, 4091, 4093, 4099, 4111, 4127, 4129, 4133, 4139, 4153, 4157, 4159, 4177, 4201, 4211, 4217, 4219, 4229, 4231, 4241, 4243, 4253, 4259, 4261, 4271, 4273, 4283, 4289, 4297, 4327, 4337, 4339, 4349, 4357, 4363, 4373, 4391, 4397, 4409, 4421, 4423, 4441, 4447, 4451, 4457, 4463, 4481, 4483, 4493, 4507, 4513, 4517, 4519, 4523, 4547, 4549, 4561, 4567, 4583, 4591, 4597, 4603, 4621, 4637, 4639, 4643, 4649, 4651, 4657, 4663, 4673, 4679, 4691, 4703, 4721, 4723, 4729, 4733, 4751, 4759, 4783, 4787, 4789, 4793, 4799, 4801, 4813, 4817, 4831, 4861, 4871, 4877, 4889, 4903, 4909, 4919, 4931, 4933, 4937, 4943, 4951, 4957, 4967, 4969, 4973, 4987, 4993, 4999, 5003, 5009, 5011, 5021, 5023, 5039, 5051, 5059, 5077, 5081, 5087, 5099, 5101, 5107, 5113, 5119, 5147, 5153, 5167, 5171, 5179, 5189, 5197, 5209, 5227, 5231, 5233, 5237, 5261, 5273, 5279, 5281, 5297, 5303, 5309, 5323, 5333, 5347, 5351, 5381, 5387, 5393, 5399, 5407, 5413, 5417, 5419, 5431, 5437, 5441, 5443, 5449, 5471, 5477, 5479, 5483, 5501, 5503, 5507, 5519, 5521, 5527, 5531, 5557, 5563, 5569, 5573, 5581, 5591, 5623, 5639, 5641, 5647, 5651, 5653, 5657, 5659, 5669, 5683, 5689, 5693, 5701, 5711, 5717, 5737, 5741, 5743, 5749, 5779, 5783, 5791, 5801, 5807, 5813, 5821, 5827, 5839, 5843, 5849, 5851, 5857, 5861, 5867, 5869, 5879, 5881, 5897, 5903, 5923, 5927, 5939, 5953, 5981, 5987, 6007, 6011, 6029, 6037, 6043, 6047, 6053, 6067, 6073, 6079, 6089, 6091, 6101, 6113, 6121, 6131, 6133, 6143, 6151, 6163, 6173, 6197, 6199, 6203, 6211, 6217, 6221, 6229, 6247, 6257, 6263, 6269, 6271, 6277, 6287, 6299, 6301, 6311, 6317, 6323, 6329, 6337, 6343, 6353, 6359, 6361, 6367, 6373, 6379, 6389, 6397, 6421, 6427, 6449, 6451, 6469, 6473, 6481, 6491, 6521, 6529, 6547, 6551, 6553, 6563, 6569, 6571, 6577, 6581, 6599, 6607, 6619, 6637, 6653, 6659, 6661, 6673, 6679, 6689, 6691, 6701, 6703, 6709, 6719, 6733, 6737, 6761, 6763, 6779, 6781, 6791, 6793, 6803, 6823, 6827, 6829, 6833, 6841, 6857, 6863, 6869, 6871, 6883, 6899, 6907, 6911, 6917, 6947, 6949, 6959, 6961, 6967, 6971, 6977, 6983, 6991, 6997, 7001, 7013, 7019, 7027, 7039, 7043, 7057, 7069, 7079, 7103, 7109, 7121, 7127, 7129, 7151, 7159, 7177, 7187, 7193, 7207, 7211, 7213, 7219, 7229, 7237, 7243, 7247, 7253, 7283, 7297, 7307, 7309, 7321, 7331, 7333, 7349, 7351, 7369, 7393, 7411, 7417, 7433, 7451, 7457, 7459, 7477, 7481, 7487, 7489, 7499, 7507, 7517, 7523, 7529, 7537, 7541, 7547, 7549, 7559, 7561, 7573, 7577, 7583, 7589, 7591, 7603, 7607, 7621, 7639, 7643, 7649, 7669, 7673, 7681, 7687, 7691, 7699, 7703, 7717, 7723, 7727, 7741, 7753, 7757, 7759, 7789, 7793, 7817, 7823, 7829, 7841, 7853, 7867, 7873, 7877, 7879, 7883, 7901, 7907, 7919, 7927, 7933, 7937, 7949, 7951, 7963, 7993, 8009, 8011, 8017, 8039, 8053, 8059, 8069, 8081, 8087, 8089, 8093, 8101, 8111, 8117, 8123, 8147, 8161, 8167, 8171, 8179, 8191, 8209, 8219, 8221, 8231, 8233, 8237, 8243, 8263, 8269, 8273, 8287, 8291, 8293, 8297, 8311, 8317, 8329, 8353, 8363, 8369, 8377, 8387, 8389, 8419, 8423, 8429, 8431, 8443, 8447, 8461, 8467, 8501, 8513, 8521, 8527, 8537, 8539, 8543, 8563, 8573, 8581, 8597, 8599, 8609, 8623, 8627, 8629, 8641, 8647, 8663, 8669, 8677, 8681, 8689, 8693, 8699, 8707, 8713, 8719, 8731, 8737, 8741, 8747, 8753, 8761, 8779, 8783, 8803, 8807, 8819, 8821, 8831, 8837, 8839, 8849, 8861, 8863, 8867, 8887, 8893, 8923, 8929, 8933, 8941, 8951, 8963, 8969, 8971, 8999, 9001, 9007, 9011, 9013, 9029, 9041, 9043, 9049, 9059, 9067, 9091, 9103, 9109, 9127, 9133, 9137, 9151, 9157, 9161, 9173, 9181, 9187, 9199, 9203, 9209, 9221, 9227, 9239, 9241, 9257, 9277, 9281, 9283, 9293, 9311, 9319, 9323, 9337, 9341, 9343, 9349, 9371, 9377, 9391, 9397, 9403, 9413, 9419, 9421, 9431, 9433, 9437, 9439, 9461, 9463, 9467, 9473, 9479, 9491, 9497, 9511, 9521, 9533, 9539, 9547, 9551, 9587, 9601, 9613, 9619, 9623, 9629, 9631, 9643, 9649, 9661, 9677, 9679, 9689, 9697, 9719, 9721, 9733, 9739, 9743, 9749, 9767, 9769, 9781, 9787, 9791, 9803, 9811, 9817, 9829, 9833, 9839, 9851, 9857, 9859, 9871, 9883, 9887, 9901, 9907, 9923, 9929, 9931, 9941, 9949, 9967, 9973, 10007, 10009, 10037, 10039, 10061, 10067, 10069, 10079, 10091, 10093, 10099, 10103, 10111, 10133, 10139, 10141, 10151, 10159, 10163, 10169, 10177, 10181, 10193, 10211, 10223, 10243, 10247, 10253, 10259, 10267, 10271, 10273, 10289, 10301, 10303, 10313, 10321, 10331, 10333, 10337, 10343, 10357, 10369, 10391, 10399, 10427, 10429, 10433, 10453, 10457, 10459, 10463, 10477, 10487, 10499, 10501, 10513, 10529, 10531, 10559, 10567, 10589, 10597, 10601, 10607, 10613, 10627, 10631, 10639, 10651, 10657, 10663, 10667, 10687, 10691, 10709, 10711, 10723, 10729, 10733, 10739, 10753, 10771, 10781, 10789, 10799, 10831, 10837, 10847, 10853, 10859, 10861, 10867, 10883, 10889, 10891, 10903, 10909, 10937, 10939, 10949, 10957, 10973, 10979, 10987, 10993, 11003, 11027, 11047, 11057, 11059, 11069, 11071, 11083, 11087, 11093, 11113, 11117, 11119, 11131, 11149, 11159, 11161, 11171, 11173, 11177, 11197, 11213, 11239, 11243, 11251, 11257, 11261, 11273, 11279, 11287, 11299, 11311, 11317, 11321, 11329, 11351, 11353, 11369, 11383, 11393, 11399, 11411, 11423, 11437, 11443, 11447, 11467, 11471, 11483, 11489, 11491, 11497, 11503, 11519, 11527, 11549, 11551, 11579, 11587, 11593, 11597, 11617, 11621, 11633, 11657, 11677, 11681, 11689, 11699, 11701, 11717, 11719, 11731, 11743, 11777, 11779, 11783, 11789, 11801, 11807, 11813, 11821, 11827, 11831, 11833, 11839, 11863, 11867, 11887, 11897, 11903, 11909, 11923, 11927, 11933, 11939, 11941, 11953, 11959, 11969, 11971, 11981, 11987, 12007, 12011, 12037, 12041, 12043, 12049, 12071, 12073, 12097, 12101, 12107, 12109, 12113, 12119, 12143, 12149, 12157, 12161, 12163, 12197, 12203, 12211, 12227, 12239, 12241, 12251, 12253, 12263, 12269, 12277, 12281, 12289, 12301, 12323, 12329, 12343, 12347, 12373, 12377, 12379, 12391, 12401, 12409, 12413, 12421, 12433, 12437, 12451, 12457, 12473, 12479, 12487, 12491, 12497, 12503, 12511, 12517, 12527, 12539, 12541, 12547, 12553, 12569, 12577, 12583, 12589, 12601, 12611, 12613, 12619, 12637, 12641, 12647, 12653, 12659, 12671, 12689, 12697, 12703, 12713, 12721, 12739, 12743, 12757, 12763, 12781, 12791, 12799, 12809, 12821, 12823, 12829, 12841, 12853, 12889, 12893, 12899, 12907, 12911, 12917, 12919, 12923, 12941, 12953, 12959, 12967, 12973, 12979, 12983, 13001, 13003, 13007, 13009, 13033, 13037, 13043, 13049, 13063, 13093, 13099, 13103, 13109, 13121, 13127, 13147, 13151, 13159, 13163, 13171, 13177, 13183, 13187, 13217, 13219, 13229, 13241, 13249, 13259, 13267, 13291, 13297, 13309, 13313, 13327, 13331, 13337, 13339, 13367, 13381, 13397, 13399, 13411, 13417, 13421, 13441, 13451, 13457, 13463, 13469, 13477, 13487, 13499, 13513, 13523, 13537, 13553, 13567, 13577, 13591, 13597, 13613, 13619, 13627, 13633, 13649, 13669, 13679, 13681, 13687, 13691, 13693, 13697, 13709, 13711, 13721, 13723, 13729, 13751, 13757, 13759, 13763, 13781, 13789, 13799, 13807, 13829, 13831, 13841, 13859, 13873, 13877, 13879, 13883, 13901, 13903, 13907, 13913, 13921, 13931, 13933, 13963, 13967, 13997, 13999, 14009, 14011, 14029, 14033, 14051, 14057, 14071, 14081, 14083, 14087, 14107, 14143, 14149, 14153, 14159, 14173, 14177, 14197, 14207, 14221, 14243, 14249, 14251, 14281, 14293, 14303, 14321, 14323, 14327, 14341, 14347, 14369, 14387, 14389, 14401, 14407, 14411, 14419, 14423, 14431, 14437, 14447, 14449, 14461, 14479, 14489, 14503, 14519, 14533, 14537, 14543, 14549, 14551, 14557, 14561, 14563, 14591, 14593, 14621, 14627, 14629, 14633, 14639, 14653, 14657, 14669, 14683, 14699, 14713, 14717, 14723, 14731, 14737, 14741, 14747, 14753, 14759, 14767, 14771, 14779, 14783, 14797, 14813, 14821, 14827, 14831, 14843, 14851, 14867, 14869, 14879, 14887, 14891, 14897, 14923, 14929, 14939, 14947, 14951, 14957, 14969, 14983, 15013, 15017, 15031, 15053, 15061, 15073, 15077, 15083, 15091, 15101, 15107, 15121, 15131, 15137, 15139, 15149, 15161, 15173, 15187, 15193, 15199, 15217, 15227, 15233, 15241, 15259, 15263, 15269, 15271, 15277, 15287, 15289, 15299, 15307, 15313, 15319, 15329, 15331, 15349, 15359, 15361, 15373, 15377, 15383, 15391, 15401, 15413, 15427, 15439, 15443, 15451, 15461, 15467, 15473, 15493, 15497, 15511, 15527, 15541, 15551, 15559, 15569, 15581, 15583, 15601, 15607, 15619, 15629, 15641, 15643, 15647, 15649, 15661, 15667, 15671, 15679, 15683, 15727, 15731, 15733, 15737, 15739, 15749, 15761, 15767, 15773, 15787, 15791, 15797, 15803, 15809, 15817, 15823, 15859, 15877, 15881, 15887, 15889, 15901, 15907, 15913, 15919, 15923, 15937, 15959, 15971, 15973, 15991, 16001, 16007, 16033, 16057, 16061, 16063, 16067, 16069, 16073, 16087, 16091, 16097, 16103, 16111, 16127, 16139, 16141, 16183, 16187, 16189, 16193, 16217, 16223, 16229, 16231, 16249, 16253, 16267, 16273, 16301, 16319, 16333, 16339, 16349, 16361, 16363, 16369, 16381, 16411, 16417, 16421, 16427, 16433, 16447, 16451, 16453, 16477, 16481, 16487, 16493, 16519, 16529, 16547, 16553, 16561, 16567, 16573, 16603, 16607, 16619, 16631, 16633, 16649, 16651, 16657, 16661, 16673, 16691, 16693, 16699, 16703, 16729, 16741, 16747, 16759, 16763, 16787, 16811, 16823, 16829, 16831, 16843, 16871, 16879, 16883, 16889, 16901, 16903, 16921, 16927, 16931, 16937, 16943, 16963, 16979, 16981, 16987, 16993, 17011, 17021, 17027, 17029, 17033, 17041, 17047, 17053, 17077, 17093, 17099, 17107, 17117, 17123, 17137, 17159, 17167, 17183, 17189, 17191, 17203, 17207, 17209, 17231, 17239, 17257, 17291, 17293, 17299, 17317, 17321, 17327, 17333, 17341, 17351, 17359, 17377, 17383, 17387, 17389, 17393, 17401, 17417, 17419, 17431, 17443, 17449, 17467, 17471, 17477, 17483, 17489, 17491, 17497, 17509, 17519, 17539, 17551, 17569, 17573, 17579, 17581, 17597, 17599, 17609, 17623, 17627, 17657, 17659, 17669, 17681, 17683, 17707, 17713, 17729, 17737, 17747, 17749, 17761, 17783, 17789, 17791, 17807, 17827, 17837, 17839, 17851, 17863, 17881, 17891, 17903, 17909, 17911, 17921, 17923, 17929, 17939, 17957, 17959, 17971, 17977, 17981, 17987, 17989, 18013, 18041, 18043, 18047, 18049, 18059, 18061, 18077, 18089, 18097, 18119, 18121, 18127, 18131, 18133, 18143, 18149, 18169, 18181, 18191, 18199, 18211, 18217, 18223, 18229, 18233, 18251, 18253, 18257, 18269, 18287, 18289, 18301, 18307, 18311, 18313, 18329, 18341, 18353, 18367, 18371, 18379, 18397, 18401, 18413, 18427, 18433, 18439, 18443, 18451, 18457, 18461, 18481, 18493, 18503, 18517, 18521, 18523, 18539, 18541, 18553, 18583, 18587, 18593, 18617, 18637, 18661, 18671, 18679, 18691, 18701, 18713, 18719, 18731, 18743, 18749, 18757, 18773, 18787, 18793, 18797, 18803, 18839, 18859, 18869, 18899, 18911, 18913, 18917, 18919, 18947, 18959, 18973, 18979, 19001, 19009, 19013, 19031, 19037, 19051, 19069, 19073, 19079, 19081, 19087, 19121, 19139, 19141, 19157, 19163, 19181, 19183, 19207, 19211, 19213, 19219, 19231, 19237, 19249, 19259, 19267, 19273, 19289, 19301, 19309, 19319, 19333, 19373, 19379, 19381, 19387, 19391, 19403, 19417, 19421, 19423, 19427, 19429, 19433, 19441, 19447, 19457, 19463, 19469, 19471, 19477, 19483, 19489, 19501, 19507, 19531, 19541, 19543, 19553, 19559, 19571, 19577, 19583, 19597, 19603, 19609, 19661, 19681, 19687, 19697, 19699, 19709, 19717, 19727, 19739, 19751, 19753, 19759, 19763, 19777, 19793, 19801, 19813, 19819, 19841, 19843, 19853, 19861, 19867, 19889, 19891, 19913, 19919, 19927, 19937, 19949, 19961, 19963, 19973, 19979, 19991, 19993, 19997, 20011, 20021, 20023, 20029, 20047, 20051, 20063, 20071, 20089, 20101, 20107, 20113, 20117, 20123, 20129, 20143, 20147, 20149, 20161, 20173, 20177, 20183, 20201, 20219, 20231, 20233, 20249, 20261, 20269, 20287, 20297, 20323, 20327, 20333, 20341, 20347, 20353, 20357, 20359, 20369, 20389, 20393, 20399, 20407, 20411, 20431, 20441, 20443, 20477, 20479, 20483, 20507, 20509, 20521, 20533, 20543, 20549, 20551, 20563, 20593, 20599, 20611, 20627, 20639, 20641, 20663, 20681, 20693, 20707, 20717, 20719, 20731, 20743, 20747, 20749, 20753, 20759, 20771, 20773, 20789, 20807, 20809, 20849, 20857, 20873, 20879, 20887, 20897, 20899, 20903, 20921, 20929, 20939, 20947, 20959, 20963, 20981, 20983, 21001, 21011, 21013, 21017, 21019, 21023, 21031, 21059, 21061, 21067, 21089, 21101, 21107, 21121, 21139, 21143, 21149, 21157, 21163, 21169, 21179, 21187, 21191, 21193, 21211, 21221, 21227, 21247, 21269, 21277, 21283, 21313, 21317, 21319, 21323, 21341, 21347, 21377, 21379, 21383, 21391, 21397, 21401, 21407, 21419, 21433, 21467, 21481, 21487, 21491, 21493, 21499, 21503, 21517, 21521, 21523, 21529, 21557, 21559, 21563, 21569, 21577, 21587, 21589, 21599, 21601, 21611, 21613, 21617, 21647, 21649, 21661, 21673, 21683, 21701, 21713, 21727, 21737, 21739, 21751, 21757, 21767, 21773, 21787, 21799, 21803, 21817, 21821, 21839, 21841, 21851, 21859, 21863, 21871, 21881, 21893, 21911, 21929, 21937, 21943, 21961, 21977, 21991, 21997, 22003, 22013, 22027, 22031, 22037, 22039, 22051, 22063, 22067, 22073, 22079, 22091, 22093, 22109, 22111, 22123, 22129, 22133, 22147, 22153, 22157, 22159, 22171, 22189, 22193, 22229, 22247, 22259, 22271, 22273, 22277, 22279, 22283, 22291, 22303, 22307, 22343, 22349, 22367, 22369, 22381, 22391, 22397, 22409, 22433, 22441, 22447, 22453, 22469, 22481, 22483, 22501, 22511, 22531, 22541, 22543, 22549, 22567, 22571, 22573, 22613, 22619, 22621, 22637, 22639, 22643, 22651, 22669, 22679, 22691, 22697, 22699, 22709, 22717, 22721, 22727, 22739, 22741, 22751, 22769, 22777, 22783, 22787, 22807, 22811, 22817, 22853, 22859, 22861, 22871, 22877, 22901, 22907, 22921, 22937, 22943, 22961, 22963, 22973, 22993, 23003, 23011, 23017, 23021, 23027, 23029, 23039, 23041, 23053, 23057, 23059, 23063, 23071, 23081, 23087, 23099, 23117, 23131, 23143, 23159, 23167, 23173, 23189, 23197, 23201, 23203, 23209, 23227, 23251, 23269, 23279, 23291, 23293, 23297, 23311, 23321, 23327, 23333, 23339, 23357, 23369, 23371, 23399, 23417, 23431, 23447, 23459, 23473, 23497, 23509, 23531, 23537, 23539, 23549, 23557, 23561, 23563, 23567, 23581, 23593, 23599, 23603, 23609, 23623, 23627, 23629, 23633, 23663, 23669, 23671, 23677, 23687, 23689, 23719, 23741, 23743, 23747, 23753, 23761, 23767, 23773, 23789, 23801, 23813, 23819, 23827, 23831, 23833, 23857, 23869, 23873, 23879, 23887, 23893, 23899, 23909, 23911, 23917, 23929, 23957, 23971, 23977, 23981, 23993, 24001, 24007, 24019, 24023, 24029, 24043, 24049, 24061, 24071, 24077, 24083, 24091, 24097, 24103, 24107, 24109, 24113, 24121, 24133, 24137, 24151, 24169, 24179, 24181, 24197, 24203, 24223, 24229, 24239, 24247, 24251, 24281, 24317, 24329, 24337, 24359, 24371, 24373, 24379, 24391, 24407, 24413, 24419, 24421, 24439, 24443, 24469, 24473, 24481, 24499, 24509, 24517, 24527, 24533, 24547, 24551, 24571, 24593, 24611, 24623, 24631, 24659, 24671, 24677, 24683, 24691, 24697, 24709, 24733, 24749, 24763, 24767, 24781, 24793, 24799, 24809, 24821, 24841, 24847, 24851, 24859, 24877, 24889, 24907, 24917, 24919, 24923, 24943, 24953, 24967, 24971, 24977, 24979, 24989, 25013, 25031, 25033, 25037, 25057, 25073, 25087, 25097, 25111, 25117, 25121, 25127, 25147, 25153, 25163, 25169, 25171, 25183, 25189, 25219, 25229, 25237, 25243, 25247, 25253, 25261, 25301, 25303, 25307, 25309, 25321, 25339, 25343, 25349, 25357, 25367, 25373, 25391, 25409, 25411, 25423, 25439, 25447, 25453, 25457, 25463, 25469, 25471, 25523, 25537, 25541, 25561, 25577, 25579, 25583, 25589, 25601, 25603, 25609, 25621, 25633, 25639, 25643, 25657, 25667, 25673, 25679, 25693, 25703, 25717, 25733, 25741, 25747, 25759, 25763, 25771, 25793, 25799, 25801, 25819, 25841, 25847, 25849, 25867, 25873, 25889, 25903, 25913, 25919, 25931, 25933, 25939, 25943, 25951, 25969, 25981, 25997, 25999, 26003, 26017, 26021, 26029, 26041, 26053, 26083, 26099, 26107, 26111, 26113, 26119, 26141, 26153, 26161, 26171, 26177, 26183, 26189, 26203, 26209, 26227, 26237, 26249, 26251, 26261, 26263, 26267, 26293, 26297, 26309, 26317, 26321, 26339, 26347, 26357, 26371, 26387, 26393, 26399, 26407, 26417, 26423, 26431, 26437, 26449, 26459, 26479, 26489, 26497, 26501, 26513, 26539, 26557, 26561, 26573, 26591, 26597, 26627, 26633, 26641, 26647, 26669, 26681, 26683, 26687, 26693, 26699, 26701, 26711, 26713, 26717, 26723, 26729, 26731, 26737, 26759, 26777, 26783, 26801, 26813, 26821, 26833, 26839, 26849, 26861, 26863, 26879, 26881, 26891, 26893, 26903, 26921, 26927, 26947, 26951, 26953, 26959, 26981, 26987, 26993, 27011, 27017, 27031, 27043, 27059, 27061, 27067, 27073, 27077, 27091, 27103, 27107, 27109, 27127, 27143, 27179, 27191, 27197, 27211, 27239, 27241, 27253, 27259, 27271, 27277, 27281, 27283, 27299, 27329, 27337, 27361, 27367, 27397, 27407, 27409, 27427, 27431, 27437, 27449, 27457, 27479, 27481, 27487, 27509, 27527, 27529, 27539, 27541, 27551, 27581, 27583, 27611, 27617, 27631, 27647, 27653, 27673, 27689, 27691, 27697, 27701, 27733, 27737, 27739, 27743, 27749, 27751, 27763, 27767, 27773, 27779, 27791, 27793, 27799, 27803, 27809, 27817, 27823, 27827, 27847, 27851, 27883, 27893, 27901, 27917, 27919, 27941, 27943, 27947, 27953, 27961, 27967, 27983, 27997, 28001, 28019, 28027, 28031, 28051, 28057, 28069, 28081, 28087, 28097, 28099, 28109, 28111, 28123, 28151, 28163, 28181, 28183, 28201, 28211, 28219, 28229, 28277, 28279, 28283, 28289, 28297, 28307, 28309, 28319, 28349, 28351, 28387, 28393, 28403, 28409, 28411, 28429, 28433, 28439, 28447, 28463, 28477, 28493, 28499, 28513, 28517, 28537, 28541, 28547, 28549, 28559, 28571, 28573, 28579, 28591, 28597, 28603, 28607, 28619, 28621, 28627, 28631, 28643, 28649, 28657, 28661, 28663, 28669, 28687, 28697, 28703, 28711, 28723, 28729, 28751, 28753, 28759, 28771, 28789, 28793, 28807, 28813, 28817, 28837, 28843, 28859, 28867, 28871, 28879, 28901, 28909, 28921, 28927, 28933, 28949, 28961, 28979, 29009, 29017, 29021, 29023, 29027, 29033, 29059, 29063, 29077, 29101, 29123, 29129, 29131, 29137, 29147, 29153, 29167, 29173, 29179, 29191, 29201, 29207, 29209, 29221, 29231, 29243, 29251, 29269, 29287, 29297, 29303, 29311, 29327, 29333, 29339, 29347, 29363, 29383, 29387, 29389, 29399, 29401, 29411, 29423, 29429, 29437, 29443, 29453, 29473, 29483, 29501, 29527, 29531, 29537, 29567, 29569, 29573, 29581, 29587, 29599, 29611, 29629, 29633, 29641, 29663, 29669, 29671, 29683, 29717, 29723, 29741, 29753, 29759, 29761, 29789, 29803, 29819, 29833, 29837, 29851, 29863, 29867, 29873, 29879, 29881, 29917, 29921, 29927, 29947, 29959, 29983, 29989, 30011, 30013, 30029, 30047, 30059, 30071, 30089, 30091, 30097, 30103, 30109, 30113, 30119, 30133, 30137, 30139, 30161, 30169, 30181, 30187, 30197, 30203, 30211, 30223, 30241, 30253, 30259, 30269, 30271, 30293, 30307, 30313, 30319, 30323, 30341, 30347, 30367, 30389, 30391, 30403, 30427, 30431, 30449, 30467, 30469, 30491, 30493, 30497, 30509, 30517, 30529, 30539, 30553, 30557, 30559, 30577, 30593, 30631, 30637, 30643, 30649, 30661, 30671, 30677, 30689, 30697, 30703, 30707, 30713, 30727, 30757, 30763, 30773, 30781, 30803, 30809, 30817, 30829, 30839, 30841, 30851, 30853, 30859, 30869, 30871, 30881, 30893, 30911, 30931, 30937, 30941, 30949, 30971, 30977, 30983, 31013, 31019, 31033, 31039, 31051, 31063, 31069, 31079, 31081, 31091, 31121, 31123, 31139, 31147, 31151, 31153, 31159, 31177, 31181, 31183, 31189, 31193, 31219, 31223, 31231, 31237, 31247, 31249, 31253, 31259, 31267, 31271, 31277, 31307, 31319, 31321, 31327, 31333, 31337, 31357, 31379, 31387, 31391, 31393, 31397, 31469, 31477, 31481, 31489, 31511, 31513, 31517, 31531, 31541, 31543, 31547, 31567, 31573, 31583, 31601, 31607, 31627, 31643, 31649, 31657, 31663, 31667, 31687, 31699, 31721, 31723, 31727, 31729, 31741, 31751, 31769, 31771, 31793, 31799, 31817, 31847, 31849, 31859, 31873, 31883, 31891, 31907, 31957, 31963, 31973, 31981, 31991, 32003, 32009, 32027, 32029, 32051, 32057, 32059, 32063, 32069, 32077, 32083, 32089, 32099, 32117, 32119, 32141, 32143, 32159, 32173, 32183, 32189, 32191, 32203, 32213, 32233, 32237, 32251, 32257, 32261, 32297, 32299, 32303, 32309, 32321, 32323, 32327, 32341, 32353, 32359, 32363, 32369, 32371, 32377, 32381, 32401, 32411, 32413, 32423, 32429, 32441, 32443, 32467, 32479, 32491, 32497, 32503, 32507, 32531, 32533, 32537, 32561, 32563, 32569, 32573, 32579, 32587, 32603, 32609, 32611, 32621, 32633, 32647, 32653, 32687, 32693, 32707, 32713, 32717, 32719, 32749, 32771, 32779, 32783, 32789, 32797, 32801, 32803, 32831, 32833, 32839, 32843, 32869, 32887, 32909, 32911, 32917, 32933, 32939, 32941, 32957, 32969, 32971, 32983, 32987, 32993, 32999, 33013, 33023, 33029, 33037, 33049, 33053, 33071, 33073, 33083, 33091, 33107, 33113, 33119, 33149, 33151, 33161, 33179, 33181, 33191, 33199, 33203, 33211, 33223, 33247, 33287, 33289, 33301, 33311, 33317, 33329, 33331, 33343, 33347, 33349, 33353, 33359, 33377, 33391, 33403, 33409, 33413, 33427, 33457, 33461, 33469, 33479, 33487, 33493, 33503, 33521, 33529, 33533, 33547, 33563, 33569, 33577, 33581, 33587, 33589, 33599, 33601, 33613, 33617, 33619, 33623, 33629, 33637, 33641, 33647, 33679, 33703, 33713, 33721, 33739, 33749, 33751, 33757, 33767, 33769, 33773, 33791, 33797, 33809, 33811, 33827, 33829, 33851, 33857, 33863, 33871, 33889, 33893, 33911, 33923, 33931, 33937, 33941, 33961, 33967, 33997, 34019, 34031, 34033, 34039, 34057, 34061, 34123, 34127, 34129, 34141, 34147, 34157, 34159, 34171, 34183, 34211, 34213, 34217, 34231, 34253, 34259, 34261, 34267, 34273, 34283, 34297, 34301, 34303, 34313, 34319, 34327, 34337, 34351, 34361, 34367, 34369, 34381, 34403, 34421, 34429, 34439, 34457, 34469, 34471, 34483, 34487, 34499, 34501, 34511, 34513, 34519, 34537, 34543, 34549, 34583, 34589, 34591, 34603, 34607, 34613, 34631, 34649, 34651, 34667, 34673, 34679, 34687, 34693, 34703, 34721, 34729, 34739, 34747, 34757, 34759, 34763, 34781, 34807, 34819, 34841, 34843, 34847, 34849, 34871, 34877, 34883, 34897, 34913, 34919, 34939, 34949, 34961, 34963, 34981, 35023, 35027, 35051, 35053, 35059, 35069, 35081, 35083, 35089, 35099, 35107, 35111, 35117, 35129, 35141, 35149, 35153, 35159, 35171, 35201, 35221, 35227, 35251, 35257, 35267, 35279, 35281, 35291, 35311, 35317, 35323, 35327, 35339, 35353, 35363, 35381, 35393, 35401, 35407, 35419, 35423, 35437, 35447, 35449, 35461, 35491, 35507, 35509, 35521, 35527, 35531, 35533, 35537, 35543, 35569, 35573, 35591, 35593, 35597, 35603, 35617, 35671, 35677, 35729, 35731, 35747, 35753, 35759, 35771, 35797, 35801, 35803, 35809, 35831, 35837, 35839, 35851, 35863, 35869, 35879, 35897, 35899, 35911, 35923, 35933, 35951, 35963, 35969, 35977, 35983, 35993, 35999, 36007, 36011, 36013, 36017, 36037, 36061, 36067, 36073, 36083, 36097, 36107, 36109, 36131, 36137, 36151, 36161, 36187, 36191, 36209, 36217, 36229, 36241, 36251, 36263, 36269, 36277, 36293, 36299, 36307, 36313, 36319, 36341, 36343, 36353, 36373, 36383, 36389, 36433, 36451, 36457, 36467, 36469, 36473, 36479, 36493, 36497, 36523, 36527, 36529, 36541, 36551, 36559, 36563, 36571, 36583, 36587, 36599, 36607, 36629, 36637, 36643, 36653, 36671, 36677, 36683, 36691, 36697, 36709, 36713, 36721, 36739, 36749, 36761, 36767, 36779, 36781, 36787, 36791, 36793, 36809, 36821, 36833, 36847, 36857, 36871, 36877, 36887, 36899, 36901, 36913, 36919, 36923, 36929, 36931, 36943, 36947, 36973, 36979, 36997, 37003, 37013, 37019, 37021, 37039, 37049, 37057, 37061, 37087, 37097, 37117, 37123, 37139, 37159, 37171, 37181, 37189, 37199, 37201, 37217, 37223, 37243, 37253, 37273, 37277, 37307, 37309, 37313, 37321, 37337, 37339, 37357, 37361, 37363, 37369, 37379, 37397, 37409, 37423, 37441, 37447, 37463, 37483, 37489, 37493, 37501, 37507, 37511, 37517, 37529, 37537, 37547, 37549, 37561, 37567, 37571, 37573, 37579, 37589, 37591, 37607, 37619, 37633, 37643, 37649, 37657, 37663, 37691, 37693, 37699, 37717, 37747, 37781, 37783, 37799, 37811, 37813, 37831, 37847, 37853, 37861, 37871, 37879, 37889, 37897, 37907, 37951, 37957, 37963, 37967, 37987, 37991, 37993, 37997, 38011, 38039, 38047, 38053, 38069, 38083, 38113, 38119, 38149, 38153, 38167, 38177, 38183, 38189, 38197, 38201, 38219, 38231, 38237, 38239, 38261, 38273, 38281, 38287, 38299, 38303, 38317, 38321, 38327, 38329, 38333, 38351, 38371, 38377, 38393, 38431, 38447, 38449, 38453, 38459, 38461, 38501, 38543, 38557, 38561, 38567, 38569, 38593, 38603, 38609, 38611, 38629, 38639, 38651, 38653, 38669, 38671, 38677, 38693, 38699, 38707, 38711, 38713, 38723, 38729, 38737, 38747, 38749, 38767, 38783, 38791, 38803, 38821, 38833, 38839, 38851, 38861, 38867, 38873, 38891, 38903, 38917, 38921, 38923, 38933, 38953, 38959, 38971, 38977, 38993, 39019, 39023, 39041, 39043, 39047, 39079, 39089, 39097, 39103, 39107, 39113, 39119, 39133, 39139, 39157, 39161, 39163, 39181, 39191, 39199, 39209, 39217, 39227, 39229, 39233, 39239, 39241, 39251, 39293, 39301, 39313, 39317, 39323, 39341, 39343, 39359, 39367, 39371, 39373, 39383, 39397, 39409, 39419, 39439, 39443, 39451, 39461, 39499, 39503, 39509, 39511, 39521, 39541, 39551, 39563, 39569, 39581, 39607, 39619, 39623, 39631, 39659, 39667, 39671, 39679, 39703, 39709, 39719, 39727, 39733, 39749, 39761, 39769, 39779, 39791, 39799, 39821, 39827, 39829, 39839, 39841, 39847, 39857, 39863, 39869, 39877, 39883, 39887, 39901, 39929, 39937, 39953, 39971, 39979, 39983, 39989, 40009, 40013, 40031, 40037, 40039, 40063, 40087, 40093, 40099, 40111, 40123, 40127, 40129, 40151, 40153, 40163, 40169, 40177, 40189, 40193, 40213, 40231, 40237, 40241, 40253, 40277, 40283, 40289, 40343, 40351, 40357, 40361, 40387, 40423, 40427, 40429, 40433, 40459, 40471, 40483, 40487, 40493, 40499, 40507, 40519, 40529, 40531, 40543, 40559, 40577, 40583, 40591, 40597, 40609, 40627, 40637, 40639, 40693, 40697, 40699, 40709, 40739, 40751, 40759, 40763, 40771, 40787, 40801, 40813, 40819, 40823, 40829, 40841, 40847, 40849, 40853, 40867, 40879, 40883, 40897, 40903, 40927, 40933, 40939, 40949, 40961, 40973, 40993, 41011, 41017, 41023, 41039, 41047, 41051, 41057, 41077, 41081, 41113, 41117, 41131, 41141, 41143, 41149, 41161, 41177, 41179, 41183, 41189, 41201, 41203, 41213, 41221, 41227, 41231, 41233, 41243, 41257, 41263, 41269, 41281, 41299, 41333, 41341, 41351, 41357, 41381, 41387, 41389, 41399, 41411, 41413, 41443, 41453, 41467, 41479, 41491, 41507, 41513, 41519, 41521, 41539, 41543, 41549, 41579, 41593, 41597, 41603, 41609, 41611, 41617, 41621, 41627, 41641, 41647, 41651, 41659, 41669, 41681, 41687, 41719, 41729, 41737, 41759, 41761, 41771, 41777, 41801, 41809, 41813, 41843, 41849, 41851, 41863, 41879, 41887, 41893, 41897, 41903, 41911, 41927, 41941, 41947, 41953, 41957, 41959, 41969, 41981, 41983, 41999, 42013, 42017, 42019, 42023, 42043, 42061, 42071, 42073, 42083, 42089, 42101, 42131, 42139, 42157, 42169, 42179, 42181, 42187, 42193, 42197, 42209, 42221, 42223, 42227, 42239, 42257, 42281, 42283, 42293, 42299, 42307, 42323, 42331, 42337, 42349, 42359, 42373, 42379, 42391, 42397, 42403, 42407, 42409, 42433, 42437, 42443, 42451, 42457, 42461, 42463, 42467, 42473, 42487, 42491, 42499, 42509, 42533, 42557, 42569, 42571, 42577, 42589, 42611, 42641, 42643, 42649, 42667, 42677, 42683, 42689, 42697, 42701, 42703, 42709, 42719, 42727, 42737, 42743, 42751, 42767, 42773, 42787, 42793, 42797, 42821, 42829, 42839, 42841, 42853, 42859, 42863, 42899, 42901, 42923, 42929, 42937, 42943, 42953, 42961, 42967, 42979, 42989, 43003, 43013, 43019, 43037, 43049, 43051, 43063, 43067, 43093, 43103, 43117, 43133, 43151, 43159, 43177, 43189, 43201, 43207, 43223, 43237, 43261, 43271, 43283, 43291, 43313, 43319, 43321, 43331, 43391, 43397, 43399, 43403, 43411, 43427, 43441, 43451, 43457, 43481, 43487, 43499, 43517, 43541, 43543, 43573, 43577, 43579, 43591, 43597, 43607, 43609, 43613, 43627, 43633, 43649, 43651, 43661, 43669, 43691, 43711, 43717, 43721, 43753, 43759, 43777, 43781, 43783, 43787, 43789, 43793, 43801, 43853, 43867, 43889, 43891, 43913, 43933, 43943, 43951, 43961, 43963, 43969, 43973, 43987, 43991, 43997, 44017, 44021, 44027, 44029, 44041, 44053, 44059, 44071, 44087, 44089, 44101, 44111, 44119, 44123, 44129, 44131, 44159, 44171, 44179, 44189, 44201, 44203, 44207, 44221, 44249, 44257, 44263, 44267, 44269, 44273, 44279, 44281, 44293, 44351, 44357, 44371, 44381, 44383, 44389, 44417, 44449, 44453, 44483, 44491, 44497, 44501, 44507, 44519, 44531, 44533, 44537, 44543, 44549, 44563, 44579, 44587, 44617, 44621, 44623, 44633, 44641, 44647, 44651, 44657, 44683, 44687, 44699, 44701, 44711, 44729, 44741, 44753, 44771, 44773, 44777, 44789, 44797, 44809, 44819, 44839, 44843, 44851, 44867, 44879, 44887, 44893, 44909, 44917, 44927, 44939, 44953, 44959, 44963, 44971, 44983, 44987, 45007, 45013, 45053, 45061, 45077, 45083, 45119, 45121, 45127, 45131, 45137, 45139, 45161, 45179, 45181, 45191, 45197, 45233, 45247, 45259, 45263, 45281, 45289, 45293, 45307, 45317, 45319, 45329, 45337, 45341, 45343, 45361, 45377, 45389, 45403, 45413, 45427, 45433, 45439, 45481, 45491, 45497, 45503, 45523, 45533, 45541, 45553, 45557, 45569, 45587, 45589, 45599, 45613, 45631, 45641, 45659, 45667, 45673, 45677, 45691, 45697, 45707, 45737, 45751, 45757, 45763, 45767, 45779, 45817, 45821, 45823, 45827, 45833, 45841, 45853, 45863, 45869, 45887, 45893, 45943, 45949, 45953, 45959, 45971, 45979, 45989, 46021, 46027, 46049, 46051, 46061, 46073, 46091, 46093, 46099, 46103, 46133, 46141, 46147, 46153, 46171, 46181, 46183, 46187, 46199, 46219, 46229, 46237, 46261, 46271, 46273, 46279, 46301, 46307, 46309, 46327, 46337, 46349, 46351, 46381, 46399, 46411, 46439, 46441, 46447, 46451, 46457, 46471, 46477, 46489, 46499, 46507, 46511, 46523, 46549, 46559, 46567, 46573, 46589, 46591, 46601, 46619, 46633, 46639, 46643, 46649, 46663, 46679, 46681, 46687, 46691, 46703, 46723, 46727, 46747, 46751, 46757, 46769, 46771, 46807, 46811, 46817, 46819, 46829, 46831, 46853, 46861, 46867, 46877, 46889, 46901, 46919, 46933, 46957, 46993, 46997, 47017, 47041, 47051, 47057, 47059, 47087, 47093, 47111, 47119, 47123, 47129, 47137, 47143, 47147, 47149, 47161, 47189, 47207, 47221, 47237, 47251, 47269, 47279, 47287, 47293, 47297, 47303, 47309, 47317, 47339, 47351, 47353, 47363, 47381, 47387, 47389, 47407, 47417, 47419, 47431, 47441, 47459, 47491, 47497, 47501, 47507, 47513, 47521, 47527, 47533, 47543, 47563, 47569, 47581, 47591, 47599, 47609, 47623, 47629, 47639, 47653, 47657, 47659, 47681, 47699, 47701, 47711, 47713, 47717, 47737, 47741, 47743, 47777, 47779, 47791, 47797, 47807, 47809, 47819, 47837, 47843, 47857, 47869, 47881, 47903, 47911, 47917, 47933, 47939, 47947, 47951, 47963, 47969, 47977, 47981, 48017, 48023, 48029, 48049, 48073, 48079, 48091, 48109, 48119, 48121, 48131, 48157, 48163, 48179, 48187, 48193, 48197, 48221, 48239, 48247, 48259, 48271, 48281, 48299, 48311, 48313, 48337, 48341, 48353, 48371, 48383, 48397, 48407, 48409, 48413, 48437, 48449, 48463, 48473, 48479, 48481, 48487, 48491, 48497, 48523, 48527, 48533, 48539, 48541, 48563, 48571, 48589, 48593, 48611, 48619, 48623, 48647, 48649, 48661, 48673, 48677, 48679, 48731, 48733, 48751, 48757, 48761, 48767, 48779, 48781, 48787, 48799, 48809, 48817, 48821, 48823, 48847, 48857, 48859, 48869, 48871, 48883, 48889, 48907, 48947, 48953, 48973, 48989, 48991, 49003, 49009, 49019, 49031, 49033, 49037, 49043, 49057, 49069, 49081, 49103, 49109, 49117, 49121, 49123, 49139, 49157, 49169, 49171, 49177, 49193, 49199, 49201, 49207, 49211, 49223, 49253, 49261, 49277, 49279, 49297, 49307, 49331, 49333, 49339, 49363, 49367, 49369, 49391, 49393, 49409, 49411, 49417, 49429, 49433, 49451, 49459, 49463, 49477, 49481, 49499, 49523, 49529, 49531, 49537, 49547, 49549, 49559, 49597, 49603, 49613, 49627, 49633, 49639, 49663, 49667, 49669, 49681, 49697, 49711, 49727, 49739, 49741, 49747, 49757, 49783, 49787, 49789, 49801, 49807, 49811, 49823, 49831, 49843, 49853, 49871, 49877, 49891, 49919, 49921, 49927, 49937, 49939, 49943, 49957, 49991, 49993, 49999, 50021, 50023, 50033, 50047, 50051, 50053, 50069, 50077, 50087, 50093, 50101, 50111, 50119, 50123, 50129, 50131, 50147, 50153, 50159, 50177, 50207, 50221, 50227, 50231, 50261, 50263, 50273, 50287, 50291, 50311, 50321, 50329, 50333, 50341, 50359, 50363, 50377, 50383, 50387, 50411, 50417, 50423, 50441, 50459, 50461, 50497, 50503, 50513, 50527, 50539, 50543, 50549, 50551, 50581, 50587, 50591, 50593, 50599, 50627, 50647, 50651, 50671, 50683, 50707, 50723, 50741, 50753, 50767, 50773, 50777, 50789, 50821, 50833, 50839, 50849, 50857, 50867, 50873, 50891, 50893, 50909, 50923, 50929, 50951, 50957, 50969, 50971, 50989, 50993, 51001, 51031, 51043, 51047, 51059, 51061, 51071, 51109, 51131, 51133, 51137, 51151, 51157, 51169, 51193, 51197, 51199, 51203, 51217, 51229, 51239, 51241, 51257, 51263, 51283, 51287, 51307, 51329, 51341, 51343, 51347, 51349, 51361, 51383, 51407, 51413, 51419, 51421, 51427, 51431, 51437, 51439, 51449, 51461, 51473, 51479, 51481, 51487, 51503, 51511, 51517, 51521, 51539, 51551, 51563, 51577, 51581, 51593, 51599, 51607, 51613, 51631, 51637, 51647, 51659, 51673, 51679, 51683, 51691, 51713, 51719, 51721, 51749, 51767, 51769, 51787, 51797, 51803, 51817, 51827, 51829, 51839, 51853, 51859, 51869, 51871, 51893, 51899, 51907, 51913, 51929, 51941, 51949, 51971, 51973, 51977, 51991, 52009, 52021, 52027, 52051, 52057, 52067, 52069, 52081, 52103, 52121, 52127, 52147, 52153, 52163, 52177, 52181, 52183, 52189, 52201, 52223, 52237, 52249, 52253, 52259, 52267, 52289, 52291, 52301, 52313, 52321, 52361, 52363, 52369, 52379, 52387, 52391, 52433, 52453, 52457, 52489, 52501, 52511, 52517, 52529, 52541, 52543, 52553, 52561, 52567, 52571, 52579, 52583, 52609, 52627, 52631, 52639, 52667, 52673, 52691, 52697, 52709, 52711, 52721, 52727, 52733, 52747, 52757, 52769, 52783, 52807, 52813, 52817, 52837, 52859, 52861, 52879, 52883, 52889, 52901, 52903, 52919, 52937, 52951, 52957, 52963, 52967, 52973, 52981, 52999, 53003, 53017, 53047, 53051, 53069, 53077, 53087, 53089, 53093, 53101, 53113, 53117, 53129, 53147, 53149, 53161, 53171, 53173, 53189, 53197, 53201, 53231, 53233, 53239, 53267, 53269, 53279, 53281, 53299, 53309, 53323, 53327, 53353, 53359, 53377, 53381, 53401, 53407, 53411, 53419, 53437, 53441, 53453, 53479, 53503, 53507, 53527, 53549, 53551, 53569, 53591, 53593, 53597, 53609, 53611, 53617, 53623, 53629, 53633, 53639, 53653, 53657, 53681, 53693, 53699, 53717, 53719, 53731, 53759, 53773, 53777, 53783, 53791, 53813, 53819, 53831, 53849, 53857, 53861, 53881, 53887, 53891, 53897, 53899, 53917, 53923, 53927, 53939, 53951, 53959, 53987, 53993, 54001, 54011, 54013, 54037, 54049, 54059, 54083, 54091, 54101, 54121, 54133, 54139, 54151, 54163, 54167, 54181, 54193, 54217, 54251, 54269, 54277, 54287, 54293, 54311, 54319, 54323, 54331, 54347, 54361, 54367, 54371, 54377, 54401, 54403, 54409, 54413, 54419, 54421, 54437, 54443, 54449, 54469, 54493, 54497, 54499, 54503, 54517, 54521, 54539, 54541, 54547, 54559, 54563, 54577, 54581, 54583, 54601, 54617, 54623, 54629, 54631, 54647, 54667, 54673, 54679, 54709, 54713, 54721, 54727, 54751, 54767, 54773, 54779, 54787, 54799, 54829, 54833, 54851, 54869, 54877, 54881, 54907, 54917, 54919, 54941, 54949, 54959, 54973, 54979, 54983, 55001, 55009, 55021, 55049, 55051, 55057, 55061, 55073, 55079, 55103, 55109, 55117, 55127, 55147, 55163, 55171, 55201, 55207, 55213, 55217, 55219, 55229, 55243, 55249, 55259, 55291, 55313, 55331, 55333, 55337, 55339, 55343, 55351, 55373, 55381, 55399, 55411, 55439, 55441, 55457, 55469, 55487, 55501, 55511, 55529, 55541, 55547, 55579, 55589, 55603, 55609, 55619, 55621, 55631, 55633, 55639, 55661, 55663, 55667, 55673, 55681, 55691, 55697, 55711, 55717, 55721, 55733, 55763, 55787, 55793, 55799, 55807, 55813, 55817, 55819, 55823, 55829, 55837, 55843, 55849, 55871, 55889, 55897, 55901, 55903, 55921, 55927, 55931, 55933, 55949, 55967, 55987, 55997, 56003, 56009, 56039, 56041, 56053, 56081, 56087, 56093, 56099, 56101, 56113, 56123, 56131, 56149, 56167, 56171, 56179, 56197, 56207, 56209, 56237, 56239, 56249, 56263, 56267, 56269, 56299, 56311, 56333, 56359, 56369, 56377, 56383, 56393, 56401, 56417, 56431, 56437, 56443, 56453, 56467, 56473, 56477, 56479, 56489, 56501, 56503, 56509, 56519, 56527, 56531, 56533, 56543, 56569, 56591, 56597, 56599, 56611, 56629, 56633, 56659, 56663, 56671, 56681, 56687, 56701, 56711, 56713, 56731, 56737, 56747, 56767, 56773, 56779, 56783, 56807, 56809, 56813, 56821, 56827, 56843, 56857, 56873, 56891, 56893, 56897, 56909, 56911, 56921, 56923, 56929, 56941, 56951, 56957, 56963, 56983, 56989, 56993, 56999, 57037, 57041, 57047, 57059, 57073, 57077, 57089, 57097, 57107, 57119, 57131, 57139, 57143, 57149, 57163, 57173, 57179, 57191, 57193, 57203, 57221, 57223, 57241, 57251, 57259, 57269, 57271, 57283, 57287, 57301, 57329, 57331, 57347, 57349, 57367, 57373, 57383, 57389, 57397, 57413, 57427, 57457, 57467, 57487, 57493, 57503, 57527, 57529, 57557, 57559, 57571, 57587, 57593, 57601, 57637, 57641, 57649, 57653, 57667, 57679, 57689, 57697, 57709, 57713, 57719, 57727, 57731, 57737, 57751, 57773, 57781, 57787, 57791, 57793, 57803, 57809, 57829, 57839, 57847, 57853, 57859, 57881, 57899, 57901, 57917, 57923, 57943, 57947, 57973, 57977, 57991, 58013, 58027, 58031, 58043, 58049, 58057, 58061, 58067, 58073, 58099, 58109, 58111, 58129, 58147, 58151, 58153, 58169, 58171, 58189, 58193, 58199, 58207, 58211, 58217, 58229, 58231, 58237, 58243, 58271, 58309, 58313, 58321, 58337, 58363, 58367, 58369, 58379, 58391, 58393, 58403, 58411, 58417, 58427, 58439, 58441, 58451, 58453, 58477, 58481, 58511, 58537, 58543, 58549, 58567, 58573, 58579, 58601, 58603, 58613, 58631, 58657, 58661, 58679, 58687, 58693, 58699, 58711, 58727, 58733, 58741, 58757, 58763, 58771, 58787, 58789, 58831, 58889, 58897, 58901, 58907, 58909, 58913, 58921, 58937, 58943, 58963, 58967, 58979, 58991, 58997, 59009, 59011, 59021, 59023, 59029, 59051, 59053, 59063, 59069, 59077, 59083, 59093, 59107, 59113, 59119, 59123, 59141, 59149, 59159, 59167, 59183, 59197, 59207, 59209, 59219, 59221, 59233, 59239, 59243, 59263, 59273, 59281, 59333, 59341, 59351, 59357, 59359, 59369, 59377, 59387, 59393, 59399, 59407, 59417, 59419, 59441, 59443, 59447, 59453, 59467, 59471, 59473, 59497, 59509, 59513, 59539, 59557, 59561, 59567, 59581, 59611, 59617, 59621, 59627, 59629, 59651, 59659, 59663, 59669, 59671, 59693, 59699, 59707, 59723, 59729, 59743, 59747, 59753, 59771, 59779, 59791, 59797, 59809, 59833, 59863, 59879, 59887, 59921, 59929, 59951, 59957, 59971, 59981, 59999, 60013, 60017, 60029, 60037, 60041, 60077, 60083, 60089, 60091, 60101, 60103, 60107, 60127, 60133, 60139, 60149, 60161, 60167, 60169, 60209, 60217, 60223, 60251, 60257, 60259, 60271, 60289, 60293, 60317, 60331, 60337, 60343, 60353, 60373, 60383, 60397, 60413, 60427, 60443, 60449, 60457, 60493, 60497, 60509, 60521, 60527, 60539, 60589, 60601, 60607, 60611, 60617, 60623, 60631, 60637, 60647, 60649, 60659, 60661, 60679, 60689, 60703, 60719, 60727, 60733, 60737, 60757, 60761, 60763, 60773, 60779, 60793, 60811, 60821, 60859, 60869, 60887, 60889, 60899, 60901, 60913, 60917, 60919, 60923, 60937, 60943, 60953, 60961, 61001, 61007, 61027, 61031, 61043, 61051, 61057, 61091, 61099, 61121, 61129, 61141, 61151, 61153, 61169, 61211, 61223, 61231, 61253, 61261, 61283, 61291, 61297, 61331, 61333, 61339, 61343, 61357, 61363, 61379, 61381, 61403, 61409, 61417, 61441, 61463, 61469, 61471, 61483, 61487, 61493, 61507, 61511, 61519, 61543, 61547, 61553, 61559, 61561, 61583, 61603, 61609, 61613, 61627, 61631, 61637, 61643, 61651, 61657, 61667, 61673, 61681, 61687, 61703, 61717, 61723, 61729, 61751, 61757, 61781, 61813, 61819, 61837, 61843, 61861, 61871, 61879, 61909, 61927, 61933, 61949, 61961, 61967, 61979, 61981, 61987, 61991, 62003, 62011, 62017, 62039, 62047, 62053, 62057, 62071, 62081, 62099, 62119, 62129, 62131, 62137, 62141, 62143, 62171, 62189, 62191, 62201, 62207, 62213, 62219, 62233, 62273, 62297, 62299, 62303, 62311, 62323, 62327, 62347, 62351, 62383, 62401, 62417, 62423, 62459, 62467, 62473, 62477, 62483, 62497, 62501, 62507, 62533, 62539, 62549, 62563, 62581, 62591, 62597, 62603, 62617, 62627, 62633, 62639, 62653, 62659, 62683, 62687, 62701, 62723, 62731, 62743, 62753, 62761, 62773, 62791, 62801, 62819, 62827, 62851, 62861, 62869, 62873, 62897, 62903, 62921, 62927, 62929, 62939, 62969, 62971, 62981, 62983, 62987, 62989, 63029, 63031, 63059, 63067, 63073, 63079, 63097, 63103, 63113, 63127, 63131, 63149, 63179, 63197, 63199, 63211, 63241, 63247, 63277, 63281, 63299, 63311, 63313, 63317, 63331, 63337, 63347, 63353, 63361, 63367, 63377, 63389, 63391, 63397, 63409, 63419, 63421, 63439, 63443, 63463, 63467, 63473, 63487, 63493, 63499, 63521, 63527, 63533, 63541, 63559, 63577, 63587, 63589, 63599, 63601, 63607, 63611, 63617, 63629, 63647, 63649, 63659, 63667, 63671, 63689, 63691, 63697, 63703, 63709, 63719, 63727, 63737, 63743, 63761, 63773, 63781, 63793, 63799, 63803, 63809, 63823, 63839, 63841, 63853, 63857, 63863, 63901, 63907, 63913, 63929, 63949, 63977, 63997, 64007, 64013, 64019, 64033, 64037, 64063, 64067, 64081, 64091, 64109, 64123, 64151, 64153, 64157, 64171, 64187, 64189, 64217, 64223, 64231, 64237, 64271, 64279, 64283, 64301, 64303, 64319, 64327, 64333, 64373, 64381, 64399, 64403, 64433, 64439, 64451, 64453, 64483, 64489, 64499, 64513, 64553, 64567, 64577, 64579, 64591, 64601, 64609, 64613, 64621, 64627, 64633, 64661, 64663, 64667, 64679, 64693, 64709, 64717, 64747, 64763, 64781, 64783, 64793, 64811, 64817, 64849, 64853, 64871, 64877, 64879, 64891, 64901, 64919, 64921, 64927, 64937, 64951, 64969, 64997, 65003, 65011, 65027, 65029, 65033, 65053, 65063, 65071, 65089, 65099, 65101, 65111, 65119, 65123, 65129, 65141, 65147, 65167, 65171, 65173, 65179, 65183, 65203, 65213, 65239, 65257, 65267, 65269, 65287, 65293, 65309, 65323, 65327, 65353, 65357, 65371, 65381, 65393, 65407, 65413, 65419, 65423, 65437, 65447, 65449, 65479, 65497, 65519, 65521, }; #define NPRIMES (sizeof(primes) / sizeof(*primes)) /* * Generate a prime. We can deal with various extra properties of * the prime: * * - to speed up use in RSA, we can arrange to select a prime with * the property (prime % modulus) != residue. * * - for use in DSA, we can arrange to select a prime which is one * more than a multiple of a dirty great bignum. In this case * `bits' gives the size of the factor by which we _multiply_ * that bignum, rather than the size of the whole number. * * - for the basically cosmetic purposes of generating keys of the * length actually specified rather than off by one bit, we permit * the caller to provide an unsigned integer 'firstbits' which will * match the top few bits of the returned prime. (That is, there * will exist some n such that (returnvalue >> n) == firstbits.) If * 'firstbits' is not needed, specifying it to either 0 or 1 is * an adequate no-op. */ Bignum primegen(int bits, int modulus, int residue, Bignum factor, int phase, progfn_t pfn, void *pfnparam, unsigned firstbits) { int i, k, v, byte, bitsleft, check, checks, fbsize; unsigned long delta; unsigned long moduli[NPRIMES + 1]; unsigned long residues[NPRIMES + 1]; unsigned long multipliers[NPRIMES + 1]; Bignum p, pm1, q, wqp, wqp2; int progress = 0; byte = 0; bitsleft = 0; fbsize = 0; while (firstbits >> fbsize) /* work out how to align this */ fbsize++; STARTOVER: pfn(pfnparam, PROGFN_PROGRESS, phase, ++progress); /* * Generate a k-bit random number with top and bottom bits set. * Alternatively, if `factor' is nonzero, generate a k-bit * random number with the top bit set and the bottom bit clear, * multiply it by `factor', and add one. */ p = bn_power_2(bits - 1); for (i = 0; i < bits; i++) { if (i == 0 || i == bits - 1) { v = (i != 0 || !factor) ? 1 : 0; } else if (i >= bits - fbsize) { v = (firstbits >> (i - (bits - fbsize))) & 1; } else { if (bitsleft <= 0) bitsleft = 8, byte = random_byte(); v = byte & 1; byte >>= 1; bitsleft--; } bignum_set_bit(p, i, v); |
︙ | ︙ | |||
1392 1393 1394 1395 1396 1397 1398 | /* * We have a prime! */ freebn(q); freebn(pm1); return p; } | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 | /* * We have a prime! */ freebn(q); freebn(pm1); return p; } /* * Invent a pair of values suitable for use as 'firstbits' in the * above function, such that their product is at least 2. * * This is used for generating both RSA and DSA keys which have * exactly the specified number of bits rather than one fewer - if you * generate an a-bit and a b-bit number completely at random and * multiply them together, you could end up with either an (ab-1)-bit * number or an (ab)-bit number. The former happens log(2)*2-1 of the * time (about 39%) and, though actually harmless, every time it * occurs it has a non-zero probability of sparking a user email along * the lines of 'Hey, I asked PuTTYgen for a 2048-bit key and I only * got 2047 bits! Bug!' */ void invent_firstbits(unsigned *one, unsigned *two) { /* * Our criterion is that any number in the range [one,one+1) * multiplied by any number in the range [two,two+1) should have * the highest bit set. It should be clear that we can trivially * test this by multiplying the smallest values in each interval, * i.e. the ones we actually invented. */ do { *one = 0x100 | random_byte(); *two = 0x100 | random_byte(); } while (*one * *two < 0x20000); } |
Changes to sshpubk.c.
︙ | ︙ | |||
63 64 65 66 67 68 69 | if (len - i < 4) goto end; /* reserved field not present */ if (buf[i] != 0 || buf[i + 1] != 0 || buf[i + 2] != 0 || buf[i + 3] != 0) goto end; /* reserved field nonzero, panic! */ i += 4; /* Now the serious stuff. An ordinary SSH-1 public key. */ | | | > | | | 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 | if (len - i < 4) goto end; /* reserved field not present */ if (buf[i] != 0 || buf[i + 1] != 0 || buf[i + 2] != 0 || buf[i + 3] != 0) goto end; /* reserved field nonzero, panic! */ i += 4; /* Now the serious stuff. An ordinary SSH-1 public key. */ j = makekey(buf + i, len, key, NULL, 1); if (j < 0) goto end; /* overran */ i += j; /* Next, the comment field. */ j = toint(GET_32BIT(buf + i)); i += 4; if (j < 0 || len - i < j) goto end; comment = snewn(j + 1, char); if (comment) { memcpy(comment, buf + i, j); comment[j] = '\0'; } i += j; |
︙ | ︙ | |||
104 105 106 107 108 109 110 | * Decrypt remainder of buffer. */ if (ciphertype) { MD5Init(&md5c); MD5Update(&md5c, (unsigned char *)passphrase, strlen(passphrase)); MD5Final(keybuf, &md5c); des3_decrypt_pubkey(keybuf, buf + i, (len - i + 7) & ~7); | | | 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 | * Decrypt remainder of buffer. */ if (ciphertype) { MD5Init(&md5c); MD5Update(&md5c, (unsigned char *)passphrase, strlen(passphrase)); MD5Final(keybuf, &md5c); des3_decrypt_pubkey(keybuf, buf + i, (len - i + 7) & ~7); smemclr(keybuf, sizeof(keybuf)); /* burn the evidence */ } /* * We are now in the secret part of the key. The first four * bytes should be of the form a, b, a, b. */ if (len - i < 4) |
︙ | ︙ | |||
146 147 148 149 150 151 152 | *error = "rsa_verify failed"; freersakey(key); ret = 0; } else ret = 1; end: | | | | 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 | *error = "rsa_verify failed"; freersakey(key); ret = 0; } else ret = 1; end: smemclr(buf, sizeof(buf)); /* burn the evidence */ return ret; } int loadrsakey(const Filename *filename, struct RSAKey *key, char *passphrase, const char **errorstr) { FILE *fp; char buf[64]; int ret = 0; const char *error = NULL; fp = f_open(filename, "rb", FALSE); if (!fp) { error = "can't open file"; goto end; } /* * Read the first line of the file and see if it's a v1 private |
︙ | ︙ | |||
199 200 201 202 203 204 205 | * well. */ int rsakey_encrypted(const Filename *filename, char **comment) { FILE *fp; char buf[64]; | | | 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 | * well. */ int rsakey_encrypted(const Filename *filename, char **comment) { FILE *fp; char buf[64]; fp = f_open(filename, "rb", FALSE); if (!fp) return 0; /* doesn't even exist */ /* * Read the first line of the file and see if it's a v1 private * key file. */ |
︙ | ︙ | |||
237 238 239 240 241 242 243 | const char *error = NULL; /* Default return if we fail. */ *blob = NULL; *bloblen = 0; ret = 0; | | < > | 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 | const char *error = NULL; /* Default return if we fail. */ *blob = NULL; *bloblen = 0; ret = 0; fp = f_open(filename, "rb", FALSE); if (!fp) { error = "can't open file"; goto end; } /* * Read the first line of the file and see if it's a v1 private * key file. */ if (fgets(buf, sizeof(buf), fp) && !strcmp(buf, rsa_signature)) { memset(&key, 0, sizeof(key)); if (loadrsakey_main(fp, &key, TRUE, commentptr, NULL, &error)) { *blob = rsa_public_blob(&key, bloblen); freersakey(&key); ret = 1; } fp = NULL; /* loadrsakey_main unconditionally closes fp */ } else { error = "not an SSH-1 RSA file"; } end: if (fp) fclose(fp); |
︙ | ︙ | |||
354 355 356 357 358 359 360 | * Now encrypt the encrypted portion. */ if (passphrase) { MD5Init(&md5c); MD5Update(&md5c, (unsigned char *)passphrase, strlen(passphrase)); MD5Final(keybuf, &md5c); des3_encrypt_pubkey(keybuf, estart, p - estart); | | | | 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 | * Now encrypt the encrypted portion. */ if (passphrase) { MD5Init(&md5c); MD5Update(&md5c, (unsigned char *)passphrase, strlen(passphrase)); MD5Final(keybuf, &md5c); des3_encrypt_pubkey(keybuf, estart, p - estart); smemclr(keybuf, sizeof(keybuf)); /* burn the evidence */ } /* * Done. Write the result to the file. */ fp = f_open(filename, "wb", TRUE); if (fp) { int ret = (fwrite(buf, 1, p - buf, fp) == (size_t) (p - buf)); if (fclose(fp)) ret = 0; return ret; } else return 0; |
︙ | ︙ | |||
458 459 460 461 462 463 464 | */ static int read_header(FILE * fp, char *header) { int len = 39; int c; | | | 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 | */ static int read_header(FILE * fp, char *header) { int len = 39; int c; while (1) { c = fgetc(fp); if (c == '\n' || c == '\r' || c == EOF) return 0; /* failure */ if (c == ':') { c = fgetc(fp); if (c != ' ') return 0; |
︙ | ︙ | |||
628 629 630 631 632 633 634 | int passlen = passphrase ? strlen(passphrase) : 0; const char *error = NULL; ret = NULL; /* return NULL for most errors */ encryption = comment = mac = NULL; public_blob = private_blob = NULL; | | > > > > > | 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 | int passlen = passphrase ? strlen(passphrase) : 0; const char *error = NULL; ret = NULL; /* return NULL for most errors */ encryption = comment = mac = NULL; public_blob = private_blob = NULL; fp = f_open(filename, "rb", FALSE); if (!fp) { error = "can't open file"; goto error; } /* Read the first header line which contains the key type. */ if (!read_header(fp, header)) goto error; if (0 == strcmp(header, "PuTTY-User-Key-File-2")) { old_fmt = 0; } else if (0 == strcmp(header, "PuTTY-User-Key-File-1")) { /* this is an old key file; warn and then continue */ old_keyfile_warning(); old_fmt = 1; } else if (0 == strncmp(header, "PuTTY-User-Key-File-", 20)) { /* this is a key file FROM THE FUTURE; refuse it, but with a * more specific error message than the generic one below */ error = "PuTTY key format too new"; goto error; } else { error = "not a PuTTY SSH-2 private key"; goto error; } error = "file format error"; if ((b = read_body(fp)) == NULL) goto error; |
︙ | ︙ | |||
670 671 672 673 674 675 676 | if (!strcmp(encryption, "aes256-cbc")) { cipher = 1; cipherblk = 16; } else if (!strcmp(encryption, "none")) { cipher = 0; cipherblk = 1; } else { | < | 676 677 678 679 680 681 682 683 684 685 686 687 688 689 | if (!strcmp(encryption, "aes256-cbc")) { cipher = 1; cipherblk = 16; } else if (!strcmp(encryption, "none")) { cipher = 0; cipherblk = 1; } else { goto error; } /* Read the Comment header line. */ if (!read_header(fp, header) || 0 != strcmp(header, "Comment")) goto error; if ((comment = read_body(fp)) == NULL) |
︙ | ︙ | |||
790 791 792 793 794 795 796 | SHA_Bytes(&s, header, sizeof(header)-1); if (cipher && passphrase) SHA_Bytes(&s, passphrase, passlen); SHA_Final(&s, mackey); hmac_sha1_simple(mackey, 20, macdata, maclen, binary); | | | | | 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 | SHA_Bytes(&s, header, sizeof(header)-1); if (cipher && passphrase) SHA_Bytes(&s, passphrase, passlen); SHA_Final(&s, mackey); hmac_sha1_simple(mackey, 20, macdata, maclen, binary); smemclr(mackey, sizeof(mackey)); smemclr(&s, sizeof(s)); } else { SHA_Simple(macdata, maclen, binary); } if (free_macdata) { smemclr(macdata, maclen); sfree(macdata); } for (i = 0; i < 20; i++) sprintf(realmac + 2 * i, "%02x", binary[i]); if (strcmp(mac, realmac)) { |
︙ | ︙ | |||
877 878 879 880 881 882 883 | int public_blob_len; int i; const char *error = NULL; char *comment; public_blob = NULL; | | > > > | | 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 | int public_blob_len; int i; const char *error = NULL; char *comment; public_blob = NULL; fp = f_open(filename, "rb", FALSE); if (!fp) { error = "can't open file"; goto error; } /* Read the first header line which contains the key type. */ if (!read_header(fp, header) || (0 != strcmp(header, "PuTTY-User-Key-File-2") && 0 != strcmp(header, "PuTTY-User-Key-File-1"))) { if (0 == strncmp(header, "PuTTY-User-Key-File-", 20)) error = "PuTTY key format too new"; else error = "not a PuTTY SSH-2 private key"; goto error; } error = "file format error"; if ((b = read_body(fp)) == NULL) goto error; /* Select key algorithm structure. */ alg = find_pubkey_alg(b); |
︙ | ︙ | |||
958 959 960 961 962 963 964 | FILE *fp; char header[40], *b, *comment; int ret; if (commentptr) *commentptr = NULL; | | | 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 | FILE *fp; char header[40], *b, *comment; int ret; if (commentptr) *commentptr = NULL; fp = f_open(filename, "rb", FALSE); if (!fp) return 0; if (!read_header(fp, header) || (0 != strcmp(header, "PuTTY-User-Key-File-2") && 0 != strcmp(header, "PuTTY-User-Key-File-1"))) { fclose(fp); return 0; |
︙ | ︙ | |||
996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 | fclose(fp); sfree(b); return 1; } if (commentptr) *commentptr = comment; fclose(fp); if (!strcmp(b, "aes256-cbc")) ret = 1; else ret = 0; sfree(b); | > > | 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 | fclose(fp); sfree(b); return 1; } if (commentptr) *commentptr = comment; else sfree(comment); fclose(fp); if (!strcmp(b, "aes256-cbc")) ret = 1; else ret = 0; sfree(b); |
︙ | ︙ | |||
1112 1113 1114 1115 1116 1117 1118 | SHA_Init(&s); SHA_Bytes(&s, header, sizeof(header)-1); if (passphrase) SHA_Bytes(&s, passphrase, strlen(passphrase)); SHA_Final(&s, mackey); hmac_sha1_simple(mackey, 20, macdata, maclen, priv_mac); | | | | | | | | | | 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 | SHA_Init(&s); SHA_Bytes(&s, header, sizeof(header)-1); if (passphrase) SHA_Bytes(&s, passphrase, strlen(passphrase)); SHA_Final(&s, mackey); hmac_sha1_simple(mackey, 20, macdata, maclen, priv_mac); smemclr(macdata, maclen); sfree(macdata); smemclr(mackey, sizeof(mackey)); smemclr(&s, sizeof(s)); } if (passphrase) { unsigned char key[40]; SHA_State s; passlen = strlen(passphrase); SHA_Init(&s); SHA_Bytes(&s, "\0\0\0\0", 4); SHA_Bytes(&s, passphrase, passlen); SHA_Final(&s, key + 0); SHA_Init(&s); SHA_Bytes(&s, "\0\0\0\1", 4); SHA_Bytes(&s, passphrase, passlen); SHA_Final(&s, key + 20); aes256_encrypt_pubkey(key, priv_blob_encrypted, priv_encrypted_len); smemclr(key, sizeof(key)); smemclr(&s, sizeof(s)); } fp = f_open(filename, "w", TRUE); if (!fp) return 0; fprintf(fp, "PuTTY-User-Key-File-2: %s\n", key->alg->name); fprintf(fp, "Encryption: %s\n", cipherstr); fprintf(fp, "Comment: %s\n", key->comment); fprintf(fp, "Public-Lines: %d\n", base64_lines(pub_blob_len)); base64_encode(fp, pub_blob, pub_blob_len, 64); fprintf(fp, "Private-Lines: %d\n", base64_lines(priv_encrypted_len)); base64_encode(fp, priv_blob_encrypted, priv_encrypted_len, 64); fprintf(fp, "Private-MAC: "); for (i = 0; i < 20; i++) fprintf(fp, "%02x", priv_mac[i]); fprintf(fp, "\n"); fclose(fp); sfree(pub_blob); smemclr(priv_blob, priv_blob_len); sfree(priv_blob); sfree(priv_blob_encrypted); return 1; } /* ---------------------------------------------------------------------- * A function to determine the type of a private key file. Returns * 0 on failure, 1 or 2 on success. */ int key_type(const Filename *filename) { FILE *fp; char buf[32]; const char putty2_sig[] = "PuTTY-User-Key-File-"; const char sshcom_sig[] = "---- BEGIN SSH2 ENCRYPTED PRIVAT"; const char openssh_sig[] = "-----BEGIN "; int i; fp = f_open(filename, "r", FALSE); if (!fp) return SSH_KEYTYPE_UNOPENABLE; i = fread(buf, 1, sizeof(buf), fp); fclose(fp); if (i < 0) return SSH_KEYTYPE_UNOPENABLE; if (i < 32) |
︙ | ︙ |
Changes to sshrand.c.
︙ | ︙ | |||
44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 | int stir_pending; }; static struct RandPool pool; int random_active = 0; long next_noise_collection; static void random_stir(void) { word32 block[HASHINPUT / sizeof(word32)]; word32 digest[HASHSIZE / sizeof(word32)]; int i, j, k; /* * noise_get_light will call random_add_noise, which may call * back to here. Prevent recursive stirs. */ if (pool.stir_pending) return; pool.stir_pending = TRUE; noise_get_light(random_add_noise); SHATransform((word32 *) pool.incoming, (word32 *) pool.incomingb); pool.incomingpos = 0; /* * Chunks of this code are blatantly endianness-dependent, but * as it's all random bits anyway, WHO CARES? | > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 | int stir_pending; }; static struct RandPool pool; int random_active = 0; long next_noise_collection; #ifdef RANDOM_DIAGNOSTICS int random_diagnostics = 0; #endif static void random_stir(void) { word32 block[HASHINPUT / sizeof(word32)]; word32 digest[HASHSIZE / sizeof(word32)]; int i, j, k; /* * noise_get_light will call random_add_noise, which may call * back to here. Prevent recursive stirs. */ if (pool.stir_pending) return; pool.stir_pending = TRUE; noise_get_light(random_add_noise); #ifdef RANDOM_DIAGNOSTICS { int p, q; printf("random stir starting\npool:\n"); for (p = 0; p < POOLSIZE; p += HASHSIZE) { printf(" "); for (q = 0; q < HASHSIZE; q += 4) { printf(" %08x", *(word32 *)(pool.pool + p + q)); } printf("\n"); } printf("incoming:\n "); for (q = 0; q < HASHSIZE; q += 4) { printf(" %08x", *(word32 *)(pool.incoming + q)); } printf("\nincomingb:\n "); for (q = 0; q < HASHINPUT; q += 4) { printf(" %08x", *(word32 *)(pool.incomingb + q)); } printf("\n"); random_diagnostics++; } #endif SHATransform((word32 *) pool.incoming, (word32 *) pool.incomingb); pool.incomingpos = 0; /* * Chunks of this code are blatantly endianness-dependent, but * as it's all random bits anyway, WHO CARES? |
︙ | ︙ | |||
112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 | /* * Stick the result back into the pool. */ for (k = 0; k < sizeof(digest) / sizeof(*digest); k++) ((word32 *) (pool.pool + j))[k] = digest[k]; } } /* * Might as well save this value back into `incoming', just so * there'll be some extra bizarreness there. */ SHATransform(digest, block); memcpy(pool.incoming, digest, sizeof(digest)); pool.poolpos = sizeof(pool.incoming); pool.stir_pending = FALSE; } void random_add_noise(void *noise, int length) { unsigned char *p = noise; int i; | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 | /* * Stick the result back into the pool. */ for (k = 0; k < sizeof(digest) / sizeof(*digest); k++) ((word32 *) (pool.pool + j))[k] = digest[k]; } #ifdef RANDOM_DIAGNOSTICS if (i == 0) { int p, q; printf("random stir midpoint\npool:\n"); for (p = 0; p < POOLSIZE; p += HASHSIZE) { printf(" "); for (q = 0; q < HASHSIZE; q += 4) { printf(" %08x", *(word32 *)(pool.pool + p + q)); } printf("\n"); } printf("incoming:\n "); for (q = 0; q < HASHSIZE; q += 4) { printf(" %08x", *(word32 *)(pool.incoming + q)); } printf("\nincomingb:\n "); for (q = 0; q < HASHINPUT; q += 4) { printf(" %08x", *(word32 *)(pool.incomingb + q)); } printf("\n"); } #endif } /* * Might as well save this value back into `incoming', just so * there'll be some extra bizarreness there. */ SHATransform(digest, block); memcpy(pool.incoming, digest, sizeof(digest)); pool.poolpos = sizeof(pool.incoming); pool.stir_pending = FALSE; #ifdef RANDOM_DIAGNOSTICS { int p, q; printf("random stir done\npool:\n"); for (p = 0; p < POOLSIZE; p += HASHSIZE) { printf(" "); for (q = 0; q < HASHSIZE; q += 4) { printf(" %08x", *(word32 *)(pool.pool + p + q)); } printf("\n"); } printf("incoming:\n "); for (q = 0; q < HASHSIZE; q += 4) { printf(" %08x", *(word32 *)(pool.incoming + q)); } printf("\nincomingb:\n "); for (q = 0; q < HASHINPUT; q += 4) { printf(" %08x", *(word32 *)(pool.incomingb + q)); } printf("\n"); random_diagnostics--; } #endif } void random_add_noise(void *noise, int length) { unsigned char *p = noise; int i; |
︙ | ︙ | |||
195 196 197 198 199 200 201 | } for (i = 0; i < length; i++) pool.pool[i] ^= *p++; pool.poolpos = i; } | | | < < | | | | > > > > | 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 | } for (i = 0; i < length; i++) pool.pool[i] ^= *p++; pool.poolpos = i; } static void random_timer(void *ctx, unsigned long now) { if (random_active > 0 && now == next_noise_collection) { noise_regular(); next_noise_collection = schedule_timer(NOISE_REGULAR_INTERVAL, random_timer, &pool); } } void random_ref(void) { if (!random_active) { memset(&pool, 0, sizeof(pool)); /* just to start with */ noise_get_heavy(random_add_heavynoise_bitbybit); random_stir(); next_noise_collection = schedule_timer(NOISE_REGULAR_INTERVAL, random_timer, &pool); } random_active++; } void random_unref(void) { assert(random_active > 0); if (random_active == 1) { random_save_seed(); expire_timer_context(&pool); } random_active--; } int random_byte(void) { assert(random_active); if (pool.poolpos >= POOLSIZE) random_stir(); return pool.pool[pool.poolpos++]; } void random_get_savedata(void **data, int *len) |
︙ | ︙ |
Changes to sshrsa.c.
︙ | ︙ | |||
106 107 108 109 110 111 112 | len = (bignum_bitcount(b) + 8) / 8; PUT_32BIT(lenbuf, len); SHA512_Bytes(s, lenbuf, 4); while (len-- > 0) { lenbuf[0] = bignum_byte(b, len); SHA512_Bytes(s, lenbuf, 1); } | | | 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 | len = (bignum_bitcount(b) + 8) / 8; PUT_32BIT(lenbuf, len); SHA512_Bytes(s, lenbuf, 4); while (len-- > 0) { lenbuf[0] = bignum_byte(b, len); SHA512_Bytes(s, lenbuf, 1); } smemclr(lenbuf, sizeof(lenbuf)); } /* * Compute (base ^ exp) % mod, provided mod == p * q, with p,q * distinct primes, and iqmp is the multiplicative inverse of q mod p. * Uses Chinese Remainder Theorem to speed computation up over the * obvious implementation of a single big modpow. |
︙ | ︙ | |||
269 270 271 272 273 274 275 | * Now check that this number is strictly greater than * zero, and strictly less than modulus. */ if (bignum_cmp(random, Zero) <= 0 || bignum_cmp(random, key->modulus) >= 0) { freebn(random); continue; | > | > > > > > | > | > > < | 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 | * Now check that this number is strictly greater than * zero, and strictly less than modulus. */ if (bignum_cmp(random, Zero) <= 0 || bignum_cmp(random, key->modulus) >= 0) { freebn(random); continue; } /* * Also, make sure it has an inverse mod modulus. */ random_inverse = modinv(random, key->modulus); if (!random_inverse) { freebn(random); continue; } break; } /* * RSA blinding relies on the fact that (xy)^d mod n is equal * to (x^d mod n) * (y^d mod n) mod n. We invent a random pair * y and y^d; then we multiply x by y, raise to the power d mod * n as usual, and divide by y^d to recover x^d. Thus an * attacker can't correlate the timing of the modpow with the * input, because they don't know anything about the number * that was input to the actual modpow. * * The clever bit is that we don't have to do a huge modpow to * get y and y^d; we will use the number we just invented as * _y^d_, and use the _public_ exponent to compute (y^d)^e = y * from it, which is much faster to do. */ random_encrypted = crt_modpow(random, key->exponent, key->modulus, key->p, key->q, key->iqmp); input_blinded = modmul(input, random_encrypted, key->modulus); ret_blinded = crt_modpow(input_blinded, key->private_exponent, key->modulus, key->p, key->q, key->iqmp); ret = modmul(ret_blinded, random_inverse, key->modulus); freebn(ret_blinded); freebn(input_blinded); |
︙ | ︙ | |||
409 410 411 412 413 414 415 416 | if (cmp != 0) return 0; /* e * d must be congruent to 1, modulo (p-1) and modulo (q-1). */ pm1 = copybn(key->p); decbn(pm1); ed = modmul(key->exponent, key->private_exponent, pm1); cmp = bignum_cmp(ed, One); | > | > | > > | | 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 | if (cmp != 0) return 0; /* e * d must be congruent to 1, modulo (p-1) and modulo (q-1). */ pm1 = copybn(key->p); decbn(pm1); ed = modmul(key->exponent, key->private_exponent, pm1); freebn(pm1); cmp = bignum_cmp(ed, One); freebn(ed); if (cmp != 0) return 0; qm1 = copybn(key->q); decbn(qm1); ed = modmul(key->exponent, key->private_exponent, qm1); freebn(qm1); cmp = bignum_cmp(ed, One); freebn(ed); if (cmp != 0) return 0; /* * Ensure p > q. * * I have seen key blobs in the wild which were generated with * p < q, so instead of rejecting the key in this case we * should instead flip them round into the canonical order of * p > q. This also involves regenerating iqmp. */ if (bignum_cmp(key->p, key->q) <= 0) { Bignum tmp = key->p; key->p = key->q; key->q = tmp; freebn(key->iqmp); key->iqmp = modinv(key->q, key->p); if (!key->iqmp) return 0; } /* * Ensure iqmp * q is congruent to 1, modulo p. */ n = modmul(key->iqmp, key->q, key->p); cmp = bignum_cmp(n, One); freebn(n); if (cmp != 0) return 0; return 1; } /* Public key blob as used by Pageant: exponent before modulus. */ |
︙ | ︙ | |||
521 522 523 524 525 526 527 | */ static void getstring(char **data, int *datalen, char **p, int *length) { *p = NULL; if (*datalen < 4) return; | | > > | 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 | */ static void getstring(char **data, int *datalen, char **p, int *length) { *p = NULL; if (*datalen < 4) return; *length = toint(GET_32BIT(*data)); if (*length < 0) return; *datalen -= 4; *data += 4; if (*datalen < *length) return; *p = *data; *data += *length; *datalen -= *length; |
︙ | ︙ | |||
543 544 545 546 547 548 549 550 551 552 553 554 555 556 | getstring(data, datalen, &p, &length); if (!p) return NULL; b = bignum_from_bytes((unsigned char *)p, length); return b; } static void *rsa2_newkey(char *data, int len) { char *p; int slen; struct RSAKey *rsa; rsa = snew(struct RSAKey); | > > < < > > > > > | 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 | getstring(data, datalen, &p, &length); if (!p) return NULL; b = bignum_from_bytes((unsigned char *)p, length); return b; } static void rsa2_freekey(void *key); /* forward reference */ static void *rsa2_newkey(char *data, int len) { char *p; int slen; struct RSAKey *rsa; rsa = snew(struct RSAKey); getstring(&data, &len, &p, &slen); if (!p || slen != 7 || memcmp(p, "ssh-rsa", 7)) { sfree(rsa); return NULL; } rsa->exponent = getmp(&data, &len); rsa->modulus = getmp(&data, &len); rsa->private_exponent = NULL; rsa->p = rsa->q = rsa->iqmp = NULL; rsa->comment = NULL; if (!rsa->exponent || !rsa->modulus) { rsa2_freekey(rsa); return NULL; } return rsa; } static void rsa2_freekey(void *key) { struct RSAKey *rsa = (struct RSAKey *) key; |
︙ | ︙ | |||
686 687 688 689 690 691 692 | static void *rsa2_openssh_createkey(unsigned char **blob, int *len) { char **b = (char **) blob; struct RSAKey *rsa; rsa = snew(struct RSAKey); | < < | < < | > | | | | 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 | static void *rsa2_openssh_createkey(unsigned char **blob, int *len) { char **b = (char **) blob; struct RSAKey *rsa; rsa = snew(struct RSAKey); rsa->comment = NULL; rsa->modulus = getmp(b, len); rsa->exponent = getmp(b, len); rsa->private_exponent = getmp(b, len); rsa->iqmp = getmp(b, len); rsa->p = getmp(b, len); rsa->q = getmp(b, len); if (!rsa->modulus || !rsa->exponent || !rsa->private_exponent || !rsa->iqmp || !rsa->p || !rsa->q) { rsa2_freekey(rsa); return NULL; } if (!rsa_verify(rsa)) { rsa2_freekey(rsa); return NULL; } return rsa; } static int rsa2_openssh_fmtkey(void *key, unsigned char *blob, int len) |
︙ | ︙ | |||
834 835 836 837 838 839 840 841 842 843 844 845 846 847 | unsigned char hash[20]; getstring(&sig, &siglen, &p, &slen); if (!p || slen != 7 || memcmp(p, "ssh-rsa", 7)) { return 0; } in = getmp(&sig, &siglen); out = modpow(in, rsa->exponent, rsa->modulus); freebn(in); ret = 1; bytes = (bignum_bitcount(rsa->modulus)+7) / 8; /* Top (partial) byte should be zero. */ | > > | 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 | unsigned char hash[20]; getstring(&sig, &siglen, &p, &slen); if (!p || slen != 7 || memcmp(p, "ssh-rsa", 7)) { return 0; } in = getmp(&sig, &siglen); if (!in) return 0; out = modpow(in, rsa->exponent, rsa->modulus); freebn(in); ret = 1; bytes = (bignum_bitcount(rsa->modulus)+7) / 8; /* Top (partial) byte should be zero. */ |
︙ | ︙ |
Changes to sshrsag.c.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | /* * RSA key generation. */ #include "ssh.h" #define RSA_EXPONENT 37 /* we like this prime */ int rsa_generate(struct RSAKey *key, int bits, progfn_t pfn, void *pfnparam) { Bignum pm1, qm1, phi_n; /* * Set up the phase limits for the progress report. We do this * by passing minus the phase number. * * For prime generation: our initial filter finds things * coprime to everything below 2^16. Computing the product of | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | /* * RSA key generation. */ #include <assert.h> #include "ssh.h" #define RSA_EXPONENT 37 /* we like this prime */ int rsa_generate(struct RSAKey *key, int bits, progfn_t pfn, void *pfnparam) { Bignum pm1, qm1, phi_n; unsigned pfirst, qfirst; /* * Set up the phase limits for the progress report. We do this * by passing minus the phase number. * * For prime generation: our initial filter finds things * coprime to everything below 2^16. Computing the product of |
︙ | ︙ | |||
55 56 57 58 59 60 61 62 | /* * Generate p and q: primes with combined length `bits', not * congruent to 1 modulo e. (Strictly speaking, we wanted (p-1) * and e to be coprime, and (q-1) and e to be coprime, but in * general that's slightly more fiddly to arrange. By choosing * a prime e, we can simplify the criterion.) */ key->p = primegen(bits / 2, RSA_EXPONENT, 1, NULL, | > | | | 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 | /* * Generate p and q: primes with combined length `bits', not * congruent to 1 modulo e. (Strictly speaking, we wanted (p-1) * and e to be coprime, and (q-1) and e to be coprime, but in * general that's slightly more fiddly to arrange. By choosing * a prime e, we can simplify the criterion.) */ invent_firstbits(&pfirst, &qfirst); key->p = primegen(bits / 2, RSA_EXPONENT, 1, NULL, 1, pfn, pfnparam, pfirst); key->q = primegen(bits - bits / 2, RSA_EXPONENT, 1, NULL, 2, pfn, pfnparam, qfirst); /* * Ensure p > q, by swapping them if not. */ if (bignum_cmp(key->p, key->q) < 0) { Bignum t = key->p; key->p = key->q; |
︙ | ︙ | |||
86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 | qm1 = copybn(key->q); decbn(qm1); phi_n = bigmul(pm1, qm1); pfn(pfnparam, PROGFN_PROGRESS, 3, 3); freebn(pm1); freebn(qm1); key->private_exponent = modinv(key->exponent, phi_n); pfn(pfnparam, PROGFN_PROGRESS, 3, 4); key->iqmp = modinv(key->q, key->p); pfn(pfnparam, PROGFN_PROGRESS, 3, 5); /* * Clean up temporary numbers. */ freebn(phi_n); return 1; } | > > | 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 | qm1 = copybn(key->q); decbn(qm1); phi_n = bigmul(pm1, qm1); pfn(pfnparam, PROGFN_PROGRESS, 3, 3); freebn(pm1); freebn(qm1); key->private_exponent = modinv(key->exponent, phi_n); assert(key->private_exponent); pfn(pfnparam, PROGFN_PROGRESS, 3, 4); key->iqmp = modinv(key->q, key->p); assert(key->iqmp); pfn(pfnparam, PROGFN_PROGRESS, 3, 5); /* * Clean up temporary numbers. */ freebn(phi_n); return 1; } |
Changes to sshsh256.c.
︙ | ︙ | |||
213 214 215 216 217 218 219 220 221 222 223 224 225 226 | SHA256_Final(s, output); sfree(s); } const struct ssh_hash ssh_sha256 = { sha256_init, sha256_bytes, sha256_final, 32, "SHA-256" }; #ifdef TEST #include <stdio.h> #include <stdlib.h> #include <assert.h> | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 | SHA256_Final(s, output); sfree(s); } const struct ssh_hash ssh_sha256 = { sha256_init, sha256_bytes, sha256_final, 32, "SHA-256" }; /* ---------------------------------------------------------------------- * The above is the SHA-256 algorithm itself. Now we implement the * HMAC wrapper on it. */ static void *sha256_make_context(void) { return snewn(3, SHA256_State); } static void sha256_free_context(void *handle) { sfree(handle); } static void sha256_key_internal(void *handle, unsigned char *key, int len) { SHA256_State *keys = (SHA256_State *)handle; unsigned char foo[64]; int i; memset(foo, 0x36, 64); for (i = 0; i < len && i < 64; i++) foo[i] ^= key[i]; SHA256_Init(&keys[0]); SHA256_Bytes(&keys[0], foo, 64); memset(foo, 0x5C, 64); for (i = 0; i < len && i < 64; i++) foo[i] ^= key[i]; SHA256_Init(&keys[1]); SHA256_Bytes(&keys[1], foo, 64); smemclr(foo, 64); /* burn the evidence */ } static void sha256_key(void *handle, unsigned char *key) { sha256_key_internal(handle, key, 32); } static void hmacsha256_start(void *handle) { SHA256_State *keys = (SHA256_State *)handle; keys[2] = keys[0]; /* structure copy */ } static void hmacsha256_bytes(void *handle, unsigned char const *blk, int len) { SHA256_State *keys = (SHA256_State *)handle; SHA256_Bytes(&keys[2], (void *)blk, len); } static void hmacsha256_genresult(void *handle, unsigned char *hmac) { SHA256_State *keys = (SHA256_State *)handle; SHA256_State s; unsigned char intermediate[32]; s = keys[2]; /* structure copy */ SHA256_Final(&s, intermediate); s = keys[1]; /* structure copy */ SHA256_Bytes(&s, intermediate, 32); SHA256_Final(&s, hmac); } static void sha256_do_hmac(void *handle, unsigned char *blk, int len, unsigned long seq, unsigned char *hmac) { unsigned char seqbuf[4]; PUT_32BIT_MSB_FIRST(seqbuf, seq); hmacsha256_start(handle); hmacsha256_bytes(handle, seqbuf, 4); hmacsha256_bytes(handle, blk, len); hmacsha256_genresult(handle, hmac); } static void sha256_generate(void *handle, unsigned char *blk, int len, unsigned long seq) { sha256_do_hmac(handle, blk, len, seq, blk + len); } static int hmacsha256_verresult(void *handle, unsigned char const *hmac) { unsigned char correct[32]; hmacsha256_genresult(handle, correct); return !memcmp(correct, hmac, 32); } static int sha256_verify(void *handle, unsigned char *blk, int len, unsigned long seq) { unsigned char correct[32]; sha256_do_hmac(handle, blk, len, seq, correct); return !memcmp(correct, blk + len, 32); } const struct ssh_mac ssh_hmac_sha256 = { sha256_make_context, sha256_free_context, sha256_key, sha256_generate, sha256_verify, hmacsha256_start, hmacsha256_bytes, hmacsha256_genresult, hmacsha256_verresult, "hmac-sha2-256", 32, "HMAC-SHA-256" }; #ifdef TEST #include <stdio.h> #include <stdlib.h> #include <assert.h> |
︙ | ︙ |
Changes to sshsha.c.
︙ | ︙ | |||
23 24 25 26 27 28 29 30 31 32 33 34 35 36 | } void SHATransform(word32 * digest, word32 * block) { word32 w[80]; word32 a, b, c, d, e; int t; for (t = 0; t < 16; t++) w[t] = block[t]; for (t = 16; t < 80; t++) { word32 tmp = w[t - 3] ^ w[t - 8] ^ w[t - 14] ^ w[t - 16]; w[t] = rol(tmp, 1); | > > > > > > > > > > > > > > > | 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | } void SHATransform(word32 * digest, word32 * block) { word32 w[80]; word32 a, b, c, d, e; int t; #ifdef RANDOM_DIAGNOSTICS { extern int random_diagnostics; if (random_diagnostics) { int i; printf("SHATransform:"); for (i = 0; i < 5; i++) printf(" %08x", digest[i]); printf(" +"); for (i = 0; i < 16; i++) printf(" %08x", block[i]); } } #endif for (t = 0; t < 16; t++) w[t] = block[t]; for (t = 16; t < 80; t++) { word32 tmp = w[t - 3] ^ w[t - 8] ^ w[t - 14] ^ w[t - 16]; w[t] = rol(tmp, 1); |
︙ | ︙ | |||
79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 | } digest[0] += a; digest[1] += b; digest[2] += c; digest[3] += d; digest[4] += e; } /* ---------------------------------------------------------------------- * Outer SHA algorithm: take an arbitrary length byte string, * convert it into 16-word blocks with the prescribed padding at * the end, and pass those blocks to the core SHA algorithm. */ void SHA_Init(SHA_State * s) { SHA_Core_Init(s->h); s->blkused = 0; s->lenhi = s->lenlo = 0; } | > > > > > > > > > > > > > | | | 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 | } digest[0] += a; digest[1] += b; digest[2] += c; digest[3] += d; digest[4] += e; #ifdef RANDOM_DIAGNOSTICS { extern int random_diagnostics; if (random_diagnostics) { int i; printf(" ="); for (i = 0; i < 5; i++) printf(" %08x", digest[i]); printf("\n"); } } #endif } /* ---------------------------------------------------------------------- * Outer SHA algorithm: take an arbitrary length byte string, * convert it into 16-word blocks with the prescribed padding at * the end, and pass those blocks to the core SHA algorithm. */ void SHA_Init(SHA_State * s) { SHA_Core_Init(s->h); s->blkused = 0; s->lenhi = s->lenlo = 0; } void SHA_Bytes(SHA_State * s, const void *p, int len) { const unsigned char *q = (const unsigned char *) p; uint32 wordblock[16]; uint32 lenw = len; int i; /* * Update the length field. */ |
︙ | ︙ | |||
175 176 177 178 179 180 181 | output[i * 4] = (s->h[i] >> 24) & 0xFF; output[i * 4 + 1] = (s->h[i] >> 16) & 0xFF; output[i * 4 + 2] = (s->h[i] >> 8) & 0xFF; output[i * 4 + 3] = (s->h[i]) & 0xFF; } } | | | 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 | output[i * 4] = (s->h[i] >> 24) & 0xFF; output[i * 4 + 1] = (s->h[i] >> 16) & 0xFF; output[i * 4 + 2] = (s->h[i] >> 8) & 0xFF; output[i * 4 + 3] = (s->h[i]) & 0xFF; } } void SHA_Simple(const void *p, int len, unsigned char *output) { SHA_State s; SHA_Init(&s); SHA_Bytes(&s, p, len); SHA_Final(&s, output); } |
︙ | ︙ | |||
249 250 251 252 253 254 255 | memset(foo, 0x5C, 64); for (i = 0; i < len && i < 64; i++) foo[i] ^= key[i]; SHA_Init(&keys[1]); SHA_Bytes(&keys[1], foo, 64); | | | 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 | memset(foo, 0x5C, 64); for (i = 0; i < len && i < 64; i++) foo[i] ^= key[i]; SHA_Init(&keys[1]); SHA_Bytes(&keys[1], foo, 64); smemclr(foo, 64); /* burn the evidence */ } static void sha1_key(void *handle, unsigned char *key) { sha1_key_internal(handle, key, 20); } |
︙ | ︙ | |||
293 294 295 296 297 298 299 | } static void sha1_do_hmac(void *handle, unsigned char *blk, int len, unsigned long seq, unsigned char *hmac) { unsigned char seqbuf[4]; | < < < < | | 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 | } static void sha1_do_hmac(void *handle, unsigned char *blk, int len, unsigned long seq, unsigned char *hmac) { unsigned char seqbuf[4]; PUT_32BIT_MSB_FIRST(seqbuf, seq); hmacsha1_start(handle); hmacsha1_bytes(handle, seqbuf, 4); hmacsha1_bytes(handle, blk, len); hmacsha1_genresult(handle, hmac); } static void sha1_generate(void *handle, unsigned char *blk, int len, |
︙ | ︙ |
Added sshshare.c.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 2060 2061 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 | /* * Support for SSH connection sharing, i.e. permitting one PuTTY to * open its own channels over the SSH session being run by another. */ /* * Discussion and technical documentation * ====================================== * * The basic strategy for PuTTY's implementation of SSH connection * sharing is to have a single 'upstream' PuTTY process, which manages * the real SSH connection and all the cryptography, and then zero or * more 'downstream' PuTTYs, which never talk to the real host but * only talk to the upstream through local IPC (Unix-domain sockets or * Windows named pipes). * * The downstreams communicate with the upstream using a protocol * derived from SSH itself, which I'll document in detail below. In * brief, though: the downstream->upstream protocol uses a trivial * binary packet protocol (just length/type/data) to encapsulate * unencrypted SSH messages, and downstreams talk to the upstream more * or less as if it was an SSH server itself. (So downstreams can * themselves open multiple SSH channels, for example, by sending * multiple SSH2_MSG_CHANNEL_OPENs; they can send CHANNEL_REQUESTs of * their choice within each channel, and they handle their own * WINDOW_ADJUST messages.) * * The upstream would ideally handle these downstreams by just putting * their messages into the queue for proper SSH-2 encapsulation and * encryption and sending them straight on to the server. However, * that's not quite feasible as written, because client-side channel * IDs could easily conflict (between multiple downstreams, or between * a downstream and the upstream). To protect against that, the * upstream rewrites the client-side channel IDs in messages it passes * on to the server, so that it's performing what you might describe * as 'channel-number NAT'. Then the upstream remembers which of its * own channel IDs are channels it's managing itself, and which are * placeholders associated with a particular downstream, so that when * replies come in from the server they can be sent on to the relevant * downstream (after un-NATting the channel number, of course). * * Global requests from downstreams are only accepted if the upstream * knows what to do about them; currently the only such requests are * the ones having to do with remote-to-local port forwarding (in * which, again, the upstream remembers that some of the forwardings * it's asked the server to set up were on behalf of particular * downstreams, and sends the incoming CHANNEL_OPENs to those * downstreams when connections come in). * * Other fiddly pieces of this mechanism are X forwarding and * (OpenSSH-style) agent forwarding. Both of these have a fundamental * problem arising from the protocol design: that the CHANNEL_OPEN * from the server introducing a forwarded connection does not carry * any indication of which session channel gave rise to it; so if * session channels from multiple downstreams enable those forwarding * methods, it's hard for the upstream to know which downstream to * send the resulting connections back to. * * For X forwarding, we can work around this in a really painful way * by using the fake X11 authorisation data sent to the server as part * of the forwarding setup: upstream ensures that every X forwarding * request carries distinguishable fake auth data, and then when X * connections come in it waits to see the auth data in the X11 setup * message before it decides which downstream to pass the connection * on to. * * For agent forwarding, that workaround is unavailable. As a result, * this system (and, as far as I can think of, any other system too) * has the fundamental constraint that it can only forward one SSH * agent - it can't forward two agents to different session channels. * So downstreams can request agent forwarding if they like, but if * they do, they'll get whatever SSH agent is known to the upstream * (if any) forwarded to their sessions. * * Downstream-to-upstream protocol * ------------------------------- * * Here I document in detail the protocol spoken between PuTTY * downstreams and upstreams over local IPC. The IPC mechanism can * vary between host platforms, but the protocol is the same. * * The protocol commences with a version exchange which is exactly * like the SSH-2 one, in that each side sends a single line of text * of the form * * <protocol>-<version>-<softwareversion> [comments] \r\n * * The only difference is that in real SSH-2, <protocol> is the string * "SSH", whereas in this protocol the string is * "SSHCONNECTION@putty.projects.tartarus.org". * * (The SSH RFCs allow many protocol-level identifier namespaces to be * extended by implementors without central standardisation as long as * they suffix "@" and a domain name they control to their new ids. * RFC 4253 does not define this particular name to be changeable at * all, but I like to think this is obviously how it would have done * so if the working group had foreseen the need :-) * * Thereafter, all data exchanged consists of a sequence of binary * packets concatenated end-to-end, each of which is of the form * * uint32 length of packet, N * byte[N] N bytes of packet data * * and, since these are SSH-2 messages, the first data byte is taken * to be the packet type code. * * These messages are interpreted as those of an SSH connection, after * userauth completes, and without any repeat key exchange. * Specifically, any message from the SSH Connection Protocol is * permitted, and also SSH_MSG_IGNORE, SSH_MSG_DEBUG, * SSH_MSG_DISCONNECT and SSH_MSG_UNIMPLEMENTED from the SSH Transport * Protocol. * * This protocol imposes a few additional requirements, over and above * those of the standard SSH Connection Protocol: * * Message sizes are not permitted to exceed 0x4010 (16400) bytes, * including their length header. * * When the server (i.e. really the PuTTY upstream) sends * SSH_MSG_CHANNEL_OPEN with channel type "x11", and the client * (downstream) responds with SSH_MSG_CHANNEL_OPEN_CONFIRMATION, that * confirmation message MUST include an initial window size of at * least 256. (Rationale: this is a bit of a fudge which makes it * easier, by eliminating the possibility of nasty edge cases, for an * upstream to arrange not to pass the CHANNEL_OPEN on to downstream * until after it's seen the X11 auth data to decide which downstream * it needs to go to.) */ #include <stdio.h> #include <stdlib.h> #include <assert.h> #include <limits.h> #include "putty.h" #include "tree234.h" #include "ssh.h" struct ssh_sharing_state { const struct plug_function_table *fn; /* the above variable absolutely *must* be the first in this structure */ char *sockname; /* the socket name, kept for cleanup */ Socket listensock; /* the master listening Socket */ tree234 *connections; /* holds ssh_sharing_connstates */ unsigned nextid; /* preferred id for next connstate */ Ssh ssh; /* instance of the ssh backend */ char *server_verstring; /* server version string after "SSH-" */ }; struct share_globreq; struct ssh_sharing_connstate { const struct plug_function_table *fn; /* the above variable absolutely *must* be the first in this structure */ unsigned id; /* used to identify this downstream in log messages */ Socket sock; /* the Socket for this connection */ struct ssh_sharing_state *parent; int crLine; /* coroutine state for share_receive */ int sent_verstring, got_verstring, curr_packetlen; unsigned char recvbuf[0x4010]; int recvlen; /* * Assorted state we have to remember about this downstream, so * that we can clean it up appropriately when the downstream goes * away. */ /* Channels which don't have a downstream id, i.e. we've passed a * CHANNEL_OPEN down from the server but not had an * OPEN_CONFIRMATION or OPEN_FAILURE back. If downstream goes * away, we respond to all of these with OPEN_FAILURE. */ tree234 *halfchannels; /* stores 'struct share_halfchannel' */ /* Channels which do have a downstream id. We need to index these * by both server id and upstream id, so we can find a channel * when handling either an upward or a downward message referring * to it. */ tree234 *channels_by_us; /* stores 'struct share_channel' */ tree234 *channels_by_server; /* stores 'struct share_channel' */ /* Another class of channel which doesn't have a downstream id. * The difference between these and halfchannels is that xchannels * do have an *upstream* id, because upstream has already accepted * the channel request from the server. This arises in the case of * X forwarding, where we have to accept the request and read the * X authorisation data before we know whether the channel needs * to be forwarded to a downstream. */ tree234 *xchannels_by_us; /* stores 'struct share_xchannel' */ tree234 *xchannels_by_server; /* stores 'struct share_xchannel' */ /* Remote port forwarding requests in force. */ tree234 *forwardings; /* stores 'struct share_forwarding' */ /* Global requests we've sent on to the server, pending replies. */ struct share_globreq *globreq_head, *globreq_tail; }; struct share_halfchannel { unsigned server_id; }; /* States of a share_channel. */ enum { OPEN, SENT_CLOSE, RCVD_CLOSE, /* Downstream has sent CHANNEL_OPEN but server hasn't replied yet. * If downstream goes away when a channel is in this state, we * must wait for the server's response before starting to send * CLOSE. Channels in this state are also not held in * channels_by_server, because their server_id field is * meaningless. */ UNACKNOWLEDGED }; struct share_channel { unsigned downstream_id, upstream_id, server_id; int downstream_maxpkt; int state; /* * Some channels (specifically, channels on which downstream has * sent "x11-req") have the additional function of storing a set * of downstream X authorisation data and a handle to an upstream * fake set. */ struct X11FakeAuth *x11_auth_upstream; int x11_auth_proto; char *x11_auth_data; int x11_auth_datalen; int x11_one_shot; }; struct share_forwarding { char *host; int port; int active; /* has the server sent REQUEST_SUCCESS? */ }; struct share_xchannel_message { struct share_xchannel_message *next; int type; unsigned char *data; int datalen; }; struct share_xchannel { unsigned upstream_id, server_id; /* * xchannels come in two flavours: live and dead. Live ones are * waiting for an OPEN_CONFIRMATION or OPEN_FAILURE from * downstream; dead ones have had an OPEN_FAILURE, so they only * exist as a means of letting us conveniently respond to further * channel messages from the server until such time as the server * sends us CHANNEL_CLOSE. */ int live; /* * When we receive OPEN_CONFIRMATION, we will need to send a * WINDOW_ADJUST to the server to synchronise the windows. For * this purpose we need to know what window we have so far offered * the server. We record this as exactly the value in the * OPEN_CONFIRMATION that upstream sent us, adjusted by the amount * by which the two X greetings differed in length. */ int window; /* * Linked list of SSH messages from the server relating to this * channel, which we queue up until downstream sends us an * OPEN_CONFIRMATION and we can belatedly send them all on. */ struct share_xchannel_message *msghead, *msgtail; }; enum { GLOBREQ_TCPIP_FORWARD, GLOBREQ_CANCEL_TCPIP_FORWARD }; struct share_globreq { struct share_globreq *next; int type; int want_reply; struct share_forwarding *fwd; }; static int share_connstate_cmp(void *av, void *bv) { const struct ssh_sharing_connstate *a = (const struct ssh_sharing_connstate *)av; const struct ssh_sharing_connstate *b = (const struct ssh_sharing_connstate *)bv; if (a->id < b->id) return -1; else if (a->id > b->id) return +1; else return 0; } static unsigned share_find_unused_id (struct ssh_sharing_state *sharestate, unsigned first) { int low_orig, low, mid, high, high_orig; struct ssh_sharing_connstate *cs; unsigned ret; /* * Find the lowest unused downstream ID greater or equal to * 'first'. * * Begin by seeing if 'first' itself is available. If it is, we'll * just return it; if it's already in the tree, we'll find the * tree index where it appears and use that for the next stage. */ { struct ssh_sharing_connstate dummy; dummy.id = first; cs = findrelpos234(sharestate->connections, &dummy, NULL, REL234_GE, &low_orig); if (!cs) return first; } /* * Now binary-search using the counted B-tree, to find the largest * ID which is in a contiguous sequence from the beginning of that * range. */ low = low_orig; high = high_orig = count234(sharestate->connections); while (high - low > 1) { mid = (high + low) / 2; cs = index234(sharestate->connections, mid); if (cs->id == first + (mid - low_orig)) low = mid; /* this one is still in the sequence */ else high = mid; /* this one is past the end */ } /* * Now low is the tree index of the largest ID in the initial * sequence. So the return value is one more than low's id, and we * know low's id is given by the formula in the binary search loop * above. * * (If an SSH connection went on for _enormously_ long, we might * reach a point where all ids from 'first' to UINT_MAX were in * use. In that situation the formula below would wrap round by * one and return zero, which is conveniently the right way to * signal 'no id available' from this function.) */ ret = first + (low - low_orig) + 1; { struct ssh_sharing_connstate dummy; dummy.id = ret; assert(NULL == find234(sharestate->connections, &dummy, NULL)); } return ret; } static int share_halfchannel_cmp(void *av, void *bv) { const struct share_halfchannel *a = (const struct share_halfchannel *)av; const struct share_halfchannel *b = (const struct share_halfchannel *)bv; if (a->server_id < b->server_id) return -1; else if (a->server_id > b->server_id) return +1; else return 0; } static int share_channel_us_cmp(void *av, void *bv) { const struct share_channel *a = (const struct share_channel *)av; const struct share_channel *b = (const struct share_channel *)bv; if (a->upstream_id < b->upstream_id) return -1; else if (a->upstream_id > b->upstream_id) return +1; else return 0; } static int share_channel_server_cmp(void *av, void *bv) { const struct share_channel *a = (const struct share_channel *)av; const struct share_channel *b = (const struct share_channel *)bv; if (a->server_id < b->server_id) return -1; else if (a->server_id > b->server_id) return +1; else return 0; } static int share_xchannel_us_cmp(void *av, void *bv) { const struct share_xchannel *a = (const struct share_xchannel *)av; const struct share_xchannel *b = (const struct share_xchannel *)bv; if (a->upstream_id < b->upstream_id) return -1; else if (a->upstream_id > b->upstream_id) return +1; else return 0; } static int share_xchannel_server_cmp(void *av, void *bv) { const struct share_xchannel *a = (const struct share_xchannel *)av; const struct share_xchannel *b = (const struct share_xchannel *)bv; if (a->server_id < b->server_id) return -1; else if (a->server_id > b->server_id) return +1; else return 0; } static int share_forwarding_cmp(void *av, void *bv) { const struct share_forwarding *a = (const struct share_forwarding *)av; const struct share_forwarding *b = (const struct share_forwarding *)bv; int i; if ((i = strcmp(a->host, b->host)) != 0) return i; else if (a->port < b->port) return -1; else if (a->port > b->port) return +1; else return 0; } static void share_xchannel_free(struct share_xchannel *xc) { while (xc->msghead) { struct share_xchannel_message *tmp = xc->msghead; xc->msghead = tmp->next; sfree(tmp); } sfree(xc); } static void share_connstate_free(struct ssh_sharing_connstate *cs) { struct share_halfchannel *hc; struct share_xchannel *xc; struct share_channel *chan; struct share_forwarding *fwd; while ((hc = (struct share_halfchannel *) delpos234(cs->halfchannels, 0)) != NULL) sfree(hc); freetree234(cs->halfchannels); /* All channels live in 'channels_by_us' but only some in * 'channels_by_server', so we use the former to find the list of * ones to free */ freetree234(cs->channels_by_server); while ((chan = (struct share_channel *) delpos234(cs->channels_by_us, 0)) != NULL) sfree(chan); freetree234(cs->channels_by_us); /* But every xchannel is in both trees, so it doesn't matter which * we use to free them. */ while ((xc = (struct share_xchannel *) delpos234(cs->xchannels_by_us, 0)) != NULL) share_xchannel_free(xc); freetree234(cs->xchannels_by_us); freetree234(cs->xchannels_by_server); while ((fwd = (struct share_forwarding *) delpos234(cs->forwardings, 0)) != NULL) sfree(fwd); freetree234(cs->forwardings); while (cs->globreq_head) { struct share_globreq *globreq = cs->globreq_head; cs->globreq_head = cs->globreq_head->next; sfree(globreq); } sfree(cs); } void sharestate_free(void *v) { struct ssh_sharing_state *sharestate = (struct ssh_sharing_state *)v; struct ssh_sharing_connstate *cs; platform_ssh_share_cleanup(sharestate->sockname); while ((cs = (struct ssh_sharing_connstate *) delpos234(sharestate->connections, 0)) != NULL) { share_connstate_free(cs); } freetree234(sharestate->connections); sfree(sharestate->server_verstring); sfree(sharestate->sockname); sfree(sharestate); } static struct share_halfchannel *share_add_halfchannel (struct ssh_sharing_connstate *cs, unsigned server_id) { struct share_halfchannel *hc = snew(struct share_halfchannel); hc->server_id = server_id; if (add234(cs->halfchannels, hc) != hc) { /* Duplicate?! */ sfree(hc); return NULL; } else { return hc; } } static struct share_halfchannel *share_find_halfchannel (struct ssh_sharing_connstate *cs, unsigned server_id) { struct share_halfchannel dummyhc; dummyhc.server_id = server_id; return find234(cs->halfchannels, &dummyhc, NULL); } static void share_remove_halfchannel(struct ssh_sharing_connstate *cs, struct share_halfchannel *hc) { del234(cs->halfchannels, hc); sfree(hc); } static struct share_channel *share_add_channel (struct ssh_sharing_connstate *cs, unsigned downstream_id, unsigned upstream_id, unsigned server_id, int state, int maxpkt) { struct share_channel *chan = snew(struct share_channel); chan->downstream_id = downstream_id; chan->upstream_id = upstream_id; chan->server_id = server_id; chan->state = state; chan->downstream_maxpkt = maxpkt; chan->x11_auth_upstream = NULL; chan->x11_auth_data = NULL; chan->x11_auth_proto = -1; chan->x11_auth_datalen = 0; chan->x11_one_shot = 0; if (add234(cs->channels_by_us, chan) != chan) { sfree(chan); return NULL; } if (chan->state != UNACKNOWLEDGED) { if (add234(cs->channels_by_server, chan) != chan) { del234(cs->channels_by_us, chan); sfree(chan); return NULL; } } return chan; } static void share_channel_set_server_id(struct ssh_sharing_connstate *cs, struct share_channel *chan, unsigned server_id, int newstate) { chan->server_id = server_id; chan->state = newstate; assert(newstate != UNACKNOWLEDGED); add234(cs->channels_by_server, chan); } static struct share_channel *share_find_channel_by_upstream (struct ssh_sharing_connstate *cs, unsigned upstream_id) { struct share_channel dummychan; dummychan.upstream_id = upstream_id; return find234(cs->channels_by_us, &dummychan, NULL); } static struct share_channel *share_find_channel_by_server (struct ssh_sharing_connstate *cs, unsigned server_id) { struct share_channel dummychan; dummychan.server_id = server_id; return find234(cs->channels_by_server, &dummychan, NULL); } static void share_remove_channel(struct ssh_sharing_connstate *cs, struct share_channel *chan) { del234(cs->channels_by_us, chan); del234(cs->channels_by_server, chan); if (chan->x11_auth_upstream) ssh_sharing_remove_x11_display(cs->parent->ssh, chan->x11_auth_upstream); sfree(chan->x11_auth_data); sfree(chan); } static struct share_xchannel *share_add_xchannel (struct ssh_sharing_connstate *cs, unsigned upstream_id, unsigned server_id) { struct share_xchannel *xc = snew(struct share_xchannel); xc->upstream_id = upstream_id; xc->server_id = server_id; xc->live = TRUE; xc->msghead = xc->msgtail = NULL; if (add234(cs->xchannels_by_us, xc) != xc) { sfree(xc); return NULL; } if (add234(cs->xchannels_by_server, xc) != xc) { del234(cs->xchannels_by_us, xc); sfree(xc); return NULL; } return xc; } static struct share_xchannel *share_find_xchannel_by_upstream (struct ssh_sharing_connstate *cs, unsigned upstream_id) { struct share_xchannel dummyxc; dummyxc.upstream_id = upstream_id; return find234(cs->xchannels_by_us, &dummyxc, NULL); } static struct share_xchannel *share_find_xchannel_by_server (struct ssh_sharing_connstate *cs, unsigned server_id) { struct share_xchannel dummyxc; dummyxc.server_id = server_id; return find234(cs->xchannels_by_server, &dummyxc, NULL); } static void share_remove_xchannel(struct ssh_sharing_connstate *cs, struct share_xchannel *xc) { del234(cs->xchannels_by_us, xc); del234(cs->xchannels_by_server, xc); share_xchannel_free(xc); } static struct share_forwarding *share_add_forwarding (struct ssh_sharing_connstate *cs, const char *host, int port) { struct share_forwarding *fwd = snew(struct share_forwarding); fwd->host = dupstr(host); fwd->port = port; fwd->active = FALSE; if (add234(cs->forwardings, fwd) != fwd) { /* Duplicate?! */ sfree(fwd); return NULL; } return fwd; } static struct share_forwarding *share_find_forwarding (struct ssh_sharing_connstate *cs, const char *host, int port) { struct share_forwarding dummyfwd, *ret; dummyfwd.host = dupstr(host); dummyfwd.port = port; ret = find234(cs->forwardings, &dummyfwd, NULL); sfree(dummyfwd.host); return ret; } static void share_remove_forwarding(struct ssh_sharing_connstate *cs, struct share_forwarding *fwd) { del234(cs->forwardings, fwd); sfree(fwd); } static void send_packet_to_downstream(struct ssh_sharing_connstate *cs, int type, const void *pkt, int pktlen, struct share_channel *chan) { if (!cs->sock) /* throw away all packets destined for a dead downstream */ return; if (type == SSH2_MSG_CHANNEL_DATA) { /* * Special case which we take care of at a low level, so as to * be sure to apply it in all cases. On rare occasions we * might find that we have a channel for which the * downstream's maximum packet size exceeds the max packet * size we presented to the server on its behalf. (This can * occur in X11 forwarding, where we have to send _our_ * CHANNEL_OPEN_CONFIRMATION before we discover which if any * downstream the channel is destined for, so if that * downstream turns out to present a smaller max packet size * then we're in this situation.) * * If that happens, we just chop up the packet into pieces and * send them as separate CHANNEL_DATA packets. */ const char *upkt = (const char *)pkt; char header[13]; /* 4 length + 1 type + 4 channel id + 4 string len */ int len = toint(GET_32BIT(upkt + 4)); upkt += 8; /* skip channel id + length field */ if (len < 0 || len > pktlen - 8) len = pktlen - 8; do { int this_len = (len > chan->downstream_maxpkt ? chan->downstream_maxpkt : len); PUT_32BIT(header, this_len + 9); header[4] = type; PUT_32BIT(header + 5, chan->downstream_id); PUT_32BIT(header + 9, this_len); sk_write(cs->sock, header, 13); sk_write(cs->sock, upkt, this_len); len -= this_len; upkt += this_len; } while (len > 0); } else { /* * Just do the obvious thing. */ char header[9]; PUT_32BIT(header, pktlen + 1); header[4] = type; sk_write(cs->sock, header, 5); sk_write(cs->sock, pkt, pktlen); } } static void share_try_cleanup(struct ssh_sharing_connstate *cs) { int i; struct share_halfchannel *hc; struct share_channel *chan; struct share_forwarding *fwd; /* * Any half-open channels, i.e. those for which we'd received * CHANNEL_OPEN from the server but not passed back a response * from downstream, should be responded to with OPEN_FAILURE. */ while ((hc = (struct share_halfchannel *) index234(cs->halfchannels, 0)) != NULL) { static const char reason[] = "PuTTY downstream no longer available"; static const char lang[] = "en"; unsigned char packet[256]; int pos = 0; PUT_32BIT(packet + pos, hc->server_id); pos += 4; PUT_32BIT(packet + pos, SSH2_OPEN_CONNECT_FAILED); pos += 4; PUT_32BIT(packet + pos, strlen(reason)); pos += 4; memcpy(packet + pos, reason, strlen(reason)); pos += strlen(reason); PUT_32BIT(packet + pos, strlen(lang)); pos += 4; memcpy(packet + pos, lang, strlen(lang)); pos += strlen(lang); ssh_send_packet_from_downstream(cs->parent->ssh, cs->id, SSH2_MSG_CHANNEL_OPEN_FAILURE, packet, pos, "cleanup after" " downstream went away"); share_remove_halfchannel(cs, hc); } /* * Any actually open channels should have a CHANNEL_CLOSE sent for * them, unless we've already done so. We won't be able to * actually clean them up until CHANNEL_CLOSE comes back from the * server, though (unless the server happens to have sent a CLOSE * already). * * Another annoying exception is UNACKNOWLEDGED channels, i.e. * we've _sent_ a CHANNEL_OPEN to the server but not received an * OPEN_CONFIRMATION or OPEN_FAILURE. We must wait for a reply * before closing the channel, because until we see that reply we * won't have the server's channel id to put in the close message. */ for (i = 0; (chan = (struct share_channel *) index234(cs->channels_by_us, i)) != NULL; i++) { unsigned char packet[256]; int pos = 0; if (chan->state != SENT_CLOSE && chan->state != UNACKNOWLEDGED) { PUT_32BIT(packet + pos, chan->server_id); pos += 4; ssh_send_packet_from_downstream(cs->parent->ssh, cs->id, SSH2_MSG_CHANNEL_CLOSE, packet, pos, "cleanup after" " downstream went away"); if (chan->state != RCVD_CLOSE) { chan->state = SENT_CLOSE; } else { /* In this case, we _can_ clear up the channel now. */ ssh_delete_sharing_channel(cs->parent->ssh, chan->upstream_id); share_remove_channel(cs, chan); i--; /* don't accidentally skip one as a result */ } } } /* * Any remote port forwardings we're managing on behalf of this * downstream should be cancelled. Again, we must defer those for * which we haven't yet seen REQUEST_SUCCESS/FAILURE. * * We take a fire-and-forget approach during cleanup, not * bothering to set want_reply. */ for (i = 0; (fwd = (struct share_forwarding *) index234(cs->forwardings, i)) != NULL; i++) { if (fwd->active) { static const char request[] = "cancel-tcpip-forward"; char *packet = snewn(256 + strlen(fwd->host), char); int pos = 0; PUT_32BIT(packet + pos, strlen(request)); pos += 4; memcpy(packet + pos, request, strlen(request)); pos += strlen(request); packet[pos++] = 0; /* !want_reply */ PUT_32BIT(packet + pos, strlen(fwd->host)); pos += 4; memcpy(packet + pos, fwd->host, strlen(fwd->host)); pos += strlen(fwd->host); PUT_32BIT(packet + pos, fwd->port); pos += 4; ssh_send_packet_from_downstream(cs->parent->ssh, cs->id, SSH2_MSG_GLOBAL_REQUEST, packet, pos, "cleanup after" " downstream went away"); share_remove_forwarding(cs, fwd); i--; /* don't accidentally skip one as a result */ } } if (count234(cs->halfchannels) == 0 && count234(cs->channels_by_us) == 0 && count234(cs->forwardings) == 0) { /* * Now we're _really_ done, so we can get rid of cs completely. */ del234(cs->parent->connections, cs); ssh_sharing_downstream_disconnected(cs->parent->ssh, cs->id); share_connstate_free(cs); } } static void share_begin_cleanup(struct ssh_sharing_connstate *cs) { sk_close(cs->sock); cs->sock = NULL; share_try_cleanup(cs); } static void share_disconnect(struct ssh_sharing_connstate *cs, const char *message) { static const char lang[] = "en"; int msglen = strlen(message); char *packet = snewn(msglen + 256, char); int pos = 0; PUT_32BIT(packet + pos, SSH2_DISCONNECT_PROTOCOL_ERROR); pos += 4; PUT_32BIT(packet + pos, msglen); pos += 4; memcpy(packet + pos, message, msglen); pos += msglen; PUT_32BIT(packet + pos, strlen(lang)); pos += 4; memcpy(packet + pos, lang, strlen(lang)); pos += strlen(lang); send_packet_to_downstream(cs, SSH2_MSG_DISCONNECT, packet, pos, NULL); share_begin_cleanup(cs); } static int share_closing(Plug plug, const char *error_msg, int error_code, int calling_back) { struct ssh_sharing_connstate *cs = (struct ssh_sharing_connstate *)plug; if (error_msg) ssh_sharing_logf(cs->parent->ssh, cs->id, "%s", error_msg); share_begin_cleanup(cs); return 1; } static int getstring_inner(const void *vdata, int datalen, char **out, int *outlen) { const unsigned char *data = (const unsigned char *)vdata; int len; if (datalen < 4) return FALSE; len = toint(GET_32BIT(data)); if (len < 0 || len > datalen - 4) return FALSE; if (outlen) *outlen = len + 4; /* total size including length field */ if (out) *out = dupprintf("%.*s", len, (char *)data + 4); return TRUE; } static char *getstring(const void *data, int datalen) { char *ret; if (getstring_inner(data, datalen, &ret, NULL)) return ret; else return NULL; } static int getstring_size(const void *data, int datalen) { int ret; if (getstring_inner(data, datalen, NULL, &ret)) return ret; else return -1; } /* * Append a message to the end of an xchannel's queue, with the length * and type code filled in and the data block allocated but * uninitialised. */ struct share_xchannel_message *share_xchannel_add_message (struct share_xchannel *xc, int type, int len) { unsigned char *block; struct share_xchannel_message *msg; /* * Be a little tricksy here by allocating a single memory block * containing both the 'struct share_xchannel_message' and the * actual data. Simplifies freeing it later. */ block = smalloc(sizeof(struct share_xchannel_message) + len); msg = (struct share_xchannel_message *)block; msg->data = block + sizeof(struct share_xchannel_message); msg->datalen = len; msg->type = type; /* * Queue it in the xchannel. */ if (xc->msgtail) xc->msgtail->next = msg; else xc->msghead = msg; msg->next = NULL; xc->msgtail = msg; return msg; } void share_dead_xchannel_respond(struct ssh_sharing_connstate *cs, struct share_xchannel *xc) { /* * Handle queued incoming messages from the server destined for an * xchannel which is dead (i.e. downstream sent OPEN_FAILURE). */ int delete = FALSE; while (xc->msghead) { struct share_xchannel_message *msg = xc->msghead; xc->msghead = msg->next; if (msg->type == SSH2_MSG_CHANNEL_REQUEST && msg->datalen > 4) { /* * A CHANNEL_REQUEST is responded to by sending * CHANNEL_FAILURE, if it has want_reply set. */ int wantreplypos = getstring_size(msg->data, msg->datalen); if (wantreplypos > 0 && wantreplypos < msg->datalen && msg->data[wantreplypos] != 0) { unsigned char id[4]; PUT_32BIT(id, xc->server_id); ssh_send_packet_from_downstream (cs->parent->ssh, cs->id, SSH2_MSG_CHANNEL_FAILURE, id, 4, "downstream refused X channel open"); } } else if (msg->type == SSH2_MSG_CHANNEL_CLOSE) { /* * On CHANNEL_CLOSE we can discard the channel completely. */ delete = TRUE; } sfree(msg); } xc->msgtail = NULL; if (delete) { ssh_delete_sharing_channel(cs->parent->ssh, xc->upstream_id); share_remove_xchannel(cs, xc); } } void share_xchannel_confirmation(struct ssh_sharing_connstate *cs, struct share_xchannel *xc, struct share_channel *chan, unsigned downstream_window) { unsigned char window_adjust[8]; /* * Send all the queued messages downstream. */ while (xc->msghead) { struct share_xchannel_message *msg = xc->msghead; xc->msghead = msg->next; if (msg->datalen >= 4) PUT_32BIT(msg->data, chan->downstream_id); send_packet_to_downstream(cs, msg->type, msg->data, msg->datalen, chan); sfree(msg); } /* * Send a WINDOW_ADJUST back upstream, to synchronise the window * size downstream thinks it's presented with the one we've * actually presented. */ PUT_32BIT(window_adjust, xc->server_id); PUT_32BIT(window_adjust + 4, downstream_window - xc->window); ssh_send_packet_from_downstream(cs->parent->ssh, cs->id, SSH2_MSG_CHANNEL_WINDOW_ADJUST, window_adjust, 8, "window adjustment after" " downstream accepted X channel"); } void share_xchannel_failure(struct ssh_sharing_connstate *cs, struct share_xchannel *xc) { /* * If downstream refuses to open our X channel at all for some * reason, we must respond by sending an emergency CLOSE upstream. */ unsigned char id[4]; PUT_32BIT(id, xc->server_id); ssh_send_packet_from_downstream (cs->parent->ssh, cs->id, SSH2_MSG_CHANNEL_CLOSE, id, 4, "downstream refused X channel open"); /* * Now mark the xchannel as dead, and respond to anything sent on * it until we see CLOSE for it in turn. */ xc->live = FALSE; share_dead_xchannel_respond(cs, xc); } void share_setup_x11_channel(void *csv, void *chanv, unsigned upstream_id, unsigned server_id, unsigned server_currwin, unsigned server_maxpkt, unsigned client_adjusted_window, const char *peer_addr, int peer_port, int endian, int protomajor, int protominor, const void *initial_data, int initial_len) { struct ssh_sharing_connstate *cs = (struct ssh_sharing_connstate *)csv; struct share_channel *chan = (struct share_channel *)chanv; struct share_xchannel *xc; struct share_xchannel_message *msg; void *greeting; int greeting_len; unsigned char *pkt; int pktlen; /* * Create an xchannel containing data we've already received from * the X client, and preload it with a CHANNEL_DATA message * containing our own made-up authorisation greeting and any * additional data sent from the server so far. */ xc = share_add_xchannel(cs, upstream_id, server_id); greeting = x11_make_greeting(endian, protomajor, protominor, chan->x11_auth_proto, chan->x11_auth_data, chan->x11_auth_datalen, peer_addr, peer_port, &greeting_len); msg = share_xchannel_add_message(xc, SSH2_MSG_CHANNEL_DATA, 8 + greeting_len + initial_len); /* leave the channel id field unfilled - we don't know the * downstream id yet, of course */ PUT_32BIT(msg->data + 4, greeting_len + initial_len); memcpy(msg->data + 8, greeting, greeting_len); memcpy(msg->data + 8 + greeting_len, initial_data, initial_len); sfree(greeting); xc->window = client_adjusted_window + greeting_len; /* * Send on a CHANNEL_OPEN to downstream. */ pktlen = 27 + strlen(peer_addr); pkt = snewn(pktlen, unsigned char); PUT_32BIT(pkt, 3); /* strlen("x11") */ memcpy(pkt+4, "x11", 3); PUT_32BIT(pkt+7, server_id); PUT_32BIT(pkt+11, server_currwin); PUT_32BIT(pkt+15, server_maxpkt); PUT_32BIT(pkt+19, strlen(peer_addr)); memcpy(pkt+23, peer_addr, strlen(peer_addr)); PUT_32BIT(pkt+23+strlen(peer_addr), peer_port); send_packet_to_downstream(cs, SSH2_MSG_CHANNEL_OPEN, pkt, pktlen, NULL); sfree(pkt); /* * If this was a once-only X forwarding, clean it up now. */ if (chan->x11_one_shot) { ssh_sharing_remove_x11_display(cs->parent->ssh, chan->x11_auth_upstream); chan->x11_auth_upstream = NULL; sfree(chan->x11_auth_data); chan->x11_auth_proto = -1; chan->x11_auth_datalen = 0; chan->x11_one_shot = 0; } } void share_got_pkt_from_server(void *csv, int type, unsigned char *pkt, int pktlen) { struct ssh_sharing_connstate *cs = (struct ssh_sharing_connstate *)csv; struct share_globreq *globreq; int id_pos; unsigned upstream_id, server_id; struct share_channel *chan; struct share_xchannel *xc; switch (type) { case SSH2_MSG_REQUEST_SUCCESS: case SSH2_MSG_REQUEST_FAILURE: globreq = cs->globreq_head; if (globreq->type == GLOBREQ_TCPIP_FORWARD) { if (type == SSH2_MSG_REQUEST_FAILURE) { share_remove_forwarding(cs, globreq->fwd); } else { globreq->fwd->active = TRUE; } } else if (globreq->type == GLOBREQ_CANCEL_TCPIP_FORWARD) { if (type == SSH2_MSG_REQUEST_SUCCESS) { share_remove_forwarding(cs, globreq->fwd); } } if (globreq->want_reply) { send_packet_to_downstream(cs, type, pkt, pktlen, NULL); } cs->globreq_head = globreq->next; sfree(globreq); if (cs->globreq_head == NULL) cs->globreq_tail = NULL; if (!cs->sock) { /* Retry cleaning up this connection, in case that reply * was the last thing we were waiting for. */ share_try_cleanup(cs); } break; case SSH2_MSG_CHANNEL_OPEN: id_pos = getstring_size(pkt, pktlen); assert(id_pos >= 0); server_id = GET_32BIT(pkt + id_pos); share_add_halfchannel(cs, server_id); send_packet_to_downstream(cs, type, pkt, pktlen, NULL); break; case SSH2_MSG_CHANNEL_OPEN_CONFIRMATION: case SSH2_MSG_CHANNEL_OPEN_FAILURE: case SSH2_MSG_CHANNEL_CLOSE: case SSH2_MSG_CHANNEL_WINDOW_ADJUST: case SSH2_MSG_CHANNEL_DATA: case SSH2_MSG_CHANNEL_EXTENDED_DATA: case SSH2_MSG_CHANNEL_EOF: case SSH2_MSG_CHANNEL_REQUEST: case SSH2_MSG_CHANNEL_SUCCESS: case SSH2_MSG_CHANNEL_FAILURE: /* * All these messages have the recipient channel id as the * first uint32 field in the packet. Substitute the downstream * channel id for our one and pass the packet downstream. */ assert(pktlen >= 4); upstream_id = GET_32BIT(pkt); if ((chan = share_find_channel_by_upstream(cs, upstream_id)) != NULL) { /* * The normal case: this id refers to an open channel. */ PUT_32BIT(pkt, chan->downstream_id); send_packet_to_downstream(cs, type, pkt, pktlen, chan); /* * Update the channel state, for messages that need it. */ if (type == SSH2_MSG_CHANNEL_OPEN_CONFIRMATION) { if (chan->state == UNACKNOWLEDGED && pktlen >= 8) { share_channel_set_server_id(cs, chan, GET_32BIT(pkt+4), OPEN); if (!cs->sock) { /* Retry cleaning up this connection, so that we * can send an immediate CLOSE on this channel for * which we now know the server id. */ share_try_cleanup(cs); } } } else if (type == SSH2_MSG_CHANNEL_OPEN_FAILURE) { ssh_delete_sharing_channel(cs->parent->ssh, chan->upstream_id); share_remove_channel(cs, chan); } else if (type == SSH2_MSG_CHANNEL_CLOSE) { if (chan->state == SENT_CLOSE) { ssh_delete_sharing_channel(cs->parent->ssh, chan->upstream_id); share_remove_channel(cs, chan); if (!cs->sock) { /* Retry cleaning up this connection, in case this * channel closure was the last thing we were * waiting for. */ share_try_cleanup(cs); } } else { chan->state = RCVD_CLOSE; } } } else if ((xc = share_find_xchannel_by_upstream(cs, upstream_id)) != NULL) { /* * The unusual case: this id refers to an xchannel. Add it * to the xchannel's queue. */ struct share_xchannel_message *msg; msg = share_xchannel_add_message(xc, type, pktlen); memcpy(msg->data, pkt, pktlen); /* If the xchannel is dead, then also respond to it (which * may involve deleting the channel). */ if (!xc->live) share_dead_xchannel_respond(cs, xc); } break; default: assert(!"This packet type should never have come from ssh.c"); break; } } static void share_got_pkt_from_downstream(struct ssh_sharing_connstate *cs, int type, unsigned char *pkt, int pktlen) { char *request_name; struct share_forwarding *fwd; int id_pos; unsigned old_id, new_id, server_id; struct share_globreq *globreq; struct share_channel *chan; struct share_halfchannel *hc; struct share_xchannel *xc; char *err = NULL; switch (type) { case SSH2_MSG_DISCONNECT: /* * This message stops here: if downstream is disconnecting * from us, that doesn't mean we want to disconnect from the * SSH server. Close the downstream connection and start * cleanup. */ share_begin_cleanup(cs); break; case SSH2_MSG_GLOBAL_REQUEST: /* * The only global requests we understand are "tcpip-forward" * and "cancel-tcpip-forward". Since those require us to * maintain state, we must assume that other global requests * will probably require that too, and so we don't forward on * any request we don't understand. */ request_name = getstring(pkt, pktlen); if (request_name == NULL) { err = dupprintf("Truncated GLOBAL_REQUEST packet"); goto confused; } if (!strcmp(request_name, "tcpip-forward")) { int wantreplypos, orig_wantreply, port, ret; char *host; sfree(request_name); /* * Pick the packet apart to find the want_reply field and * the host/port we're going to ask to listen on. */ wantreplypos = getstring_size(pkt, pktlen); if (wantreplypos < 0 || wantreplypos >= pktlen) { err = dupprintf("Truncated GLOBAL_REQUEST packet"); goto confused; } orig_wantreply = pkt[wantreplypos]; port = getstring_size(pkt + (wantreplypos + 1), pktlen - (wantreplypos + 1)); port += (wantreplypos + 1); if (port < 0 || port > pktlen - 4) { err = dupprintf("Truncated GLOBAL_REQUEST packet"); goto confused; } host = getstring(pkt + (wantreplypos + 1), pktlen - (wantreplypos + 1)); assert(host != NULL); port = GET_32BIT(pkt + port); /* * See if we can allocate space in ssh.c's tree of remote * port forwardings. If we can't, it's because another * client sharing this connection has already allocated * the identical port forwarding, so we take it on * ourselves to manufacture a failure packet and send it * back to downstream. */ ret = ssh_alloc_sharing_rportfwd(cs->parent->ssh, host, port, cs); if (!ret) { if (orig_wantreply) { send_packet_to_downstream(cs, SSH2_MSG_REQUEST_FAILURE, "", 0, NULL); } } else { /* * We've managed to make space for this forwarding * locally. Pass the request on to the SSH server, but * set want_reply even if it wasn't originally set, so * that we know whether this forwarding needs to be * cleaned up if downstream goes away. */ int old_wantreply = pkt[wantreplypos]; pkt[wantreplypos] = 1; ssh_send_packet_from_downstream (cs->parent->ssh, cs->id, type, pkt, pktlen, old_wantreply ? NULL : "upstream added want_reply flag"); fwd = share_add_forwarding(cs, host, port); ssh_sharing_queue_global_request(cs->parent->ssh, cs); if (fwd) { globreq = snew(struct share_globreq); globreq->next = NULL; if (cs->globreq_tail) cs->globreq_tail->next = globreq; else cs->globreq_head = globreq; globreq->fwd = fwd; globreq->want_reply = orig_wantreply; globreq->type = GLOBREQ_TCPIP_FORWARD; } } sfree(host); } else if (!strcmp(request_name, "cancel-tcpip-forward")) { int wantreplypos, orig_wantreply, port; char *host; struct share_forwarding *fwd; sfree(request_name); /* * Pick the packet apart to find the want_reply field and * the host/port we're going to ask to listen on. */ wantreplypos = getstring_size(pkt, pktlen); if (wantreplypos < 0 || wantreplypos >= pktlen) { err = dupprintf("Truncated GLOBAL_REQUEST packet"); goto confused; } orig_wantreply = pkt[wantreplypos]; port = getstring_size(pkt + (wantreplypos + 1), pktlen - (wantreplypos + 1)); port += (wantreplypos + 1); if (port < 0 || port > pktlen - 4) { err = dupprintf("Truncated GLOBAL_REQUEST packet"); goto confused; } host = getstring(pkt + (wantreplypos + 1), pktlen - (wantreplypos + 1)); assert(host != NULL); port = GET_32BIT(pkt + port); /* * Look up the existing forwarding with these details. */ fwd = share_find_forwarding(cs, host, port); if (!fwd) { if (orig_wantreply) { send_packet_to_downstream(cs, SSH2_MSG_REQUEST_FAILURE, "", 0, NULL); } } else { /* * Pass the cancel request on to the SSH server, but * set want_reply even if it wasn't originally set, so * that _we_ know whether the forwarding has been * deleted even if downstream doesn't want to know. */ int old_wantreply = pkt[wantreplypos]; pkt[wantreplypos] = 1; ssh_send_packet_from_downstream (cs->parent->ssh, cs->id, type, pkt, pktlen, old_wantreply ? NULL : "upstream added want_reply flag"); ssh_sharing_queue_global_request(cs->parent->ssh, cs); } sfree(host); } else { /* * Request we don't understand. Manufacture a failure * message if an answer was required. */ int wantreplypos; sfree(request_name); wantreplypos = getstring_size(pkt, pktlen); if (wantreplypos < 0 || wantreplypos >= pktlen) { err = dupprintf("Truncated GLOBAL_REQUEST packet"); goto confused; } if (pkt[wantreplypos]) send_packet_to_downstream(cs, SSH2_MSG_REQUEST_FAILURE, "", 0, NULL); } break; case SSH2_MSG_CHANNEL_OPEN: /* Sender channel id comes after the channel type string */ id_pos = getstring_size(pkt, pktlen); if (id_pos < 0 || id_pos > pktlen - 12) { err = dupprintf("Truncated CHANNEL_OPEN packet"); goto confused; } old_id = GET_32BIT(pkt + id_pos); new_id = ssh_alloc_sharing_channel(cs->parent->ssh, cs); share_add_channel(cs, old_id, new_id, 0, UNACKNOWLEDGED, GET_32BIT(pkt + id_pos + 8)); PUT_32BIT(pkt + id_pos, new_id); ssh_send_packet_from_downstream(cs->parent->ssh, cs->id, type, pkt, pktlen, NULL); break; case SSH2_MSG_CHANNEL_OPEN_CONFIRMATION: if (pktlen < 16) { err = dupprintf("Truncated CHANNEL_OPEN_CONFIRMATION packet"); goto confused; } id_pos = 4; /* sender channel id is 2nd uint32 field in packet */ old_id = GET_32BIT(pkt + id_pos); server_id = GET_32BIT(pkt); /* This server id may refer to either a halfchannel or an xchannel. */ hc = NULL, xc = NULL; /* placate optimiser */ if ((hc = share_find_halfchannel(cs, server_id)) != NULL) { new_id = ssh_alloc_sharing_channel(cs->parent->ssh, cs); } else if ((xc = share_find_xchannel_by_server(cs, server_id)) != NULL) { new_id = xc->upstream_id; } else { err = dupprintf("CHANNEL_OPEN_CONFIRMATION packet cited unknown channel %u", (unsigned)server_id); goto confused; } PUT_32BIT(pkt + id_pos, new_id); chan = share_add_channel(cs, old_id, new_id, server_id, OPEN, GET_32BIT(pkt + 12)); if (hc) { ssh_send_packet_from_downstream(cs->parent->ssh, cs->id, type, pkt, pktlen, NULL); share_remove_halfchannel(cs, hc); } else if (xc) { unsigned downstream_window = GET_32BIT(pkt + 8); if (downstream_window < 256) { err = dupprintf("Initial window size for x11 channel must be at least 256 (got %u)", downstream_window); goto confused; } share_xchannel_confirmation(cs, xc, chan, downstream_window); share_remove_xchannel(cs, xc); } break; case SSH2_MSG_CHANNEL_OPEN_FAILURE: if (pktlen < 4) { err = dupprintf("Truncated CHANNEL_OPEN_FAILURE packet"); goto confused; } server_id = GET_32BIT(pkt); /* This server id may refer to either a halfchannel or an xchannel. */ if ((hc = share_find_halfchannel(cs, server_id)) != NULL) { ssh_send_packet_from_downstream(cs->parent->ssh, cs->id, type, pkt, pktlen, NULL); share_remove_halfchannel(cs, hc); } else if ((xc = share_find_xchannel_by_server(cs, server_id)) != NULL) { share_xchannel_failure(cs, xc); } else { err = dupprintf("CHANNEL_OPEN_FAILURE packet cited unknown channel %u", (unsigned)server_id); goto confused; } break; case SSH2_MSG_CHANNEL_WINDOW_ADJUST: case SSH2_MSG_CHANNEL_DATA: case SSH2_MSG_CHANNEL_EXTENDED_DATA: case SSH2_MSG_CHANNEL_EOF: case SSH2_MSG_CHANNEL_CLOSE: case SSH2_MSG_CHANNEL_REQUEST: case SSH2_MSG_CHANNEL_SUCCESS: case SSH2_MSG_CHANNEL_FAILURE: case SSH2_MSG_IGNORE: case SSH2_MSG_DEBUG: if (type == SSH2_MSG_CHANNEL_REQUEST && (request_name = getstring(pkt + 4, pktlen - 4)) != NULL) { /* * Agent forwarding requests from downstream are treated * specially. Because OpenSSHD doesn't let us enable agent * forwarding independently per session channel, and in * particular because the OpenSSH-defined agent forwarding * protocol does not mark agent-channel requests with the * id of the session channel they originate from, the only * way we can implement agent forwarding in a * connection-shared PuTTY is to forward the _upstream_ * agent. Hence, we unilaterally deny agent forwarding * requests from downstreams if we aren't prepared to * forward an agent ourselves. * * (If we are, then we dutifully pass agent forwarding * requests upstream. OpenSSHD has the curious behaviour * that all but the first such request will be rejected, * but all session channels opened after the first request * get agent forwarding enabled whether they ask for it or * not; but that's not our concern, since other SSH * servers supporting the same piece of protocol might in * principle at least manage to enable agent forwarding on * precisely the channels that requested it, even if the * subsequent CHANNEL_OPENs still can't be associated with * a parent session channel.) */ if (!strcmp(request_name, "auth-agent-req@openssh.com") && !ssh_agent_forwarding_permitted(cs->parent->ssh)) { unsigned server_id = GET_32BIT(pkt); unsigned char recipient_id[4]; chan = share_find_channel_by_server(cs, server_id); if (chan) { PUT_32BIT(recipient_id, chan->downstream_id); send_packet_to_downstream(cs, SSH2_MSG_CHANNEL_FAILURE, recipient_id, 4, NULL); } else { char *buf = dupprintf("Agent forwarding request for " "unrecognised channel %u", server_id); share_disconnect(cs, buf); sfree(buf); return; } break; } /* * Another thing we treat specially is X11 forwarding * requests. For these, we have to make up another set of * X11 auth data, and enter it into our SSH connection's * list of possible X11 authorisation credentials so that * when we see an X11 channel open request we can know * whether it's one to handle locally or one to pass on to * a downstream, and if the latter, which one. */ if (!strcmp(request_name, "x11-req")) { unsigned server_id = GET_32BIT(pkt); int want_reply, single_connection, screen; char *auth_proto_str, *auth_data; int auth_proto, protolen, datalen; int pos; chan = share_find_channel_by_server(cs, server_id); if (!chan) { char *buf = dupprintf("X11 forwarding request for " "unrecognised channel %u", server_id); share_disconnect(cs, buf); sfree(buf); return; } /* * Pick apart the whole message to find the downstream * auth details. */ /* we have already seen: 4 bytes channel id, 4+7 request name */ if (pktlen < 17) { err = dupprintf("Truncated CHANNEL_REQUEST(\"x11\") packet"); goto confused; } want_reply = pkt[15] != 0; single_connection = pkt[16] != 0; auth_proto_str = getstring(pkt+17, pktlen-17); pos = 17 + getstring_size(pkt+17, pktlen-17); auth_data = getstring(pkt+pos, pktlen-pos); pos += getstring_size(pkt+pos, pktlen-pos); if (pktlen < pos+4) { err = dupprintf("Truncated CHANNEL_REQUEST(\"x11\") packet"); goto confused; } screen = GET_32BIT(pkt+pos); auth_proto = x11_identify_auth_proto(auth_proto_str); if (auth_proto < 0) { /* Reject due to not understanding downstream's * requested authorisation method. */ unsigned char recipient_id[4]; PUT_32BIT(recipient_id, chan->downstream_id); send_packet_to_downstream(cs, SSH2_MSG_CHANNEL_FAILURE, recipient_id, 4, NULL); } chan->x11_auth_proto = auth_proto; chan->x11_auth_data = x11_dehexify(auth_data, &chan->x11_auth_datalen); chan->x11_auth_upstream = ssh_sharing_add_x11_display(cs->parent->ssh, auth_proto, cs, chan); chan->x11_one_shot = single_connection; /* * Now construct a replacement X forwarding request, * containing our own auth data, and send that to the * server. */ protolen = strlen(chan->x11_auth_upstream->protoname); datalen = strlen(chan->x11_auth_upstream->datastring); pktlen = 29+protolen+datalen; pkt = snewn(pktlen, unsigned char); PUT_32BIT(pkt, server_id); PUT_32BIT(pkt+4, 7); /* strlen("x11-req") */ memcpy(pkt+8, "x11-req", 7); pkt[15] = want_reply; pkt[16] = single_connection; PUT_32BIT(pkt+17, protolen); memcpy(pkt+21, chan->x11_auth_upstream->protoname, protolen); PUT_32BIT(pkt+21+protolen, datalen); memcpy(pkt+25+protolen, chan->x11_auth_upstream->datastring, datalen); PUT_32BIT(pkt+25+protolen+datalen, screen); ssh_send_packet_from_downstream(cs->parent->ssh, cs->id, SSH2_MSG_CHANNEL_REQUEST, pkt, pktlen, NULL); sfree(pkt); break; } } ssh_send_packet_from_downstream(cs->parent->ssh, cs->id, type, pkt, pktlen, NULL); if (type == SSH2_MSG_CHANNEL_CLOSE && pktlen >= 4) { server_id = GET_32BIT(pkt); chan = share_find_channel_by_server(cs, server_id); if (chan) { if (chan->state == RCVD_CLOSE) { ssh_delete_sharing_channel(cs->parent->ssh, chan->upstream_id); share_remove_channel(cs, chan); } else { chan->state = SENT_CLOSE; } } } break; default: err = dupprintf("Unexpected packet type %d\n", type); goto confused; /* * Any other packet type is unexpected. In particular, we * never pass GLOBAL_REQUESTs downstream, so we never expect * to see SSH2_MSG_REQUEST_{SUCCESS,FAILURE}. */ confused: assert(err != NULL); share_disconnect(cs, err); sfree(err); break; } } /* * Coroutine macros similar to, but simplified from, those in ssh.c. */ #define crBegin(v) { int *crLine = &v; switch(v) { case 0:; #define crFinish(z) } *crLine = 0; return (z); } #define crGetChar(c) do \ { \ while (len == 0) { \ *crLine =__LINE__; return 1; case __LINE__:; \ } \ len--; \ (c) = (unsigned char)*data++; \ } while (0) static int share_receive(Plug plug, int urgent, char *data, int len) { struct ssh_sharing_connstate *cs = (struct ssh_sharing_connstate *)plug; static const char expected_verstring_prefix[] = "SSHCONNECTION@putty.projects.tartarus.org-2.0-"; unsigned char c; crBegin(cs->crLine); /* * First read the version string from downstream. */ cs->recvlen = 0; while (1) { crGetChar(c); if (c == '\012') break; if (cs->recvlen > sizeof(cs->recvbuf)) { char *buf = dupprintf("Version string far too long\n"); share_disconnect(cs, buf); sfree(buf); goto dead; } cs->recvbuf[cs->recvlen++] = c; } /* * Now parse the version string to make sure it's at least vaguely * sensible, and log it. */ if (cs->recvlen < sizeof(expected_verstring_prefix)-1 || memcmp(cs->recvbuf, expected_verstring_prefix, sizeof(expected_verstring_prefix) - 1)) { char *buf = dupprintf("Version string did not have expected prefix\n"); share_disconnect(cs, buf); sfree(buf); goto dead; } if (cs->recvlen > 0 && cs->recvbuf[cs->recvlen-1] == '\015') cs->recvlen--; /* trim off \r before \n */ ssh_sharing_logf(cs->parent->ssh, cs->id, "Downstream version string: %.*s", cs->recvlen, cs->recvbuf); /* * Loop round reading packets. */ while (1) { cs->recvlen = 0; while (cs->recvlen < 4) { crGetChar(c); cs->recvbuf[cs->recvlen++] = c; } cs->curr_packetlen = toint(GET_32BIT(cs->recvbuf) + 4); if (cs->curr_packetlen < 5 || cs->curr_packetlen > sizeof(cs->recvbuf)) { char *buf = dupprintf("Bad packet length %u\n", (unsigned)cs->curr_packetlen); share_disconnect(cs, buf); sfree(buf); goto dead; } while (cs->recvlen < cs->curr_packetlen) { crGetChar(c); cs->recvbuf[cs->recvlen++] = c; } share_got_pkt_from_downstream(cs, cs->recvbuf[4], cs->recvbuf + 5, cs->recvlen - 5); } dead:; crFinish(1); } static void share_sent(Plug plug, int bufsize) { /* struct ssh_sharing_connstate *cs = (struct ssh_sharing_connstate *)plug; */ /* * We do nothing here, because we expect that there won't be a * need to throttle and unthrottle the connection to a downstream. * It should automatically throttle itself: if the SSH server * sends huge amounts of data on all channels then it'll run out * of window until our downstream sends it back some * WINDOW_ADJUSTs. */ } static int share_listen_closing(Plug plug, const char *error_msg, int error_code, int calling_back) { struct ssh_sharing_state *sharestate = (struct ssh_sharing_state *)plug; if (error_msg) ssh_sharing_logf(sharestate->ssh, 0, "listening socket: %s", error_msg); sk_close(sharestate->listensock); return 1; } static void share_send_verstring(struct ssh_sharing_connstate *cs) { char *fullstring = dupcat("SSHCONNECTION@putty.projects.tartarus.org-2.0-", cs->parent->server_verstring, "\015\012", NULL); sk_write(cs->sock, fullstring, strlen(fullstring)); sfree(fullstring); cs->sent_verstring = TRUE; } int share_ndownstreams(void *state) { struct ssh_sharing_state *sharestate = (struct ssh_sharing_state *)state; return count234(sharestate->connections); } void share_activate(void *state, const char *server_verstring) { /* * Indication from ssh.c that we are now ready to begin serving * any downstreams that have already connected to us. */ struct ssh_sharing_state *sharestate = (struct ssh_sharing_state *)state; struct ssh_sharing_connstate *cs; int i; /* * Trim the server's version string down to just the software * version component, removing "SSH-2.0-" or whatever at the * front. */ for (i = 0; i < 2; i++) { server_verstring += strcspn(server_verstring, "-"); if (*server_verstring) server_verstring++; } sharestate->server_verstring = dupstr(server_verstring); for (i = 0; (cs = (struct ssh_sharing_connstate *) index234(sharestate->connections, i)) != NULL; i++) { assert(!cs->sent_verstring); share_send_verstring(cs); } } static int share_listen_accepting(Plug plug, accept_fn_t constructor, accept_ctx_t ctx) { static const struct plug_function_table connection_fn_table = { NULL, /* no log function, because that's for outgoing connections */ share_closing, share_receive, share_sent, NULL /* no accepting function, because we've already done it */ }; struct ssh_sharing_state *sharestate = (struct ssh_sharing_state *)plug; struct ssh_sharing_connstate *cs; const char *err; /* * A new downstream has connected to us. */ cs = snew(struct ssh_sharing_connstate); cs->fn = &connection_fn_table; cs->parent = sharestate; if ((cs->id = share_find_unused_id(sharestate, sharestate->nextid)) == 0 && (cs->id = share_find_unused_id(sharestate, 1)) == 0) { sfree(cs); return 1; } sharestate->nextid = cs->id + 1; if (sharestate->nextid == 0) sharestate->nextid++; /* only happens in VERY long-running upstreams */ cs->sock = constructor(ctx, (Plug) cs); if ((err = sk_socket_error(cs->sock)) != NULL) { sfree(cs); return err != NULL; } sk_set_frozen(cs->sock, 0); add234(cs->parent->connections, cs); cs->sent_verstring = FALSE; if (sharestate->server_verstring) share_send_verstring(cs); cs->got_verstring = FALSE; cs->recvlen = 0; cs->crLine = 0; cs->halfchannels = newtree234(share_halfchannel_cmp); cs->channels_by_us = newtree234(share_channel_us_cmp); cs->channels_by_server = newtree234(share_channel_server_cmp); cs->xchannels_by_us = newtree234(share_xchannel_us_cmp); cs->xchannels_by_server = newtree234(share_xchannel_server_cmp); cs->forwardings = newtree234(share_forwarding_cmp); cs->globreq_head = cs->globreq_tail = NULL; ssh_sharing_downstream_connected(sharestate->ssh, cs->id); return 0; } /* Per-application overrides for what roles we can take (e.g. pscp * will never be an upstream) */ extern const int share_can_be_downstream; extern const int share_can_be_upstream; /* * Init function for connection sharing. We either open a listening * socket and become an upstream, or connect to an existing one and * become a downstream, or do neither. We are responsible for deciding * which of these to do (including checking the Conf to see if * connection sharing is even enabled in the first place). If we * become a downstream, we return the Socket with which we connected * to the upstream; otherwise (whether or not we have established an * upstream) we return NULL. */ Socket ssh_connection_sharing_init(const char *host, int port, Conf *conf, Ssh ssh, void **state) { static const struct plug_function_table listen_fn_table = { NULL, /* no log function, because that's for outgoing connections */ share_listen_closing, NULL, /* no receive function on a listening socket */ NULL, /* no sent function on a listening socket */ share_listen_accepting }; int result, can_upstream, can_downstream; char *logtext, *ds_err, *us_err; char *sockname; Socket sock; struct ssh_sharing_state *sharestate; if (!conf_get_int(conf, CONF_ssh_connection_sharing)) return NULL; /* do not share anything */ can_upstream = share_can_be_upstream && conf_get_int(conf, CONF_ssh_connection_sharing_upstream); can_downstream = share_can_be_downstream && conf_get_int(conf, CONF_ssh_connection_sharing_downstream); if (!can_upstream && !can_downstream) return NULL; /* * Decide on the string used to identify the connection point * between upstream and downstream (be it a Windows named pipe or * a Unix-domain socket or whatever else). * * I wondered about making this a SHA hash of all sorts of pieces * of the PuTTY configuration - essentially everything PuTTY uses * to know where and how to make a connection, including all the * proxy details (or rather, all the _relevant_ ones - only * including settings that other settings didn't prevent from * having any effect), plus the username. However, I think it's * better to keep it really simple: the connection point * identifier is derived from the hostname and port used to index * the host-key cache (not necessarily where we _physically_ * connected to, in cases involving proxies or CONF_loghost), plus * the username if one is specified. */ { char *username = get_remote_username(conf); if (port == 22) { if (username) sockname = dupprintf("%s@%s", username, host); else sockname = dupprintf("%s", host); } else { if (username) sockname = dupprintf("%s@%s:%d", username, host, port); else sockname = dupprintf("%s:%d", host, port); } sfree(username); /* * The platform-specific code may transform this further in * order to conform to local namespace conventions (e.g. not * using slashes in filenames), but that's its job and not * ours. */ } /* * Create a data structure for the listening plug if we turn out * to be an upstream. */ sharestate = snew(struct ssh_sharing_state); sharestate->fn = &listen_fn_table; sharestate->listensock = NULL; /* * Now hand off to a per-platform routine that either connects to * an existing upstream (using 'ssh' as the plug), establishes our * own upstream (using 'sharestate' as the plug), or forks off a * separate upstream and then connects to that. It will return a * code telling us which kind of socket it put in 'sock'. */ sock = NULL; logtext = ds_err = us_err = NULL; result = platform_ssh_share(sockname, conf, (Plug)ssh, (Plug)sharestate, &sock, &logtext, &ds_err, &us_err, can_upstream, can_downstream); ssh_connshare_log(ssh, result, logtext, ds_err, us_err); sfree(logtext); sfree(ds_err); sfree(us_err); switch (result) { case SHARE_NONE: /* * We aren't sharing our connection at all (e.g. something * went wrong setting the socket up). Free the upstream * structure and return NULL. */ assert(sock == NULL); *state = NULL; sfree(sharestate); sfree(sockname); return NULL; case SHARE_DOWNSTREAM: /* * We are downstream, so free sharestate which it turns out we * don't need after all, and return the downstream socket as a * replacement for an ordinary SSH connection. */ *state = NULL; sfree(sharestate); sfree(sockname); return sock; case SHARE_UPSTREAM: /* * We are upstream. Set up sharestate properly and pass a copy * to the caller; return NULL, to tell ssh.c that it has to * make an ordinary connection after all. */ *state = sharestate; sharestate->listensock = sock; sharestate->connections = newtree234(share_connstate_cmp); sharestate->ssh = ssh; sharestate->server_verstring = NULL; sharestate->sockname = dupstr(sockname); sharestate->nextid = 1; return NULL; } return NULL; } |
Changes to sshzlib.c.
︙ | ︙ | |||
34 35 36 37 38 39 40 41 42 43 44 45 46 47 | * difference of opinion mentioned above has arisen _precisely_ * because there has been only one zlib implementation and * everybody has used it. I don't intend that this should happen * again. */ #include <stdlib.h> #include <assert.h> #ifdef ZLIB_STANDALONE /* * This module also makes a handy zlib decoding tool for when * you're picking apart Zip files or PDFs or PNGs. If you compile | > | 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 | * difference of opinion mentioned above has arisen _precisely_ * because there has been only one zlib implementation and * everybody has used it. I don't intend that this should happen * again. */ #include <stdlib.h> #include <string.h> #include <assert.h> #ifdef ZLIB_STANDALONE /* * This module also makes a handy zlib decoding tool for when * you're picking apart Zip files or PDFs or PNGs. If you compile |
︙ | ︙ |
Changes to storage.h.
1 2 3 4 5 6 7 8 9 10 11 | /* * storage.h: interface defining functions for storage and recovery * of PuTTY's persistent data. */ #ifndef PUTTY_STORAGE_H #define PUTTY_STORAGE_H /* ---------------------------------------------------------------------- * Functions to save and restore PuTTY sessions. Note that this is * only the low-level code to do the reading and writing. The | | | | | | | > > | < < < | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 | /* * storage.h: interface defining functions for storage and recovery * of PuTTY's persistent data. */ #ifndef PUTTY_STORAGE_H #define PUTTY_STORAGE_H /* ---------------------------------------------------------------------- * Functions to save and restore PuTTY sessions. Note that this is * only the low-level code to do the reading and writing. The * higher-level code that translates an internal Conf structure into * a set of (key,value) pairs in their external storage format is * elsewhere, since it doesn't (mostly) change between platforms. */ /* * Write a saved session. The caller is expected to call * open_setting_w() to get a `void *' handle, then pass that to a * number of calls to write_setting_s() and write_setting_i(), and * then close it using close_settings_w(). At the end of this call * sequence the settings should have been written to the PuTTY * persistent storage area. * * A given key will be written at most once while saving a session. * Keys may be up to 255 characters long. String values have no length * limit. * * Any returned error message must be freed after use. */ void *open_settings_w(const char *sessionname, char **errmsg); void write_setting_s(void *handle, const char *key, const char *value); void write_setting_i(void *handle, const char *key, int value); void write_setting_filename(void *handle, const char *key, Filename *value); void write_setting_fontspec(void *handle, const char *key, FontSpec *font); void close_settings_w(void *handle); /* * Read a saved session. The caller is expected to call * open_setting_r() to get a `void *' handle, then pass that to a * number of calls to read_setting_s() and read_setting_i(), and * then close it using close_settings_r(). * * read_setting_s() returns a dynamically allocated string which the * caller must free. read_setting_filename() and * read_setting_fontspec() likewise return dynamically allocated * structures. * * If a particular string setting is not present in the session, * read_setting_s() can return NULL, in which case the caller * should invent a sensible default. If an integer setting is not * present, read_setting_i() returns its provided default. */ void *open_settings_r(const char *sessionname); char *read_setting_s(void *handle, const char *key); int read_setting_i(void *handle, const char *key, int defvalue); Filename *read_setting_filename(void *handle, const char *key); FontSpec *read_setting_fontspec(void *handle, const char *key); void close_settings_r(void *handle); /* * Delete a whole saved session. */ void del_settings(const char *sessionname); |
︙ | ︙ |
Changes to telnet.c.
1 2 3 4 5 6 7 8 9 10 11 12 13 | /* * Telnet backend. */ #include <stdio.h> #include <stdlib.h> #include "putty.h" #ifndef FALSE #define FALSE 0 #endif #ifndef TRUE | > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 | /* * Telnet backend. */ #include <stdio.h> #include <stdlib.h> #include <limits.h> #include "putty.h" #ifndef FALSE #define FALSE 0 #endif #ifndef TRUE |
︙ | ︙ | |||
177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 | }; typedef struct telnet_tag { const struct plug_function_table *fn; /* the above field _must_ be first in the structure */ Socket s; void *frontend; void *ldisc; int term_width, term_height; int opt_states[NUM_OPTS]; int echoing, editing; int activated; int bufsize; int in_synch; int sb_opt, sb_len; unsigned char *sb_buf; int sb_size; enum { TOP_LEVEL, SEENIAC, SEENWILL, SEENWONT, SEENDO, SEENDONT, SEENSB, SUBNEGOT, SUBNEG_IAC, SEENCR } state; | > | | 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 | }; typedef struct telnet_tag { const struct plug_function_table *fn; /* the above field _must_ be first in the structure */ Socket s; int closed_on_socket_error; void *frontend; void *ldisc; int term_width, term_height; int opt_states[NUM_OPTS]; int echoing, editing; int activated; int bufsize; int in_synch; int sb_opt, sb_len; unsigned char *sb_buf; int sb_size; enum { TOP_LEVEL, SEENIAC, SEENWILL, SEENWONT, SEENDO, SEENDONT, SEENSB, SUBNEGOT, SUBNEG_IAC, SEENCR } state; Conf *conf; Pinger pinger; } *Telnet; #define TELNET_MAX_BACKLOG 4096 #define SB_DELTA 1024 |
︙ | ︙ | |||
359 360 361 362 363 364 365 | */ if (cmd == WILL || cmd == DO) send_opt(telnet, (cmd == WILL ? DONT : WONT), option); } static void process_subneg(Telnet telnet) { | | | | > > | | | > > > | | < | | > | | 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 | */ if (cmd == WILL || cmd == DO) send_opt(telnet, (cmd == WILL ? DONT : WONT), option); } static void process_subneg(Telnet telnet) { unsigned char *b, *p, *q; int var, value, n, bsize; char *e, *eval, *ekey, *user; switch (telnet->sb_opt) { case TELOPT_TSPEED: if (telnet->sb_len == 1 && telnet->sb_buf[0] == TELQUAL_SEND) { char *logbuf; char *termspeed = conf_get_str(telnet->conf, CONF_termspeed); b = snewn(20 + strlen(termspeed), unsigned char); b[0] = IAC; b[1] = SB; b[2] = TELOPT_TSPEED; b[3] = TELQUAL_IS; strcpy((char *)(b + 4), termspeed); n = 4 + strlen(termspeed); b[n] = IAC; b[n + 1] = SE; telnet->bufsize = sk_write(telnet->s, (char *)b, n + 2); logevent(telnet->frontend, "server:\tSB TSPEED SEND"); logbuf = dupprintf("client:\tSB TSPEED IS %s", termspeed); logevent(telnet->frontend, logbuf); sfree(logbuf); sfree(b); } else logevent(telnet->frontend, "server:\tSB TSPEED <something weird>"); break; case TELOPT_TTYPE: if (telnet->sb_len == 1 && telnet->sb_buf[0] == TELQUAL_SEND) { char *logbuf; char *termtype = conf_get_str(telnet->conf, CONF_termtype); b = snewn(20 + strlen(termtype), unsigned char); b[0] = IAC; b[1] = SB; b[2] = TELOPT_TTYPE; b[3] = TELQUAL_IS; for (n = 0; termtype[n]; n++) b[n + 4] = (termtype[n] >= 'a' && termtype[n] <= 'z' ? termtype[n] + 'A' - 'a' : termtype[n]); b[n + 4] = IAC; b[n + 5] = SE; telnet->bufsize = sk_write(telnet->s, (char *)b, n + 6); b[n + 4] = 0; logevent(telnet->frontend, "server:\tSB TTYPE SEND"); logbuf = dupprintf("client:\tSB TTYPE IS %s", b + 4); logevent(telnet->frontend, logbuf); sfree(logbuf); sfree(b); } else logevent(telnet->frontend, "server:\tSB TTYPE <something weird>\r\n"); break; case TELOPT_OLD_ENVIRON: case TELOPT_NEW_ENVIRON: p = telnet->sb_buf; q = p + telnet->sb_len; if (p < q && *p == TELQUAL_SEND) { char *logbuf; p++; logbuf = dupprintf("server:\tSB %s SEND", telopt(telnet->sb_opt)); logevent(telnet->frontend, logbuf); sfree(logbuf); if (telnet->sb_opt == TELOPT_OLD_ENVIRON) { if (conf_get_int(telnet->conf, CONF_rfc_environ)) { value = RFC_VALUE; var = RFC_VAR; } else { value = BSD_VALUE; var = BSD_VAR; } /* |
︙ | ︙ | |||
445 446 447 448 449 450 451 452 453 454 455 456 | /* * With NEW_ENVIRON, the sense of VAR and VALUE * isn't in doubt. */ value = RFC_VALUE; var = RFC_VAR; } b[0] = IAC; b[1] = SB; b[2] = telnet->sb_opt; b[3] = TELQUAL_IS; n = 4; | > > > > > > > > > > > > > > > | | < > | < < | | < < < < | | | | | | | | < | | | | | > | | < > > | < < > | > > > > > > > > | > > > > > > > > | 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 | /* * With NEW_ENVIRON, the sense of VAR and VALUE * isn't in doubt. */ value = RFC_VALUE; var = RFC_VAR; } bsize = 20; for (eval = conf_get_str_strs(telnet->conf, CONF_environmt, NULL, &ekey); eval != NULL; eval = conf_get_str_strs(telnet->conf, CONF_environmt, ekey, &ekey)) bsize += strlen(ekey) + strlen(eval) + 2; user = get_remote_username(telnet->conf); if (user) bsize += 6 + strlen(user); b = snewn(bsize, unsigned char); b[0] = IAC; b[1] = SB; b[2] = telnet->sb_opt; b[3] = TELQUAL_IS; n = 4; for (eval = conf_get_str_strs(telnet->conf, CONF_environmt, NULL, &ekey); eval != NULL; eval = conf_get_str_strs(telnet->conf, CONF_environmt, ekey, &ekey)) { b[n++] = var; for (e = ekey; *e; e++) b[n++] = *e; b[n++] = value; for (e = eval; *e; e++) b[n++] = *e; } if (user) { b[n++] = var; b[n++] = 'U'; b[n++] = 'S'; b[n++] = 'E'; b[n++] = 'R'; b[n++] = value; for (e = user; *e; e++) b[n++] = *e; } b[n++] = IAC; b[n++] = SE; telnet->bufsize = sk_write(telnet->s, (char *)b, n); if (n == 6) { logbuf = dupprintf("client:\tSB %s IS <nothing>", telopt(telnet->sb_opt)); logevent(telnet->frontend, logbuf); sfree(logbuf); } else { logbuf = dupprintf("client:\tSB %s IS:", telopt(telnet->sb_opt)); logevent(telnet->frontend, logbuf); sfree(logbuf); for (eval = conf_get_str_strs(telnet->conf, CONF_environmt, NULL, &ekey); eval != NULL; eval = conf_get_str_strs(telnet->conf, CONF_environmt, ekey, &ekey)) { logbuf = dupprintf("\t%s=%s", ekey, eval); logevent(telnet->frontend, logbuf); sfree(logbuf); } if (user) { logbuf = dupprintf("\tUSER=%s", user); logevent(telnet->frontend, logbuf); sfree(logbuf); } } sfree(b); sfree(user); } break; } } static void do_telnet_read(Telnet telnet, char *buf, int len) { |
︙ | ︙ | |||
626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 | if (type == 0) msg = dupprintf("Connecting to %s port %d", addrbuf, port); else msg = dupprintf("Failed to connect to %s: %s", addrbuf, error_msg); logevent(telnet->frontend, msg); } static int telnet_closing(Plug plug, const char *error_msg, int error_code, int calling_back) { Telnet telnet = (Telnet) plug; if (telnet->s) { sk_close(telnet->s); telnet->s = NULL; notify_remote_exit(telnet->frontend); } if (error_msg) { logevent(telnet->frontend, error_msg); connection_fatal(telnet->frontend, "%s", error_msg); } /* Otherwise, the remote side closed the connection normally. */ | > > > > > > > > > | 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 | if (type == 0) msg = dupprintf("Connecting to %s port %d", addrbuf, port); else msg = dupprintf("Failed to connect to %s: %s", addrbuf, error_msg); logevent(telnet->frontend, msg); sfree(msg); } static int telnet_closing(Plug plug, const char *error_msg, int error_code, int calling_back) { Telnet telnet = (Telnet) plug; /* * We don't implement independent EOF in each direction for Telnet * connections; as soon as we get word that the remote side has * sent us EOF, we wind up the whole connection. */ if (telnet->s) { sk_close(telnet->s); telnet->s = NULL; if (error_msg) telnet->closed_on_socket_error = TRUE; notify_remote_exit(telnet->frontend); } if (error_msg) { logevent(telnet->frontend, error_msg); connection_fatal(telnet->frontend, "%s", error_msg); } /* Otherwise, the remote side closed the connection normally. */ |
︙ | ︙ | |||
670 671 672 673 674 675 676 | * * Returns an error message, or NULL on success. * * Also places the canonical host name into `realhost'. It must be * freed by the caller. */ static const char *telnet_init(void *frontend_handle, void **backend_handle, | < | | > > | > | | > | | | | | | | 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 | * * Returns an error message, or NULL on success. * * Also places the canonical host name into `realhost'. It must be * freed by the caller. */ static const char *telnet_init(void *frontend_handle, void **backend_handle, Conf *conf, char *host, int port, char **realhost, int nodelay, int keepalive) { static const struct plug_function_table fn_table = { telnet_log, telnet_closing, telnet_receive, telnet_sent }; SockAddr addr; const char *err; Telnet telnet; char *loghost; int addressfamily; telnet = snew(struct telnet_tag); telnet->fn = &fn_table; telnet->conf = conf_copy(conf); telnet->s = NULL; telnet->closed_on_socket_error = FALSE; telnet->echoing = TRUE; telnet->editing = TRUE; telnet->activated = FALSE; telnet->sb_buf = NULL; telnet->sb_size = 0; telnet->frontend = frontend_handle; telnet->term_width = conf_get_int(telnet->conf, CONF_width); telnet->term_height = conf_get_int(telnet->conf, CONF_height); telnet->state = TOP_LEVEL; telnet->ldisc = NULL; telnet->pinger = NULL; *backend_handle = telnet; /* * Try to find host. */ { char *buf; addressfamily = conf_get_int(telnet->conf, CONF_addressfamily); buf = dupprintf("Looking up host \"%s\"%s", host, (addressfamily == ADDRTYPE_IPV4 ? " (IPv4)" : (addressfamily == ADDRTYPE_IPV6 ? " (IPv6)" : ""))); logevent(telnet->frontend, buf); sfree(buf); } addr = name_lookup(host, port, realhost, telnet->conf, addressfamily); if ((err = sk_addr_error(addr)) != NULL) { sk_addr_free(addr); return err; } if (port < 0) port = 23; /* default telnet port */ /* * Open socket. */ telnet->s = new_connection(addr, *realhost, port, 0, 1, nodelay, keepalive, (Plug) telnet, telnet->conf); if ((err = sk_socket_error(telnet->s)) != NULL) return err; telnet->pinger = pinger_new(telnet->conf, &telnet_backend, telnet); /* * Initialise option states. */ if (conf_get_int(telnet->conf, CONF_passive_telnet)) { const struct Opt *const *o; for (o = opts; *o; o++) telnet->opt_states[(*o)->index] = INACTIVE; } else { const struct Opt *const *o; |
︙ | ︙ | |||
764 765 766 767 768 769 770 | * We can send special commands from the start. */ update_specials_menu(telnet->frontend); /* * loghost overrides realhost, if specified. */ | > | | | 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 | * We can send special commands from the start. */ update_specials_menu(telnet->frontend); /* * loghost overrides realhost, if specified. */ loghost = conf_get_str(telnet->conf, CONF_loghost); if (*loghost) { char *colon; sfree(*realhost); *realhost = dupstr(loghost); colon = strrchr(*realhost, ':'); if (colon) { /* * FIXME: if we ever update this aspect of ssh.c for * IPv6 literal management, this should change in line * with it. */ |
︙ | ︙ | |||
792 793 794 795 796 797 798 799 800 801 802 803 804 805 | Telnet telnet = (Telnet) handle; sfree(telnet->sb_buf); if (telnet->s) sk_close(telnet->s); if (telnet->pinger) pinger_free(telnet->pinger); sfree(telnet); } /* * Reconfigure the Telnet backend. There's no immediate action * necessary, in this backend: we just save the fresh config for * any subsequent negotiations. */ | > | | | > | 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 | Telnet telnet = (Telnet) handle; sfree(telnet->sb_buf); if (telnet->s) sk_close(telnet->s); if (telnet->pinger) pinger_free(telnet->pinger); conf_free(telnet->conf); sfree(telnet); } /* * Reconfigure the Telnet backend. There's no immediate action * necessary, in this backend: we just save the fresh config for * any subsequent negotiations. */ static void telnet_reconfig(void *handle, Conf *conf) { Telnet telnet = (Telnet) handle; pinger_reconfig(telnet->pinger, telnet->conf, conf); conf_free(telnet->conf); telnet->conf = conf_copy(conf); } /* * Called to send data down the Telnet connection. */ static int telnet_send(void *handle, char *buf, int len) { |
︙ | ︙ | |||
1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 | } static int telnet_exitcode(void *handle) { Telnet telnet = (Telnet) handle; if (telnet->s != NULL) return -1; /* still connected */ else /* Telnet doesn't transmit exit codes back to the client */ return 0; } /* * cfg_info for Telnet does nothing at all. | > > | 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 | } static int telnet_exitcode(void *handle) { Telnet telnet = (Telnet) handle; if (telnet->s != NULL) return -1; /* still connected */ else if (telnet->closed_on_socket_error) return INT_MAX; /* a socket error counts as an unclean exit */ else /* Telnet doesn't transmit exit codes back to the client */ return 0; } /* * cfg_info for Telnet does nothing at all. |
︙ | ︙ |
Changes to terminal.c.
︙ | ︙ | |||
985 986 987 988 989 990 991 | /* * Get the number of lines in the scrollback. */ static int sblines(Terminal *term) { int sblines = count234(term->scrollback); | | | 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 | /* * Get the number of lines in the scrollback. */ static int sblines(Terminal *term) { int sblines = count234(term->scrollback); if (term->erase_to_scrollback && term->alt_which && term->alt_screen) { sblines += term->alt_sblines; } return sblines; } /* |
︙ | ︙ | |||
1011 1012 1013 1014 1015 1016 1017 | whichtree = term->screen; treeindex = y; } else { int altlines = 0; assert(!screen); | | | 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 | whichtree = term->screen; treeindex = y; } else { int altlines = 0; assert(!screen); if (term->erase_to_scrollback && term->alt_which && term->alt_screen) { altlines = term->alt_sblines; } if (y < -altlines) { whichtree = term->scrollback; treeindex = y + altlines + count234(term->scrollback); } else { |
︙ | ︙ | |||
1061 1062 1063 1064 1065 1066 1067 | #define lineptr(x) (lineptr)(term,x,__LINE__,FALSE) #define scrlineptr(x) (lineptr)(term,x,__LINE__,TRUE) static void term_schedule_tblink(Terminal *term); static void term_schedule_cblink(Terminal *term); | | | | | | | 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 | #define lineptr(x) (lineptr)(term,x,__LINE__,FALSE) #define scrlineptr(x) (lineptr)(term,x,__LINE__,TRUE) static void term_schedule_tblink(Terminal *term); static void term_schedule_cblink(Terminal *term); static void term_timer(void *ctx, unsigned long now) { Terminal *term = (Terminal *)ctx; int update = FALSE; if (term->tblink_pending && now == term->next_tblink) { term->tblinker = !term->tblinker; term->tblink_pending = FALSE; term_schedule_tblink(term); update = TRUE; } if (term->cblink_pending && now == term->next_cblink) { term->cblinker = !term->cblinker; term->cblink_pending = FALSE; term_schedule_cblink(term); update = TRUE; } if (term->in_vbell && now == term->vbell_end) { term->in_vbell = FALSE; update = TRUE; } if (update || (term->window_update_pending && now == term->next_update)) term_update(term); } static void term_schedule_update(Terminal *term) { if (!term->window_update_pending) { term->window_update_pending = TRUE; |
︙ | ︙ | |||
1129 1130 1131 1132 1133 1134 1135 | } /* * Likewise with cursor blinks. */ static void term_schedule_cblink(Terminal *term) { | | | 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 | } /* * Likewise with cursor blinks. */ static void term_schedule_cblink(Terminal *term) { if (term->blink_cur && term->has_focus) { if (!term->cblink_pending) term->next_cblink = schedule_timer(CBLINK_DELAY, term_timer, term); term->cblink_pending = TRUE; } else { term->cblinker = 1; /* reset when not in use */ term->cblink_pending = FALSE; } |
︙ | ︙ | |||
1193 1194 1195 1196 1197 1198 1199 | else term->alt_b = term->marg_b = 0; if (term->cols != -1) { int i; for (i = 0; i < term->cols; i++) term->tabs[i] = (i % 8 == 0 ? TRUE : FALSE); } | | | | | | | > > > | | 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 | else term->alt_b = term->marg_b = 0; if (term->cols != -1) { int i; for (i = 0; i < term->cols; i++) term->tabs[i] = (i % 8 == 0 ? TRUE : FALSE); } term->alt_om = term->dec_om = conf_get_int(term->conf, CONF_dec_om); term->alt_ins = term->insert = FALSE; term->alt_wnext = term->wrapnext = term->save_wnext = term->alt_save_wnext = FALSE; term->alt_wrap = term->wrap = conf_get_int(term->conf, CONF_wrap_mode); term->alt_cset = term->cset = term->save_cset = term->alt_save_cset = 0; term->alt_utf = term->utf = term->save_utf = term->alt_save_utf = 0; term->utf_state = 0; term->alt_sco_acs = term->sco_acs = term->save_sco_acs = term->alt_save_sco_acs = 0; term->cset_attr[0] = term->cset_attr[1] = term->save_csattr = term->alt_save_csattr = CSET_ASCII; term->rvideo = 0; term->in_vbell = FALSE; term->cursor_on = 1; term->big_cursor = 0; term->default_attr = term->save_attr = term->alt_save_attr = term->curr_attr = ATTR_DEFAULT; term->term_editing = term->term_echoing = FALSE; term->app_cursor_keys = conf_get_int(term->conf, CONF_app_cursor); term->app_keypad_keys = conf_get_int(term->conf, CONF_app_keypad); term->use_bce = conf_get_int(term->conf, CONF_bce); term->blink_is_real = conf_get_int(term->conf, CONF_blinktext); term->erase_char = term->basic_erase_char; term->alt_which = 0; term_print_finish(term); term->xterm_mouse = 0; term->xterm_extended_mouse = 0; term->urxvt_extended_mouse = 0; set_raw_mouse_mode(term->frontend, FALSE); term->bracketed_paste = FALSE; { int i; for (i = 0; i < 256; i++) term->wordness[i] = conf_get_int_int(term->conf, CONF_wordness, i); } if (term->screen) { swap_screen(term, 1, FALSE, FALSE); erase_lots(term, FALSE, TRUE, TRUE); swap_screen(term, 0, FALSE, FALSE); if (clear) erase_lots(term, FALSE, TRUE, TRUE); |
︙ | ︙ | |||
1257 1258 1259 1260 1261 1262 1263 | Context ctx; term->window_update_pending = FALSE; ctx = get_ctx(term->frontend); if (ctx) { int need_sbar_update = term->seen_disp_event; | | | 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 | Context ctx; term->window_update_pending = FALSE; ctx = get_ctx(term->frontend); if (ctx) { int need_sbar_update = term->seen_disp_event; if (term->seen_disp_event && term->scroll_on_disp) { term->disptop = 0; /* return to main screen */ term->seen_disp_event = 0; need_sbar_update = TRUE; } if (need_sbar_update) update_sbar(term); |
︙ | ︙ | |||
1296 1297 1298 1299 1300 1301 1302 | } term->beeptail = NULL; term->nbeeps = 0; /* * Reset the scrollback on keypress, if we're doing that. */ | | | 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 | } term->beeptail = NULL; term->nbeeps = 0; /* * Reset the scrollback on keypress, if we're doing that. */ if (term->scroll_on_key) { term->disptop = 0; /* return to main screen */ seen_disp_event(term); } } /* * Same as power_on(), but an external function. |
︙ | ︙ | |||
1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 | static void set_erase_char(Terminal *term) { term->erase_char = term->basic_erase_char; if (term->use_bce) term->erase_char.attr = (term->curr_attr & (ATTR_FGMASK | ATTR_BGMASK)); } /* * When the user reconfigures us, we need to check the forbidden- * alternate-screen config option, disable raw mouse mode if the * user has disabled mouse reporting, and abandon a print job if * the user has disabled printing. */ | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | > | > | > | > | > | > | | > | > | | | | | | | | | > | | < > > | 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 | static void set_erase_char(Terminal *term) { term->erase_char = term->basic_erase_char; if (term->use_bce) term->erase_char.attr = (term->curr_attr & (ATTR_FGMASK | ATTR_BGMASK)); } /* * We copy a bunch of stuff out of the Conf structure into local * fields in the Terminal structure, to avoid the repeated tree234 * lookups which would be involved in fetching them from the former * every time. */ void term_copy_stuff_from_conf(Terminal *term) { term->ansi_colour = conf_get_int(term->conf, CONF_ansi_colour); term->arabicshaping = conf_get_int(term->conf, CONF_arabicshaping); term->beep = conf_get_int(term->conf, CONF_beep); term->bellovl = conf_get_int(term->conf, CONF_bellovl); term->bellovl_n = conf_get_int(term->conf, CONF_bellovl_n); term->bellovl_s = conf_get_int(term->conf, CONF_bellovl_s); term->bellovl_t = conf_get_int(term->conf, CONF_bellovl_t); term->bidi = conf_get_int(term->conf, CONF_bidi); term->bksp_is_delete = conf_get_int(term->conf, CONF_bksp_is_delete); term->blink_cur = conf_get_int(term->conf, CONF_blink_cur); term->blinktext = conf_get_int(term->conf, CONF_blinktext); term->cjk_ambig_wide = conf_get_int(term->conf, CONF_cjk_ambig_wide); term->conf_height = conf_get_int(term->conf, CONF_height); term->conf_width = conf_get_int(term->conf, CONF_width); term->crhaslf = conf_get_int(term->conf, CONF_crhaslf); term->erase_to_scrollback = conf_get_int(term->conf, CONF_erase_to_scrollback); term->funky_type = conf_get_int(term->conf, CONF_funky_type); term->lfhascr = conf_get_int(term->conf, CONF_lfhascr); term->logflush = conf_get_int(term->conf, CONF_logflush); term->logtype = conf_get_int(term->conf, CONF_logtype); term->mouse_override = conf_get_int(term->conf, CONF_mouse_override); term->nethack_keypad = conf_get_int(term->conf, CONF_nethack_keypad); term->no_alt_screen = conf_get_int(term->conf, CONF_no_alt_screen); term->no_applic_c = conf_get_int(term->conf, CONF_no_applic_c); term->no_applic_k = conf_get_int(term->conf, CONF_no_applic_k); term->no_dbackspace = conf_get_int(term->conf, CONF_no_dbackspace); term->no_mouse_rep = conf_get_int(term->conf, CONF_no_mouse_rep); term->no_remote_charset = conf_get_int(term->conf, CONF_no_remote_charset); term->no_remote_resize = conf_get_int(term->conf, CONF_no_remote_resize); term->no_remote_wintitle = conf_get_int(term->conf, CONF_no_remote_wintitle); term->rawcnp = conf_get_int(term->conf, CONF_rawcnp); term->rect_select = conf_get_int(term->conf, CONF_rect_select); term->remote_qtitle_action = conf_get_int(term->conf, CONF_remote_qtitle_action); term->rxvt_homeend = conf_get_int(term->conf, CONF_rxvt_homeend); term->scroll_on_disp = conf_get_int(term->conf, CONF_scroll_on_disp); term->scroll_on_key = conf_get_int(term->conf, CONF_scroll_on_key); term->xterm_256_colour = conf_get_int(term->conf, CONF_xterm_256_colour); /* * Parse the control-character escapes in the configured * answerback string. */ { char *answerback = conf_get_str(term->conf, CONF_answerback); int maxlen = strlen(answerback); term->answerback = snewn(maxlen, char); term->answerbacklen = 0; while (*answerback) { char *n; char c = ctrlparse(answerback, &n); if (n) { term->answerback[term->answerbacklen++] = c; answerback = n; } else { term->answerback[term->answerbacklen++] = *answerback++; } } } } /* * When the user reconfigures us, we need to check the forbidden- * alternate-screen config option, disable raw mouse mode if the * user has disabled mouse reporting, and abandon a print job if * the user has disabled printing. */ void term_reconfig(Terminal *term, Conf *conf) { /* * Before adopting the new config, check all those terminal * settings which control power-on defaults; and if they've * changed, we will modify the current state as well as the * default one. The full list is: Auto wrap mode, DEC Origin * Mode, BCE, blinking text, character classes. */ int reset_wrap, reset_decom, reset_bce, reset_tblink, reset_charclass; int i; reset_wrap = (conf_get_int(term->conf, CONF_wrap_mode) != conf_get_int(conf, CONF_wrap_mode)); reset_decom = (conf_get_int(term->conf, CONF_dec_om) != conf_get_int(conf, CONF_dec_om)); reset_bce = (conf_get_int(term->conf, CONF_bce) != conf_get_int(conf, CONF_bce)); reset_tblink = (conf_get_int(term->conf, CONF_blinktext) != conf_get_int(conf, CONF_blinktext)); reset_charclass = 0; for (i = 0; i < 256; i++) if (conf_get_int_int(term->conf, CONF_wordness, i) != conf_get_int_int(conf, CONF_wordness, i)) reset_charclass = 1; /* * If the bidi or shaping settings have changed, flush the bidi * cache completely. */ if (conf_get_int(term->conf, CONF_arabicshaping) != conf_get_int(conf, CONF_arabicshaping) || conf_get_int(term->conf, CONF_bidi) != conf_get_int(conf, CONF_bidi)) { for (i = 0; i < term->bidi_cache_size; i++) { sfree(term->pre_bidi_cache[i].chars); sfree(term->post_bidi_cache[i].chars); term->pre_bidi_cache[i].width = -1; term->pre_bidi_cache[i].chars = NULL; term->post_bidi_cache[i].width = -1; term->post_bidi_cache[i].chars = NULL; } } conf_free(term->conf); term->conf = conf_copy(conf); if (reset_wrap) term->alt_wrap = term->wrap = conf_get_int(term->conf, CONF_wrap_mode); if (reset_decom) term->alt_om = term->dec_om = conf_get_int(term->conf, CONF_dec_om); if (reset_bce) { term->use_bce = conf_get_int(term->conf, CONF_bce); set_erase_char(term); } if (reset_tblink) { term->blink_is_real = conf_get_int(term->conf, CONF_blinktext); } if (reset_charclass) for (i = 0; i < 256; i++) term->wordness[i] = conf_get_int_int(term->conf, CONF_wordness, i); if (conf_get_int(term->conf, CONF_no_alt_screen)) swap_screen(term, 0, FALSE, FALSE); if (conf_get_int(term->conf, CONF_no_mouse_rep)) { term->xterm_mouse = 0; set_raw_mouse_mode(term->frontend, 0); } if (conf_get_int(term->conf, CONF_no_remote_charset)) { term->cset_attr[0] = term->cset_attr[1] = CSET_ASCII; term->sco_acs = term->alt_sco_acs = 0; term->utf = 0; } if (!conf_get_str(term->conf, CONF_printer)) { term_print_finish(term); } term_schedule_tblink(term); term_schedule_cblink(term); term_copy_stuff_from_conf(term); } /* * Clear the scrollback. */ void term_clrsb(Terminal *term) { unsigned char *line; term->disptop = 0; while ((line = delpos234(term->scrollback, 0)) != NULL) { sfree(line); /* this is compressed data, not a termline */ } term->tempsblines = 0; term->alt_sblines = 0; update_sbar(term); } /* * Initialise the terminal. */ Terminal *term_init(Conf *myconf, struct unicode_data *ucsdata, void *frontend) { Terminal *term; /* * Allocate a new Terminal structure and initialise the fields * that need it. */ term = snew(Terminal); term->frontend = frontend; term->ucsdata = ucsdata; term->conf = conf_copy(myconf); term->logctx = NULL; term->compatibility_level = TM_PUTTY; strcpy(term->id_string, "\033[?6c"); term->cblink_pending = term->tblink_pending = FALSE; term->paste_buffer = NULL; term->paste_len = 0; bufchain_init(&term->inbuf); bufchain_init(&term->printer_buf); term->printing = term->only_printing = FALSE; term->print_job = NULL; term->vt52_mode = FALSE; term->cr_lf_return = FALSE; term->seen_disp_event = FALSE; term->mouse_is_down = FALSE; term->reset_132 = FALSE; term->cblinker = term->tblinker = 0; term->has_focus = 1; term->repeat_off = FALSE; term->termstate = TOPLEVEL; term->selstate = NO_SELECTION; term->curstype = 0; term_copy_stuff_from_conf(term); term->screen = term->alt_screen = term->scrollback = NULL; term->tempsblines = 0; term->alt_sblines = 0; term->disptop = 0; term->disptext = NULL; term->dispcursx = term->dispcursy = -1; |
︙ | ︙ | |||
1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 | sfree(term->ltemp); sfree(term->wcFrom); sfree(term->wcTo); for (i = 0; i < term->bidi_cache_size; i++) { sfree(term->pre_bidi_cache[i].chars); sfree(term->post_bidi_cache[i].chars); } sfree(term->pre_bidi_cache); sfree(term->post_bidi_cache); expire_timer_context(term); sfree(term); } /* * Set up the terminal for a given size. */ | > > > > > > | 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 | sfree(term->ltemp); sfree(term->wcFrom); sfree(term->wcTo); for (i = 0; i < term->bidi_cache_size; i++) { sfree(term->pre_bidi_cache[i].chars); sfree(term->post_bidi_cache[i].chars); sfree(term->post_bidi_cache[i].forward); sfree(term->post_bidi_cache[i].backward); } sfree(term->pre_bidi_cache); sfree(term->post_bidi_cache); sfree(term->tabs); expire_timer_context(term); conf_free(term->conf); sfree(term); } /* * Set up the terminal for a given size. */ |
︙ | ︙ | |||
1623 1624 1625 1626 1627 1628 1629 | } term->rows += 1; } /* Do this loop to shrink the screen if newrows < rows */ while (term->rows > newrows) { if (term->curs.y < term->rows - 1) { /* delete bottom row, unless it contains the cursor */ | | > | 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 | } term->rows += 1; } /* Do this loop to shrink the screen if newrows < rows */ while (term->rows > newrows) { if (term->curs.y < term->rows - 1) { /* delete bottom row, unless it contains the cursor */ line = delpos234(term->screen, term->rows - 1); freeline(line); } else { /* push top row to scrollback */ line = delpos234(term->screen, 0); addpos234(term->scrollback, compressline(line), sblen++); freeline(line); term->tempsblines += 1; term->curs.y -= 1; |
︙ | ︙ | |||
1885 1886 1887 1888 1889 1890 1891 | * Scroll the screen. (`lines' is +ve for scrolling forward, -ve * for backward.) `sb' is TRUE if the scrolling is permitted to * affect the scrollback buffer. */ static void scroll(Terminal *term, int topline, int botline, int lines, int sb) { termline *line; | | > > > > > > | | 1975 1976 1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 | * Scroll the screen. (`lines' is +ve for scrolling forward, -ve * for backward.) `sb' is TRUE if the scrolling is permitted to * affect the scrollback buffer. */ static void scroll(Terminal *term, int topline, int botline, int lines, int sb) { termline *line; int i, seltop, scrollwinsize; #ifdef OPTIMISE_SCROLL int olddisptop, shift; #endif /* OPTIMISE_SCROLL */ if (topline != 0 || term->alt_which != 0) sb = FALSE; #ifdef OPTIMISE_SCROLL olddisptop = term->disptop; shift = lines; #endif /* OPTIMISE_SCROLL */ scrollwinsize = botline - topline + 1; if (lines < 0) { lines = -lines; if (lines > scrollwinsize) lines = scrollwinsize; while (lines-- > 0) { line = delpos234(term->screen, botline); resizeline(term, line, term->cols); for (i = 0; i < term->cols; i++) copy_termchar(line, i, &term->erase_char); line->lattr = LATTR_NORM; addpos234(term->screen, line, topline); |
︙ | ︙ | |||
1920 1921 1922 1923 1924 1925 1926 | if (term->selend.y >= topline && term->selend.y <= botline) { term->selend.y++; if (term->selend.y > botline) { term->selend.y = botline + 1; term->selend.x = 0; } } | | < < > > | | 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032 2033 2034 | if (term->selend.y >= topline && term->selend.y <= botline) { term->selend.y++; if (term->selend.y > botline) { term->selend.y = botline + 1; term->selend.x = 0; } } } } else { if (lines > scrollwinsize) lines = scrollwinsize; while (lines-- > 0) { line = delpos234(term->screen, topline); #ifdef TERM_CC_DIAGS cc_check(line); #endif if (sb && term->savelines > 0) { int sblen = count234(term->scrollback); /* |
︙ | ︙ | |||
2010 2011 2012 2013 2014 2015 2016 | term->selanchor.y--; if (term->selanchor.y < seltop) { term->selanchor.y = seltop; term->selanchor.x = 0; } } } | < < | 2106 2107 2108 2109 2110 2111 2112 2113 2114 2115 2116 2117 2118 2119 | term->selanchor.y--; if (term->selanchor.y < seltop) { term->selanchor.y = seltop; term->selanchor.x = 0; } } } } } #ifdef OPTIMISE_SCROLL shift += term->disptop - olddisptop; if (shift < term->rows && shift > -term->rows && shift != 0) scroll_display(term, topline, botline, shift); #endif /* OPTIMISE_SCROLL */ |
︙ | ︙ | |||
2239 2240 2241 2242 2243 2244 2245 | term_invalidate(term); /* Lines scrolled away shouldn't be brought back on if the terminal * resizes. */ if (start.y == 0 && start.x == 0 && end.x == 0 && erase_lattr) erasing_lines_from_top = 1; | | | 2333 2334 2335 2336 2337 2338 2339 2340 2341 2342 2343 2344 2345 2346 2347 | term_invalidate(term); /* Lines scrolled away shouldn't be brought back on if the terminal * resizes. */ if (start.y == 0 && start.x == 0 && end.x == 0 && erase_lattr) erasing_lines_from_top = 1; if (term->erase_to_scrollback && erasing_lines_from_top) { /* If it's a whole number of lines, starting at the top, and * we're fully erasing them, erase by scrolling and keep the * lines in the scrollback. */ int scrolllines = end.y; if (end.y == term->rows) { /* Shrink until we find a non-empty row.*/ scrolllines = find_last_nonempty_line(term, term->screen) + 1; |
︙ | ︙ | |||
2330 2331 2332 2333 2334 2335 2336 | break; case 2: /* DECANM: VT52 mode */ term->vt52_mode = !state; if (term->vt52_mode) { term->blink_is_real = FALSE; term->vt52_bold = FALSE; } else { | | | | 2424 2425 2426 2427 2428 2429 2430 2431 2432 2433 2434 2435 2436 2437 2438 2439 2440 2441 2442 2443 2444 | break; case 2: /* DECANM: VT52 mode */ term->vt52_mode = !state; if (term->vt52_mode) { term->blink_is_real = FALSE; term->vt52_bold = FALSE; } else { term->blink_is_real = term->blinktext; } term_schedule_tblink(term); break; case 3: /* DECCOLM: 80/132 columns */ deselect(term); if (!term->no_remote_resize) request_resize(term->frontend, state ? 132 : 80, term->rows); term->reset_132 = state; term->alt_t = term->marg_t = 0; term->alt_b = term->marg_b = term->rows - 1; move(term, 0, 0, 0); erase_lots(term, FALSE, TRUE, TRUE); break; |
︙ | ︙ | |||
2383 2384 2385 2386 2387 2388 2389 | compatibility2(OTHER, VT220); term->cursor_on = state; seen_disp_event(term); break; case 47: /* alternate screen */ compatibility(OTHER); deselect(term); | | > > > > > > | | | | | > > > | 2477 2478 2479 2480 2481 2482 2483 2484 2485 2486 2487 2488 2489 2490 2491 2492 2493 2494 2495 2496 2497 2498 2499 2500 2501 2502 2503 2504 2505 2506 2507 2508 2509 2510 2511 2512 2513 2514 2515 2516 2517 2518 2519 2520 2521 2522 2523 2524 2525 2526 2527 2528 2529 2530 2531 2532 | compatibility2(OTHER, VT220); term->cursor_on = state; seen_disp_event(term); break; case 47: /* alternate screen */ compatibility(OTHER); deselect(term); swap_screen(term, term->no_alt_screen ? 0 : state, FALSE, FALSE); term->disptop = 0; break; case 1000: /* xterm mouse 1 (normal) */ term->xterm_mouse = state ? 1 : 0; set_raw_mouse_mode(term->frontend, state); break; case 1002: /* xterm mouse 2 (inc. button drags) */ term->xterm_mouse = state ? 2 : 0; set_raw_mouse_mode(term->frontend, state); break; case 1006: /* xterm extended mouse */ term->xterm_extended_mouse = state ? 1 : 0; break; case 1015: /* urxvt extended mouse */ term->urxvt_extended_mouse = state ? 1 : 0; break; case 1047: /* alternate screen */ compatibility(OTHER); deselect(term); swap_screen(term, term->no_alt_screen ? 0 : state, TRUE, TRUE); term->disptop = 0; break; case 1048: /* save/restore cursor */ if (!term->no_alt_screen) save_cursor(term, state); if (!state) seen_disp_event(term); break; case 1049: /* cursor & alternate screen */ if (state && !term->no_alt_screen) save_cursor(term, state); if (!state) seen_disp_event(term); compatibility(OTHER); deselect(term); swap_screen(term, term->no_alt_screen ? 0 : state, TRUE, FALSE); if (!state && !term->no_alt_screen) save_cursor(term, state); term->disptop = 0; break; case 2004: /* xterm bracketed paste */ term->bracketed_paste = state ? TRUE : FALSE; break; } else switch (mode) { case 4: /* IRM: set insert mode */ compatibility(VT102); term->insert = state; break; case 12: /* SRM: set echo mode */ |
︙ | ︙ | |||
2450 2451 2452 2453 2454 2455 2456 | term->wordness[(unsigned char) term->osc_string[term->osc_strlen]] = term->esc_args[0]; } else { term->osc_string[term->osc_strlen] = '\0'; switch (term->esc_args[0]) { case 0: case 1: | | | | | | 2553 2554 2555 2556 2557 2558 2559 2560 2561 2562 2563 2564 2565 2566 2567 2568 2569 2570 2571 2572 2573 2574 2575 2576 2577 2578 2579 2580 2581 2582 2583 2584 2585 2586 2587 | term->wordness[(unsigned char) term->osc_string[term->osc_strlen]] = term->esc_args[0]; } else { term->osc_string[term->osc_strlen] = '\0'; switch (term->esc_args[0]) { case 0: case 1: if (!term->no_remote_wintitle) set_icon(term->frontend, term->osc_string); if (term->esc_args[0] == 1) break; /* fall through: parameter 0 means set both */ case 2: case 21: if (!term->no_remote_wintitle) set_title(term->frontend, term->osc_string); break; } } } /* * ANSI printing routines. */ static void term_print_setup(Terminal *term, char *printer) { bufchain_clear(&term->printer_buf); term->print_job = printer_start_job(printer); } static void term_print_flush(Terminal *term) { void *data; int len; int size; while ((size = bufchain_size(&term->printer_buf)) > 5) { |
︙ | ︙ | |||
2545 2546 2547 2548 2549 2550 2551 | c = *chars++; nchars--; /* * Optionally log the session traffic to a file. Useful for * debugging and possibly also useful for actual logging. */ | | | 2648 2649 2650 2651 2652 2653 2654 2655 2656 2657 2658 2659 2660 2661 2662 | c = *chars++; nchars--; /* * Optionally log the session traffic to a file. Useful for * debugging and possibly also useful for actual logging. */ if (term->logtype == LGTYP_DEBUG && term->logctx) logtraffic(term->logctx, (unsigned char) c, LGTYP_DEBUG); } else { c = unget; unget = -1; } /* Note only VT220+ are 8-bit VT102 is seven bit, it shouldn't even |
︙ | ︙ | |||
2737 2738 2739 2740 2741 2742 2743 | /* Or the GL control. */ if (c == '\177' && term->termstate < DO_CTRLS && has_compat(OTHER)) { if (term->curs.x && !term->wrapnext) term->curs.x--; term->wrapnext = FALSE; /* destructive backspace might be disabled */ | | < < < < < < < < < < < | | 2840 2841 2842 2843 2844 2845 2846 2847 2848 2849 2850 2851 2852 2853 2854 2855 2856 2857 2858 2859 2860 2861 2862 2863 2864 2865 2866 2867 2868 2869 2870 2871 2872 2873 2874 2875 | /* Or the GL control. */ if (c == '\177' && term->termstate < DO_CTRLS && has_compat(OTHER)) { if (term->curs.x && !term->wrapnext) term->curs.x--; term->wrapnext = FALSE; /* destructive backspace might be disabled */ if (!term->no_dbackspace) { check_boundary(term, term->curs.x, term->curs.y); check_boundary(term, term->curs.x+1, term->curs.y); copy_termchar(scrlineptr(term->curs.y), term->curs.x, &term->erase_char); } } else /* Or normal C0 controls. */ if ((c & ~0x1F) == 0 && term->termstate < DO_CTRLS) { switch (c) { case '\005': /* ENQ: terminal type query */ /* * Strictly speaking this is VT100 but a VT100 defaults to * no response. Other terminals respond at their option. * * Don't put a CR in the default string as this tends to * upset some weird software. */ compatibility(ANSIMIN); if (term->ldisc) { lpage_send(term->ldisc, DEFAULT_CODEPAGE, term->answerback, term->answerbacklen, 0); } break; case '\007': /* BEL: Bell */ { struct beeptime *newbeep; unsigned long ticks; |
︙ | ︙ | |||
2796 2797 2798 2799 2800 2801 2802 | } /* * Throw out any beeps that happened more than * t seconds ago. */ while (term->beephead && | | | | | | | | | | 2888 2889 2890 2891 2892 2893 2894 2895 2896 2897 2898 2899 2900 2901 2902 2903 2904 2905 2906 2907 2908 2909 2910 2911 2912 2913 2914 2915 2916 2917 2918 2919 2920 2921 2922 2923 2924 2925 2926 2927 2928 2929 2930 2931 2932 2933 2934 2935 2936 | } /* * Throw out any beeps that happened more than * t seconds ago. */ while (term->beephead && term->beephead->ticks < ticks - term->bellovl_t) { struct beeptime *tmp = term->beephead; term->beephead = tmp->next; sfree(tmp); if (!term->beephead) term->beeptail = NULL; term->nbeeps--; } if (term->bellovl && term->beep_overloaded && ticks - term->lastbeep >= (unsigned)term->bellovl_s) { /* * If we're currently overloaded and the * last beep was more than s seconds ago, * leave overload mode. */ term->beep_overloaded = FALSE; } else if (term->bellovl && !term->beep_overloaded && term->nbeeps >= term->bellovl_n) { /* * Now, if we have n or more beeps * remaining in the queue, go into overload * mode. */ term->beep_overloaded = TRUE; } term->lastbeep = ticks; /* * Perform an actual beep if we're not overloaded. */ if (!term->bellovl || !term->beep_overloaded) { do_beep(term->frontend, term->beep); if (term->beep == BELL_VISUAL) { term_schedule_vbell(term, FALSE, 0); } } seen_disp_event(term); } break; case '\b': /* BS: Back space */ |
︙ | ︙ | |||
2870 2871 2872 2873 2874 2875 2876 | term->esc_query = FALSE; } break; case '\015': /* CR: Carriage return */ term->curs.x = 0; term->wrapnext = FALSE; seen_disp_event(term); | < | | | | | | | < | 2962 2963 2964 2965 2966 2967 2968 2969 2970 2971 2972 2973 2974 2975 2976 2977 2978 2979 2980 2981 2982 2983 2984 2985 2986 2987 2988 2989 2990 2991 2992 2993 2994 2995 2996 2997 2998 2999 3000 3001 3002 3003 3004 3005 | term->esc_query = FALSE; } break; case '\015': /* CR: Carriage return */ term->curs.x = 0; term->wrapnext = FALSE; seen_disp_event(term); if (term->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; case '\014': /* FF: Form feed */ if (has_compat(SCOANSI)) { move(term, 0, 0, 0); erase_lots(term, FALSE, FALSE, TRUE); term->disptop = 0; term->wrapnext = FALSE; seen_disp_event(term); break; } case '\013': /* VT: Line tabulation */ compatibility(VT100); case '\012': /* LF: Line feed */ 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->lfhascr) term->curs.x = 0; term->wrapnext = FALSE; seen_disp_event(term); if (term->logctx) logtraffic(term->logctx, (unsigned char) c, LGTYP_ASCII); break; case '\t': /* HT: Character tabulation */ { pos old_curs = term->curs; termline *ldata = scrlineptr(term->curs.y); |
︙ | ︙ | |||
2939 2940 2941 2942 2943 2944 2945 | * ctrls are stripped above */ { termline *cline = scrlineptr(term->curs.y); int width = 0; if (DIRECT_CHAR(c)) width = 1; if (!width) | | | | | 3029 3030 3031 3032 3033 3034 3035 3036 3037 3038 3039 3040 3041 3042 3043 3044 3045 | * ctrls are stripped above */ { termline *cline = scrlineptr(term->curs.y); int width = 0; if (DIRECT_CHAR(c)) width = 1; if (!width) width = (term->cjk_ambig_wide ? mk_wcwidth_cjk((unsigned int) c) : mk_wcwidth((unsigned int) c)); if (term->wrapnext && term->wrap && width > 0) { cline->lattr |= LATTR_WRAPPED; 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++; |
︙ | ︙ | |||
3162 3163 3164 3165 3166 3167 3168 | break; case 'c': /* RIS: restore power-on settings */ compatibility(VT100); power_on(term, TRUE); if (term->ldisc) /* cause ldisc to notice changes */ ldisc_send(term->ldisc, NULL, 0, 0); if (term->reset_132) { | | | 3252 3253 3254 3255 3256 3257 3258 3259 3260 3261 3262 3263 3264 3265 3266 | break; case 'c': /* RIS: restore power-on settings */ compatibility(VT100); power_on(term, TRUE); if (term->ldisc) /* cause ldisc to notice changes */ ldisc_send(term->ldisc, NULL, 0, 0); if (term->reset_132) { if (!term->no_remote_resize) request_resize(term->frontend, 80, term->rows); term->reset_132 = 0; } term->disptop = 0; seen_disp_event(term); break; case 'H': /* HTS: set a tab */ |
︙ | ︙ | |||
3227 3228 3229 3230 3231 3232 3233 | } scrlineptr(term->curs.y)->lattr = nlattr; } break; /* GZD4: G0 designate 94-set */ case ANSI('A', '('): compatibility(VT100); | | | | | | | | | | | | 3317 3318 3319 3320 3321 3322 3323 3324 3325 3326 3327 3328 3329 3330 3331 3332 3333 3334 3335 3336 3337 3338 3339 3340 3341 3342 3343 3344 3345 3346 3347 3348 3349 3350 3351 3352 3353 3354 3355 3356 3357 3358 3359 3360 3361 3362 3363 3364 3365 3366 3367 3368 3369 3370 3371 3372 3373 3374 3375 3376 3377 3378 3379 | } scrlineptr(term->curs.y)->lattr = nlattr; } break; /* GZD4: G0 designate 94-set */ case ANSI('A', '('): compatibility(VT100); if (!term->no_remote_charset) term->cset_attr[0] = CSET_GBCHR; break; case ANSI('B', '('): compatibility(VT100); if (!term->no_remote_charset) term->cset_attr[0] = CSET_ASCII; break; case ANSI('0', '('): compatibility(VT100); if (!term->no_remote_charset) term->cset_attr[0] = CSET_LINEDRW; break; case ANSI('U', '('): compatibility(OTHER); if (!term->no_remote_charset) term->cset_attr[0] = CSET_SCOACS; break; /* G1D4: G1-designate 94-set */ case ANSI('A', ')'): compatibility(VT100); if (!term->no_remote_charset) term->cset_attr[1] = CSET_GBCHR; break; case ANSI('B', ')'): compatibility(VT100); if (!term->no_remote_charset) term->cset_attr[1] = CSET_ASCII; break; case ANSI('0', ')'): compatibility(VT100); if (!term->no_remote_charset) term->cset_attr[1] = CSET_LINEDRW; break; case ANSI('U', ')'): compatibility(OTHER); if (!term->no_remote_charset) term->cset_attr[1] = CSET_SCOACS; break; /* DOCS: Designate other coding system */ case ANSI('8', '%'): /* Old Linux code */ case ANSI('G', '%'): compatibility(OTHER); if (!term->no_remote_charset) term->utf = 1; break; case ANSI('@', '%'): compatibility(OTHER); if (!term->no_remote_charset) term->utf = 0; break; } break; case SEEN_CSI: term->termstate = TOPLEVEL; /* default */ if (isdigit(c)) { |
︙ | ︙ | |||
3459 3460 3461 3462 3463 3464 3465 3466 | term->esc_query, TRUE); } break; case 'i': /* MC: Media copy */ case ANSI_QUE('i'): compatibility(VT100); { if (term->esc_nargs != 1) break; | > | > > | | 3549 3550 3551 3552 3553 3554 3555 3556 3557 3558 3559 3560 3561 3562 3563 3564 3565 3566 3567 3568 3569 3570 3571 | term->esc_query, TRUE); } break; case 'i': /* MC: Media copy */ case ANSI_QUE('i'): compatibility(VT100); { char *printer; if (term->esc_nargs != 1) break; if (term->esc_args[0] == 5 && (printer = conf_get_str(term->conf, CONF_printer))[0]) { term->printing = TRUE; term->only_printing = !term->esc_query; term->print_state = 0; term_print_setup(term, printer); } else if (term->esc_args[0] == 4 && term->printing) { term_print_finish(term); } } break; case 'l': /* RM: toggle modes to low */ |
︙ | ︙ | |||
3587 3588 3589 3590 3591 3592 3593 | term_schedule_tblink(term); break; case 7: /* enable reverse video */ term->curr_attr |= ATTR_REVERSE; break; case 10: /* SCO acs off */ compatibility(SCOANSI); | | | | | 3680 3681 3682 3683 3684 3685 3686 3687 3688 3689 3690 3691 3692 3693 3694 3695 3696 3697 3698 3699 3700 3701 3702 | term_schedule_tblink(term); break; case 7: /* enable reverse video */ term->curr_attr |= ATTR_REVERSE; break; case 10: /* SCO acs off */ compatibility(SCOANSI); if (term->no_remote_charset) break; term->sco_acs = 0; break; case 11: /* SCO acs on */ compatibility(SCOANSI); if (term->no_remote_charset) break; term->sco_acs = 1; break; case 12: /* SCO acs on, |0x80 */ compatibility(SCOANSI); if (term->no_remote_charset) break; term->sco_acs = 2; break; case 22: /* disable bold */ compatibility2(OTHER, VT220); term->curr_attr &= ~ATTR_BOLD; break; case 24: /* disable underline */ compatibility2(OTHER, VT220); |
︙ | ︙ | |||
3718 3719 3720 3721 3722 3723 3724 | * illegal values (eg first arg 1..9) for window changing * and reports. */ if (term->esc_nargs <= 1 && (term->esc_args[0] < 1 || term->esc_args[0] >= 24)) { compatibility(VT340TEXT); | | | | 3811 3812 3813 3814 3815 3816 3817 3818 3819 3820 3821 3822 3823 3824 3825 3826 3827 3828 3829 3830 3831 3832 3833 3834 3835 3836 3837 3838 3839 3840 3841 3842 3843 3844 3845 | * illegal values (eg first arg 1..9) for window changing * and reports. */ if (term->esc_nargs <= 1 && (term->esc_args[0] < 1 || term->esc_args[0] >= 24)) { compatibility(VT340TEXT); if (!term->no_remote_resize) request_resize(term->frontend, term->cols, def(term->esc_args[0], 24)); deselect(term); } else if (term->esc_nargs >= 1 && term->esc_args[0] >= 1 && term->esc_args[0] < 24) { compatibility(OTHER); switch (term->esc_args[0]) { int x, y, len; char buf[80], *p; case 1: set_iconic(term->frontend, FALSE); break; case 2: set_iconic(term->frontend, TRUE); break; case 3: if (term->esc_nargs >= 3) { if (!term->no_remote_resize) move_window(term->frontend, def(term->esc_args[1], 0), def(term->esc_args[2], 0)); } break; case 4: /* We should resize the window to a given |
︙ | ︙ | |||
3763 3764 3765 3766 3767 3768 3769 | set_zorder(term->frontend, FALSE); break; case 7: refresh_window(term->frontend); break; case 8: if (term->esc_nargs >= 3) { | | | | | 3856 3857 3858 3859 3860 3861 3862 3863 3864 3865 3866 3867 3868 3869 3870 3871 3872 3873 | set_zorder(term->frontend, FALSE); break; case 7: refresh_window(term->frontend); break; case 8: if (term->esc_nargs >= 3) { if (!term->no_remote_resize) request_resize(term->frontend, def(term->esc_args[2], term->conf_width), def(term->esc_args[1], term->conf_height)); } break; case 9: if (term->esc_nargs >= 2) set_zoomed(term->frontend, term->esc_args[1] ? TRUE : FALSE); |
︙ | ︙ | |||
3821 3822 3823 3824 3825 3826 3827 | * moment and see if anyone * complains, and then ask them * what they would like it to do. */ break; case 20: if (term->ldisc && | | | | | | 3914 3915 3916 3917 3918 3919 3920 3921 3922 3923 3924 3925 3926 3927 3928 3929 3930 3931 3932 3933 3934 3935 3936 3937 3938 3939 3940 3941 3942 | * moment and see if anyone * complains, and then ask them * what they would like it to do. */ break; case 20: if (term->ldisc && term->remote_qtitle_action != TITLE_NONE) { if(term->remote_qtitle_action == TITLE_REAL) p = get_window_title(term->frontend, TRUE); else p = EMPTY_WINDOW_TITLE; len = strlen(p); ldisc_send(term->ldisc, "\033]L", 3, 0); ldisc_send(term->ldisc, p, len, 0); ldisc_send(term->ldisc, "\033\\", 2, 0); } break; case 21: if (term->ldisc && term->remote_qtitle_action != TITLE_NONE) { if(term->remote_qtitle_action == TITLE_REAL) p = get_window_title(term->frontend, FALSE); else p = EMPTY_WINDOW_TITLE; len = strlen(p); ldisc_send(term->ldisc, "\033]l", 3, 0); ldisc_send(term->ldisc, p, len, 0); ldisc_send(term->ldisc, "\033\\", 2, 0); |
︙ | ︙ | |||
3871 3872 3873 3874 3875 3876 3877 | * Set number of lines on screen * VT420 uses VGA like hardware and can * support any size in reasonable range * (24..49 AIUI) with no default specified. */ compatibility(VT420); if (term->esc_nargs == 1 && term->esc_args[0] > 0) { | | | | > | | 3964 3965 3966 3967 3968 3969 3970 3971 3972 3973 3974 3975 3976 3977 3978 3979 3980 3981 3982 3983 3984 3985 3986 3987 3988 3989 3990 3991 3992 3993 3994 3995 3996 3997 | * Set number of lines on screen * VT420 uses VGA like hardware and can * support any size in reasonable range * (24..49 AIUI) with no default specified. */ compatibility(VT420); if (term->esc_nargs == 1 && term->esc_args[0] > 0) { if (!term->no_remote_resize) request_resize(term->frontend, term->cols, def(term->esc_args[0], term->conf_height)); deselect(term); } break; case ANSI('|', '$'): /* DECSCPP */ /* * Set number of columns per page * Docs imply range is only 80 or 132, but * I'll allow any. */ compatibility(VT340TEXT); if (term->esc_nargs <= 1) { if (!term->no_remote_resize) request_resize(term->frontend, def(term->esc_args[0], term->conf_width), term->rows); deselect(term); } break; case 'X': /* ECH: write N spaces w/o moving cursor */ /* XXX VTTEST says this is vt220, vt510 manual * says vt100 */ compatibility(ANSIMIN); |
︙ | ︙ | |||
4091 4092 4093 4094 4095 4096 4097 | strcat(term->id_string, "c"); } #if 0 /* Is this a good idea ? * Well we should do a soft reset at this point ... */ if (!has_compat(VT420) && has_compat(VT100)) { | | | 4185 4186 4187 4188 4189 4190 4191 4192 4193 4194 4195 4196 4197 4198 4199 | strcat(term->id_string, "c"); } #if 0 /* Is this a good idea ? * Well we should do a soft reset at this point ... */ if (!has_compat(VT420) && has_compat(VT100)) { if (!term->no_remote_resize) { if (term->reset_132) request_resize(132, 24); else request_resize(80, 24); } } #endif |
︙ | ︙ | |||
4326 4327 4328 4329 4330 4331 4332 | break; case '<': /* XXX This should switch to VT100 mode not current or default * VT mode. But this will only have effect in a VT220+ * emulation. */ term->vt52_mode = FALSE; | | | 4420 4421 4422 4423 4424 4425 4426 4427 4428 4429 4430 4431 4432 4433 4434 | break; case '<': /* XXX This should switch to VT100 mode not current or default * VT mode. But this will only have effect in a VT220+ * emulation. */ term->vt52_mode = FALSE; term->blink_is_real = term->blinktext; term_schedule_tblink(term); break; #if 0 case '^': /* XXX Enter auto print mode */ break; case '_': |
︙ | ︙ | |||
4470 4471 4472 4473 4474 4475 4476 | pos cursplus = term->curs; incpos(cursplus); check_selection(term, term->curs, cursplus); } } term_print_flush(term); | | | 4564 4565 4566 4567 4568 4569 4570 4571 4572 4573 4574 4575 4576 4577 4578 | pos cursplus = term->curs; incpos(cursplus); check_selection(term, term->curs, cursplus); } } term_print_flush(term); if (term->logflush) logflush(term->logctx); } /* * To prevent having to run the reasonably tricky bidi algorithm * too many times, we maintain a cache of the last lineful of data * fed to the algorithm on each line of the display. |
︙ | ︙ | |||
4573 4574 4575 4576 4577 4578 4579 | static termchar *term_bidi_line(Terminal *term, struct termline *ldata, int scr_y) { termchar *lchars; int it; /* Do Arabic shaping and bidi. */ | | | | | | | | 4667 4668 4669 4670 4671 4672 4673 4674 4675 4676 4677 4678 4679 4680 4681 4682 4683 4684 4685 4686 4687 4688 4689 4690 4691 4692 4693 4694 4695 4696 4697 4698 4699 4700 4701 4702 4703 4704 4705 4706 4707 4708 4709 4710 4711 4712 4713 4714 4715 4716 4717 4718 4719 4720 4721 4722 4723 4724 4725 4726 4727 4728 4729 4730 4731 4732 | static termchar *term_bidi_line(Terminal *term, struct termline *ldata, int scr_y) { termchar *lchars; int it; /* Do Arabic shaping and bidi. */ if(!term->bidi || !term->arabicshaping) { if (!term_bidi_cache_hit(term, scr_y, ldata->chars, term->cols)) { if (term->wcFromTo_size < term->cols) { term->wcFromTo_size = term->cols; term->wcFrom = sresize(term->wcFrom, term->wcFromTo_size, bidi_char); term->wcTo = sresize(term->wcTo, term->wcFromTo_size, bidi_char); } for(it=0; it<term->cols ; it++) { unsigned long uc = (ldata->chars[it].chr); switch (uc & CSET_MASK) { case CSET_LINEDRW: if (!term->rawcnp) { uc = term->ucsdata->unitab_xterm[uc & 0xFF]; break; } case CSET_ASCII: uc = term->ucsdata->unitab_line[uc & 0xFF]; break; case CSET_SCOACS: uc = term->ucsdata->unitab_scoacs[uc&0xFF]; break; } switch (uc & CSET_MASK) { case CSET_ACP: uc = term->ucsdata->unitab_font[uc & 0xFF]; break; case CSET_OEMCP: uc = term->ucsdata->unitab_oemcp[uc & 0xFF]; break; } term->wcFrom[it].origwc = term->wcFrom[it].wc = (unsigned int)uc; term->wcFrom[it].index = it; } if(!term->bidi) do_bidi(term->wcFrom, term->cols); /* this is saved iff done from inside the shaping */ if(!term->bidi && term->arabicshaping) for(it=0; it<term->cols; it++) term->wcTo[it] = term->wcFrom[it]; if(!term->arabicshaping) do_shape(term->wcFrom, term->wcTo, term->cols); if (term->ltemp_size < ldata->size) { term->ltemp_size = ldata->size; term->ltemp = sresize(term->ltemp, term->ltemp_size, termchar); } |
︙ | ︙ | |||
4686 4687 4688 4689 4690 4691 4692 | newline = snewn(term->cols, termchar); rv = (!term->rvideo ^ !term->in_vbell ? ATTR_REVERSE : 0); /* Depends on: * screen array, disptop, scrtop, * selection, rv, | | | | | 4780 4781 4782 4783 4784 4785 4786 4787 4788 4789 4790 4791 4792 4793 4794 4795 4796 4797 4798 4799 4800 4801 | newline = snewn(term->cols, termchar); rv = (!term->rvideo ^ !term->in_vbell ? ATTR_REVERSE : 0); /* Depends on: * screen array, disptop, scrtop, * selection, rv, * blinkpc, blink_is_real, tblinker, * curs.y, curs.x, cblinker, blink_cur, cursor_on, has_focus, wrapnext */ /* Has the cursor position or type changed ? */ if (term->cursor_on) { if (term->has_focus) { if (term->cblinker || !term->blink_cur) cursor = TATTR_ACTCURS; else cursor = 0; } else cursor = TATTR_PASCURS; if (term->wrapnext) cursor |= TATTR_RIGHTCURS; |
︙ | ︙ | |||
4801 4802 4803 4804 4805 4806 4807 | unsigned long tattr, tchar; termchar *d = lchars + j; scrpos.x = backward ? backward[j] : j; tchar = d->chr; tattr = d->attr; | | | | 4895 4896 4897 4898 4899 4900 4901 4902 4903 4904 4905 4906 4907 4908 4909 4910 4911 4912 4913 | unsigned long tattr, tchar; termchar *d = lchars + j; scrpos.x = backward ? backward[j] : j; tchar = d->chr; tattr = d->attr; if (!term->ansi_colour) tattr = (tattr & ~(ATTR_FGMASK | ATTR_BGMASK)) | ATTR_DEFFG | ATTR_DEFBG; if (!term->xterm_256_colour) { int colour; colour = (tattr & ATTR_FGMASK) >> ATTR_FGSHIFT; if (colour >= 16 && colour < 256) tattr = (tattr &~ ATTR_FGMASK) | ATTR_DEFFG; colour = (tattr & ATTR_BGMASK) >> ATTR_BGSHIFT; if (colour >= 16 && colour < 256) tattr = (tattr &~ ATTR_BGMASK) | ATTR_DEFBG; |
︙ | ︙ | |||
4935 4936 4937 4938 4939 4940 4941 4942 4943 4944 4945 4946 4947 4948 4949 4950 4951 4952 4953 | tchar = newline[j].chr; if ((term->disptext[i]->chars[j].attr ^ tattr) & ATTR_WIDE) dirty_line = TRUE; break_run = ((tattr ^ attr) & term->attr_mask) != 0; /* Special hack for VT100 Linedraw glyphs */ if ((tchar >= 0x23BA && tchar <= 0x23BD) || (j > 0 && (newline[j-1].chr >= 0x23BA && newline[j-1].chr <= 0x23BD))) break_run = TRUE; /* * Separate out sequences of characters that have the * same CSET, if that CSET is a magic one. */ if (CSET_OF(tchar) != cset) break_run = TRUE; | > > | 5029 5030 5031 5032 5033 5034 5035 5036 5037 5038 5039 5040 5041 5042 5043 5044 5045 5046 5047 5048 5049 | tchar = newline[j].chr; if ((term->disptext[i]->chars[j].attr ^ tattr) & ATTR_WIDE) dirty_line = TRUE; break_run = ((tattr ^ attr) & term->attr_mask) != 0; #ifdef USES_VTLINE_HACK /* Special hack for VT100 Linedraw glyphs */ if ((tchar >= 0x23BA && tchar <= 0x23BD) || (j > 0 && (newline[j-1].chr >= 0x23BA && newline[j-1].chr <= 0x23BD))) break_run = TRUE; #endif /* * Separate out sequences of characters that have the * same CSET, if that CSET is a magic one. */ if (CSET_OF(tchar) != cset) break_run = TRUE; |
︙ | ︙ | |||
4987 4988 4989 4990 4991 4992 4993 | do_copy = FALSE; if (!termchars_equal_override(&term->disptext[i]->chars[j], d, tchar, tattr)) { do_copy = TRUE; dirty_run = TRUE; } | | > > > > > > > | 5083 5084 5085 5086 5087 5088 5089 5090 5091 5092 5093 5094 5095 5096 5097 5098 5099 5100 5101 5102 5103 5104 5105 5106 5107 | do_copy = FALSE; if (!termchars_equal_override(&term->disptext[i]->chars[j], d, tchar, tattr)) { do_copy = TRUE; dirty_run = TRUE; } if (ccount+2 > chlen) { chlen = ccount + 256; ch = sresize(ch, chlen, wchar_t); } #ifdef PLATFORM_IS_UTF16 if (tchar > 0x10000 && tchar < 0x110000) { ch[ccount++] = (wchar_t) HIGH_SURROGATE_OF(tchar); ch[ccount++] = (wchar_t) LOW_SURROGATE_OF(tchar); } else #endif /* PLATFORM_IS_UTF16 */ ch[ccount++] = (wchar_t) tchar; if (d->cc_next) { termchar *dd = d; while (dd->cc_next) { unsigned long schar; |
︙ | ︙ | |||
5014 5015 5016 5017 5018 5019 5020 | schar = term->ucsdata->unitab_xterm[schar & 0xFF]; break; case CSET_SCOACS: schar = term->ucsdata->unitab_scoacs[schar&0xFF]; break; } | | > > > > > > > | 5117 5118 5119 5120 5121 5122 5123 5124 5125 5126 5127 5128 5129 5130 5131 5132 5133 5134 5135 5136 5137 5138 5139 5140 5141 | schar = term->ucsdata->unitab_xterm[schar & 0xFF]; break; case CSET_SCOACS: schar = term->ucsdata->unitab_scoacs[schar&0xFF]; break; } if (ccount+2 > chlen) { chlen = ccount + 256; ch = sresize(ch, chlen, wchar_t); } #ifdef PLATFORM_IS_UTF16 if (schar > 0x10000 && schar < 0x110000) { ch[ccount++] = (wchar_t) HIGH_SURROGATE_OF(schar); ch[ccount++] = (wchar_t) LOW_SURROGATE_OF(schar); } else #endif /* PLATFORM_IS_UTF16 */ ch[ccount++] = (wchar_t) schar; } attr |= TATTR_COMBINING; } if (do_copy) { |
︙ | ︙ | |||
5263 5264 5265 5266 5267 5268 5269 | while (1) { int uc = ldata->chars[x].chr; attr = ldata->chars[x].attr; switch (uc & CSET_MASK) { case CSET_LINEDRW: | | | 5373 5374 5375 5376 5377 5378 5379 5380 5381 5382 5383 5384 5385 5386 5387 | while (1) { int uc = ldata->chars[x].chr; attr = ldata->chars[x].attr; switch (uc & CSET_MASK) { case CSET_LINEDRW: if (!term->rawcnp) { uc = term->ucsdata->unitab_xterm[uc & 0xFF]; break; } case CSET_ASCII: uc = term->ucsdata->unitab_line[uc & 0xFF]; break; case CSET_SCOACS: |
︙ | ︙ | |||
5517 5518 5519 5520 5521 5522 5523 | term->cols-1 : term->cols); if (p.x < maxcols-1) { if (wordtype(term, UCSGET(ldata->chars, p.x+1)) == wvalue) p.x++; else break; } else { | > | | 5627 5628 5629 5630 5631 5632 5633 5634 5635 5636 5637 5638 5639 5640 5641 5642 | term->cols-1 : term->cols); if (p.x < maxcols-1) { if (wordtype(term, UCSGET(ldata->chars, p.x+1)) == wvalue) p.x++; else break; } else { if (p.y+1 < term->rows && (ldata->lattr & LATTR_WRAPPED)) { termline *ldata2; ldata2 = lineptr(p.y+1); if (wordtype(term, UCSGET(ldata2->chars, 0)) == wvalue) { p.x = 0; p.y++; unlineptr(ldata); |
︙ | ︙ | |||
5587 5588 5589 5590 5591 5592 5593 5594 5595 5596 5597 5598 5599 5600 5601 5602 5603 5604 5605 5606 5607 | if (term->seltype == LEXICOGRAPHIC) { term->selstart = sel_spread_half(term, term->selstart, -1); decpos(term->selend); term->selend = sel_spread_half(term, term->selend, +1); incpos(term->selend); } } void term_do_paste(Terminal *term) { wchar_t *data; int len; get_clip(term->frontend, &data, &len); if (data && len > 0) { wchar_t *p, *q; term_seen_key_event(term); /* pasted data counts */ if (term->paste_buffer) sfree(term->paste_buffer); | > > > > > > > > > > > > > > > > > > > > > > > > > > > | | > > > > > | 5698 5699 5700 5701 5702 5703 5704 5705 5706 5707 5708 5709 5710 5711 5712 5713 5714 5715 5716 5717 5718 5719 5720 5721 5722 5723 5724 5725 5726 5727 5728 5729 5730 5731 5732 5733 5734 5735 5736 5737 5738 5739 5740 5741 5742 5743 5744 5745 5746 5747 5748 5749 5750 5751 5752 5753 5754 5755 5756 5757 5758 5759 | if (term->seltype == LEXICOGRAPHIC) { term->selstart = sel_spread_half(term, term->selstart, -1); decpos(term->selend); term->selend = sel_spread_half(term, term->selend, +1); incpos(term->selend); } } static void term_paste_callback(void *vterm) { Terminal *term = (Terminal *)vterm; if (term->paste_len == 0) return; while (term->paste_pos < term->paste_len) { int n = 0; while (n + term->paste_pos < term->paste_len) { if (term->paste_buffer[term->paste_pos + n++] == '\015') break; } if (term->ldisc) luni_send(term->ldisc, term->paste_buffer + term->paste_pos, n, 0); term->paste_pos += n; if (term->paste_pos < term->paste_len) { queue_toplevel_callback(term_paste_callback, term); return; } } sfree(term->paste_buffer); term->paste_buffer = NULL; term->paste_len = 0; } void term_do_paste(Terminal *term) { wchar_t *data; int len; get_clip(term->frontend, &data, &len); if (data && len > 0) { wchar_t *p, *q; term_seen_key_event(term); /* pasted data counts */ if (term->paste_buffer) sfree(term->paste_buffer); term->paste_pos = term->paste_len = 0; term->paste_buffer = snewn(len + 12, wchar_t); if (term->bracketed_paste) { memcpy(term->paste_buffer, L"\033[200~", 6 * sizeof(wchar_t)); term->paste_len += 6; } p = q = data; while (p < data + len) { while (p < data + len && !(p <= data + len - sel_nl_sz && !memcmp(p, sel_nl, sizeof(sel_nl)))) p++; |
︙ | ︙ | |||
5625 5626 5627 5628 5629 5630 5631 5632 5633 5634 5635 5636 5637 5638 5639 | if (p <= data + len - sel_nl_sz && !memcmp(p, sel_nl, sizeof(sel_nl))) { term->paste_buffer[term->paste_len++] = '\015'; p += sel_nl_sz; } q = p; } /* Assume a small paste will be OK in one go. */ if (term->paste_len < 256) { if (term->ldisc) luni_send(term->ldisc, term->paste_buffer, term->paste_len, 0); if (term->paste_buffer) sfree(term->paste_buffer); term->paste_buffer = 0; | > > > > > > | > > | | | 5768 5769 5770 5771 5772 5773 5774 5775 5776 5777 5778 5779 5780 5781 5782 5783 5784 5785 5786 5787 5788 5789 5790 5791 5792 5793 5794 5795 5796 5797 5798 5799 5800 5801 5802 5803 5804 5805 5806 5807 5808 5809 5810 5811 | if (p <= data + len - sel_nl_sz && !memcmp(p, sel_nl, sizeof(sel_nl))) { term->paste_buffer[term->paste_len++] = '\015'; p += sel_nl_sz; } q = p; } if (term->bracketed_paste) { memcpy(term->paste_buffer + term->paste_len, L"\033[201~", 6 * sizeof(wchar_t)); term->paste_len += 6; } /* Assume a small paste will be OK in one go. */ if (term->paste_len < 256) { if (term->ldisc) luni_send(term->ldisc, term->paste_buffer, term->paste_len, 0); if (term->paste_buffer) sfree(term->paste_buffer); term->paste_buffer = 0; term->paste_pos = term->paste_len = 0; } } get_clip(term->frontend, NULL, NULL); queue_toplevel_callback(term_paste_callback, term); } void term_mouse(Terminal *term, Mouse_Button braw, Mouse_Button bcooked, Mouse_Action a, int x, int y, int shift, int ctrl, int alt) { pos selpoint; termline *ldata; int raw_mouse = (term->xterm_mouse && !term->no_mouse_rep && !(term->mouse_override && shift)); int default_seltype; if (y < 0) { y = 0; if (a == MA_DRAG && !raw_mouse) term_scroll(term, 0, -1); } |
︙ | ︙ | |||
5697 5698 5699 5700 5701 5702 5703 | * This makes use of Shift for selection reliable, and avoids the * host seeing mouse releases for which they never saw corresponding * presses. */ if (raw_mouse && (term->selstate != ABOUT_TO) && (term->selstate != DRAGGING)) { int encstate = 0, r, c; | | > | | | | | > > | | | > > > > > > | > | | | 5848 5849 5850 5851 5852 5853 5854 5855 5856 5857 5858 5859 5860 5861 5862 5863 5864 5865 5866 5867 5868 5869 5870 5871 5872 5873 5874 5875 5876 5877 5878 5879 5880 5881 5882 5883 5884 5885 5886 5887 5888 5889 5890 5891 5892 5893 5894 5895 5896 5897 5898 5899 5900 5901 5902 5903 5904 5905 5906 5907 5908 5909 5910 5911 5912 5913 5914 5915 5916 5917 5918 5919 5920 5921 5922 5923 5924 5925 5926 5927 5928 | * This makes use of Shift for selection reliable, and avoids the * host seeing mouse releases for which they never saw corresponding * presses. */ if (raw_mouse && (term->selstate != ABOUT_TO) && (term->selstate != DRAGGING)) { int encstate = 0, r, c; char abuf[32]; int len = 0; if (term->ldisc) { switch (braw) { case MBT_LEFT: encstate = 0x00; /* left button down */ break; case MBT_MIDDLE: encstate = 0x01; break; case MBT_RIGHT: encstate = 0x02; break; case MBT_WHEEL_UP: encstate = 0x40; break; case MBT_WHEEL_DOWN: encstate = 0x41; break; default: break; /* placate gcc warning about enum use */ } switch (a) { case MA_DRAG: if (term->xterm_mouse == 1) return; encstate += 0x20; break; case MA_RELEASE: /* If multiple extensions are enabled, the xterm 1006 is used, so it's okay to check for only that */ if (!term->xterm_extended_mouse) encstate = 0x03; term->mouse_is_down = 0; break; case MA_CLICK: if (term->mouse_is_down == braw) return; term->mouse_is_down = braw; break; default: break; /* placate gcc warning about enum use */ } if (shift) encstate += 0x04; if (ctrl) encstate += 0x10; r = y + 1; c = x + 1; /* Check the extensions in decreasing order of preference. Encoding the release event above assumes that 1006 comes first. */ if (term->xterm_extended_mouse) { len = sprintf(abuf, "\033[<%d;%d;%d%c", encstate, c, r, a == MA_RELEASE ? 'm' : 'M'); } else if (term->urxvt_extended_mouse) { len = sprintf(abuf, "\033[%d;%d;%dM", encstate + 32, c, r); } else if (c <= 223 && r <= 223) { len = sprintf(abuf, "\033[M%c%c%c", encstate + 32, c + 32, r + 32); } ldisc_send(term->ldisc, abuf, len, 0); } return; } /* * Set the selection type (rectangular or normal) at the start * of a selection attempt, from the state of Alt. */ if (!alt ^ !term->rect_select) default_seltype = RECTANGULAR; else default_seltype = LEXICOGRAPHIC; if (term->selstate == NO_SELECTION) { term->seltype = default_seltype; } |
︙ | ︙ | |||
5863 5864 5865 5866 5867 5868 5869 5870 5871 5872 5873 5874 5875 5876 5877 5878 5879 | #if MULTICLICK_ONLY_EVENT || a == MA_2CLK || a == MA_3CLK #endif )) { request_paste(term->frontend); } term_update(term); } int format_arrow_key(char *buf, Terminal *term, int xkey, int ctrl) { char *p = buf; if (term->vt52_mode) p += sprintf((char *) p, "\x1B%c", xkey); else { | > > > > > > > | | 6024 6025 6026 6027 6028 6029 6030 6031 6032 6033 6034 6035 6036 6037 6038 6039 6040 6041 6042 6043 6044 6045 6046 6047 6048 6049 6050 6051 6052 6053 6054 6055 | #if MULTICLICK_ONLY_EVENT || a == MA_2CLK || a == MA_3CLK #endif )) { request_paste(term->frontend); } /* * Since terminal output is suppressed during drag-selects, we * should make sure to write any pending output if one has just * finished. */ if (term->selstate != DRAGGING) term_out(term); term_update(term); } int format_arrow_key(char *buf, Terminal *term, int xkey, int ctrl) { char *p = buf; if (term->vt52_mode) p += sprintf((char *) p, "\x1B%c", xkey); else { int app_flg = (term->app_cursor_keys && !term->no_applic_c); #if 0 /* * RDB: VT100 & VT102 manuals both state the app cursor * keys only work if the app keypad is on. * * SGT: That may well be true, but xterm disagrees and so * does at least one application, so I've #if'ed this out |
︙ | ︙ | |||
5902 5903 5904 5905 5906 5907 5908 | else p += sprintf((char *) p, "\x1B[%c", xkey); } return p - buf; } | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < > > > > > > > > | 6070 6071 6072 6073 6074 6075 6076 6077 6078 6079 6080 6081 6082 6083 6084 6085 6086 6087 6088 6089 6090 6091 6092 6093 6094 6095 6096 6097 6098 6099 6100 6101 6102 6103 6104 6105 6106 6107 6108 6109 6110 | else p += sprintf((char *) p, "\x1B[%c", xkey); } return p - buf; } void term_nopaste(Terminal *term) { if (term->paste_len == 0) return; sfree(term->paste_buffer); term->paste_buffer = NULL; term->paste_len = 0; } static void deselect(Terminal *term) { term->selstate = NO_SELECTION; term->selstart.x = term->selstart.y = term->selend.x = term->selend.y = 0; } void term_deselect(Terminal *term) { deselect(term); term_update(term); /* * Since terminal output is suppressed during drag-selects, we * should make sure to write any pending output if one has just * finished. */ if (term->selstate != DRAGGING) term_out(term); } int term_ldisc(Terminal *term, int option) { if (option == LD_ECHO) return term->term_echoing; if (option == LD_EDIT) |
︙ | ︙ | |||
6476 6477 6478 6479 6480 6481 6482 | * Provide "auto" settings for remote tty modes, suitable for an * application with a terminal window. */ char *term_get_ttymode(Terminal *term, const char *mode) { char *val = NULL; if (strcmp(mode, "ERASE") == 0) { | | | | 6184 6185 6186 6187 6188 6189 6190 6191 6192 6193 6194 6195 6196 6197 6198 6199 6200 | * Provide "auto" settings for remote tty modes, suitable for an * application with a terminal window. */ char *term_get_ttymode(Terminal *term, const char *mode) { char *val = NULL; if (strcmp(mode, "ERASE") == 0) { val = term->bksp_is_delete ? "^?" : "^H"; } /* FIXME: perhaps we should set ONLCR based on lfhascr as well? */ /* FIXME: or ECHO and friends based on local echo state? */ return dupstr(val); } struct term_userpass_state { size_t curr_prompt; int done_prompt; /* printed out prompt yet? */ |
︙ | ︙ | |||
6524 6525 6526 6527 6528 6529 6530 | } /* * Zero all the results, in case we abort half-way through. */ { int i; for (i = 0; i < (int)p->n_prompts; i++) | | | 6232 6233 6234 6235 6236 6237 6238 6239 6240 6241 6242 6243 6244 6245 6246 | } /* * Zero all the results, in case we abort half-way through. */ { int i; for (i = 0; i < (int)p->n_prompts; i++) prompt_set_result(p->prompts[i], ""); } } while (s->curr_prompt < p->n_prompts) { prompt_t *pr = p->prompts[s->curr_prompt]; int finished_prompt = 0; |
︙ | ︙ | |||
6551 6552 6553 6554 6555 6556 6557 6558 | while (!finished_prompt && inlen) { char c = *in++; inlen--; switch (c) { case 10: case 13: term_data(term, 0, "\r\n", 2); pr->result[s->pos] = '\0'; | > < | 6259 6260 6261 6262 6263 6264 6265 6266 6267 6268 6269 6270 6271 6272 6273 6274 | while (!finished_prompt && inlen) { char c = *in++; inlen--; switch (c) { case 10: case 13: term_data(term, 0, "\r\n", 2); prompt_ensure_result_size(pr, s->pos + 1); pr->result[s->pos] = '\0'; /* go to next prompt, if any */ s->curr_prompt++; s->done_prompt = 0; finished_prompt = 1; /* break out */ break; case 8: case 127: |
︙ | ︙ | |||
6587 6588 6589 6590 6591 6592 6593 | return 0; /* user abort */ default: /* * This simplistic check for printability is disabled * when we're doing password input, because some people * have control characters in their passwords. */ | < | | | | 6295 6296 6297 6298 6299 6300 6301 6302 6303 6304 6305 6306 6307 6308 6309 6310 6311 | return 0; /* user abort */ default: /* * This simplistic check for printability is disabled * when we're doing password input, because some people * have control characters in their passwords. */ if (!pr->echo || (c >= ' ' && c <= '~') || ((unsigned char) c >= 160)) { prompt_ensure_result_size(pr, s->pos + 1); pr->result[s->pos++] = c; if (pr->echo) term_data(term, 0, &c, 1); } break; } } |
︙ | ︙ |
Changes to terminal.h.
︙ | ︙ | |||
148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 | long vbell_end; int app_cursor_keys, app_keypad_keys, vt52_mode; int repeat_off, cr_lf_return; int seen_disp_event; int big_cursor; int xterm_mouse; /* send mouse messages to host */ int mouse_is_down; /* used while tracking mouse buttons */ int cset_attr[2]; /* * Saved settings on the alternate screen. */ int alt_x, alt_y, alt_om, alt_wrap, alt_wnext, alt_ins; | > > > > | 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 | long vbell_end; int app_cursor_keys, app_keypad_keys, vt52_mode; int repeat_off, cr_lf_return; int seen_disp_event; int big_cursor; int xterm_mouse; /* send mouse messages to host */ int xterm_extended_mouse; int urxvt_extended_mouse; int mouse_is_down; /* used while tracking mouse buttons */ int bracketed_paste; int cset_attr[2]; /* * Saved settings on the alternate screen. */ int alt_x, alt_y, alt_om, alt_wrap, alt_wnext, alt_ins; |
︙ | ︙ | |||
214 215 216 217 218 219 220 | short wordness[256]; /* Mask of attributes to pay attention to when painting. */ int attr_mask; wchar_t *paste_buffer; | | < | | | | | | | 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 | short wordness[256]; /* Mask of attributes to pay attention to when painting. */ int attr_mask; wchar_t *paste_buffer; int paste_len, paste_pos; void (*resize_fn)(void *, int, int); void *resize_ctx; void *ldisc; void *frontend; void *logctx; struct unicode_data *ucsdata; /* * We maintain a full copy of a Conf here, not merely a pointer * to it. That way, when we're passed a new one for * reconfiguration, we can check the differences and adjust the * _current_ setting of (e.g.) auto wrap mode rather than only * the default. */ Conf *conf; /* * from_backend calls term_out, but it can also be called from * the ldisc if the ldisc is called _within_ term_out. So we * have to guard against re-entrancy - if from_backend is * called recursively like this, it will simply add data to the * end of the buffer term_out is in the process of working |
︙ | ︙ | |||
269 270 271 272 273 274 275 276 277 278 279 280 | */ termchar *ltemp; int ltemp_size; bidi_char *wcFrom, *wcTo; int wcFromTo_size; struct bidi_cache_entry *pre_bidi_cache, *post_bidi_cache; int bidi_cache_size; }; #define in_utf(term) ((term)->utf || (term)->ucsdata->line_codepage==CP_UTF8) #endif | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 | */ termchar *ltemp; int ltemp_size; bidi_char *wcFrom, *wcTo; int wcFromTo_size; struct bidi_cache_entry *pre_bidi_cache, *post_bidi_cache; int bidi_cache_size; /* * We copy a bunch of stuff out of the Conf structure into local * fields in the Terminal structure, to avoid the repeated * tree234 lookups which would be involved in fetching them from * the former every time. */ int ansi_colour; char *answerback; int answerbacklen; int arabicshaping; int beep; int bellovl; int bellovl_n; int bellovl_s; int bellovl_t; int bidi; int bksp_is_delete; int blink_cur; int blinktext; int cjk_ambig_wide; int conf_height; int conf_width; int crhaslf; int erase_to_scrollback; int funky_type; int lfhascr; int logflush; int logtype; int mouse_override; int nethack_keypad; int no_alt_screen; int no_applic_c; int no_applic_k; int no_dbackspace; int no_mouse_rep; int no_remote_charset; int no_remote_resize; int no_remote_wintitle; int rawcnp; int rect_select; int remote_qtitle_action; int rxvt_homeend; int scroll_on_disp; int scroll_on_key; int xterm_256_colour; }; #define in_utf(term) ((term)->utf || (term)->ucsdata->line_codepage==CP_UTF8) #endif |
Changes to testback.c.
|
| | | 1 2 3 4 5 6 7 8 | /* $Id: testback.c 9214 2011-07-14 18:52:21Z simon $ */ /* * Copyright (c) 1999 Simon Tatham * Copyright (c) 1999 Ben Harris * All rights reserved. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation |
︙ | ︙ | |||
29 30 31 32 33 34 35 | /* PuTTY test backends */ #include <stdio.h> #include <stdlib.h> #include "putty.h" | | | | | 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | /* PuTTY test backends */ #include <stdio.h> #include <stdlib.h> #include "putty.h" static const char *null_init(void *, void **, Conf *, char *, int, char **, int, int); static const char *loop_init(void *, void **, Conf *, char *, int, char **, int, int); static void null_free(void *); static void loop_free(void *); static void null_reconfig(void *, Conf *); static int null_send(void *, char *, int); static int loop_send(void *, char *, int); 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 int null_connected(void *); |
︙ | ︙ | |||
70 71 72 73 74 75 76 | }; struct loop_state { Terminal *term; }; static const char *null_init(void *frontend_handle, void **backend_handle, | | | | | 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 | }; struct loop_state { Terminal *term; }; static const char *null_init(void *frontend_handle, void **backend_handle, Conf *conf, char *host, int port, char **realhost, int nodelay, int keepalive) { return NULL; } static const char *loop_init(void *frontend_handle, void **backend_handle, Conf *conf, char *host, int port, char **realhost, int nodelay, int keepalive) { struct loop_state *st = snew(struct loop_state); st->term = frontend_handle; *backend_handle = st; return NULL; } static void null_free(void *handle) { } static void loop_free(void *handle) { sfree(handle); } static void null_reconfig(void *handle, Conf *conf) { } static int null_send(void *handle, char *buf, int len) { return 0; } |
︙ | ︙ |
Changes to testdata/bignum.py.
︙ | ︙ | |||
99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 | # carry to the very top of the number. for i in range(1,4200): a, b, p = findprod((1<<i)+1, +1, (i, i*i+1)) print "mul", hexstr(a), hexstr(b), hexstr(p) a, b, p = findprod((1<<i)+1, +1, (i, i+1)) print "mul", hexstr(a), hexstr(b), hexstr(p) # Simple tests of modpow. for i in range(64, 4097, 63): modulus = sqrt(1<<(2*i-1)) | 1 base = sqrt(3*modulus*modulus) % modulus expt = sqrt(modulus*modulus*2/5) print "pow", hexstr(base), hexstr(expt), hexstr(modulus), hexstr(pow(base, expt, modulus)) if i <= 1024: # Test even moduli, which can't be done by Montgomery. modulus = modulus - 1 print "pow", hexstr(base), hexstr(expt), hexstr(modulus), hexstr(pow(base, expt, modulus)) | > > > > > > > > > > | 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 | # carry to the very top of the number. for i in range(1,4200): a, b, p = findprod((1<<i)+1, +1, (i, i*i+1)) print "mul", hexstr(a), hexstr(b), hexstr(p) a, b, p = findprod((1<<i)+1, +1, (i, i+1)) print "mul", hexstr(a), hexstr(b), hexstr(p) # Simple tests of modmul. for ai in range(20, 200, 60): a = sqrt(3<<(2*ai-1)) for bi in range(20, 200, 60): b = sqrt(5<<(2*bi-1)) for m in range(20, 600, 32): m = sqrt(2**(m+1)) print "modmul", hexstr(a), hexstr(b), hexstr(m), hexstr((a*b) % m) # Simple tests of modpow. for i in range(64, 4097, 63): modulus = sqrt(1<<(2*i-1)) | 1 base = sqrt(3*modulus*modulus) % modulus expt = sqrt(modulus*modulus*2/5) print "pow", hexstr(base), hexstr(expt), hexstr(modulus), hexstr(pow(base, expt, modulus)) if i <= 1024: # Test even moduli, which can't be done by Montgomery. modulus = modulus - 1 print "pow", hexstr(base), hexstr(expt), hexstr(modulus), hexstr(pow(base, expt, modulus)) print "pow", hexstr(i), hexstr(expt), hexstr(modulus), hexstr(pow(i, expt, modulus)) |
Changes to timing.c.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | /* * timing.c * * This module tracks any timers set up by schedule_timer(). It * keeps all the currently active timers in a list; it informs the * front end of when the next timer is due to go off if that * changes; and, very importantly, it tracks the context pointers * passed to schedule_timer(), so that if a context is freed all * the timers associated with it can be immediately annulled. */ #include <assert.h> #include <stdio.h> #include "putty.h" #include "tree234.h" struct timer { timer_fn_t fn; void *ctx; | > > > > > > > > > > > > > > > > > > | > | | | | < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 | /* * timing.c * * This module tracks any timers set up by schedule_timer(). It * keeps all the currently active timers in a list; it informs the * front end of when the next timer is due to go off if that * changes; and, very importantly, it tracks the context pointers * passed to schedule_timer(), so that if a context is freed all * the timers associated with it can be immediately annulled. * * * The problem is that computer clocks aren't perfectly accurate. * The GETTICKCOUNT function returns a 32bit number that normally * increases by about 1000 every second. On windows this uses the PC's * interrupt timer and so is only accurate to around 20ppm. On unix it's * a value that's calculated from the current UTC time and so is in theory * accurate in the long term but may jitter and jump in the short term. * * What PuTTY needs from these timers is simply a way of delaying the * calling of a function for a little while, if it's occasionally called a * little early or late that's not a problem. So to protect against clock * jumps schedule_timer records the time that it was called in the timer * structure. With this information the run_timers function can see when * the current GETTICKCOUNT value is after the time the event should be * fired OR before the time it was set. In the latter case the clock must * have jumped, the former is (probably) just the normal passage of time. * */ #include <assert.h> #include <stdio.h> #include "putty.h" #include "tree234.h" struct timer { timer_fn_t fn; void *ctx; unsigned long now; unsigned long when_set; }; static tree234 *timers = NULL; static tree234 *timer_contexts = NULL; static unsigned long now = 0L; static int compare_timers(void *av, void *bv) { struct timer *a = (struct timer *)av; struct timer *b = (struct timer *)bv; long at = a->now - now; long bt = b->now - now; if (at < bt) return -1; else if (at > bt) return +1; /* * Failing that, compare on the other two fields, just so that * we don't get unwanted equality. */ #if defined(__LCC__) || defined(__clang__) /* lcc won't let us compare function pointers. Legal, but annoying. */ { int c = memcmp(&a->fn, &b->fn, sizeof(a->fn)); if (c) return c; } #else if (a->fn < b->fn) return -1; else if (a->fn > b->fn) return +1; #endif |
︙ | ︙ | |||
85 86 87 88 89 90 91 | if (!timers) { timers = newtree234(compare_timers); timer_contexts = newtree234(compare_timer_contexts); now = GETTICKCOUNT(); } } | | | > | > | 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 | if (!timers) { timers = newtree234(compare_timers); timer_contexts = newtree234(compare_timer_contexts); now = GETTICKCOUNT(); } } unsigned long schedule_timer(int ticks, timer_fn_t fn, void *ctx) { unsigned long when; struct timer *t, *first; init_timers(); now = GETTICKCOUNT(); when = ticks + now; /* * Just in case our various defences against timing skew fail * us: if we try to schedule a timer that's already in the * past, we instead schedule it for the immediate future. */ if (when - now <= 0) when = now + 1; t = snew(struct timer); t->fn = fn; t->ctx = ctx; t->now = when; t->when_set = now; if (t != add234(timers, t)) { sfree(t); /* identical timer already exists */ } else { add234(timer_contexts, t->ctx);/* don't care if this fails */ } |
︙ | ︙ | |||
130 131 132 133 134 135 136 | } /* * Call to run any timers whose time has reached the present. * Returns the time (in ticks) expected until the next timer after * that triggers. */ | | < < < < < < < < < < | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < > | | 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 | } /* * Call to run any timers whose time has reached the present. * Returns the time (in ticks) expected until the next timer after * that triggers. */ int run_timers(unsigned long anow, unsigned long *next) { struct timer *first; init_timers(); now = GETTICKCOUNT(); while (1) { first = (struct timer *)index234(timers, 0); if (!first) return FALSE; /* no timers remaining */ if (find234(timer_contexts, first->ctx, NULL) == NULL) { /* * This timer belongs to a context that has been * expired. Delete it without running. */ delpos234(timers, 0); sfree(first); } else if (now - (first->when_set - 10) > first->now - (first->when_set - 10)) { /* * This timer is active and has reached its running * time. Run it. */ delpos234(timers, 0); first->fn(first->ctx, first->now); sfree(first); |
︙ | ︙ |
Changes to tree234.c.
︙ | ︙ | |||
25 26 27 28 29 30 31 | * SOFTWARE. */ #include <stdio.h> #include <stdlib.h> #include <assert.h> | < > > > > > > > | 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | * SOFTWARE. */ #include <stdio.h> #include <stdlib.h> #include <assert.h> #include "tree234.h" #ifdef TEST #define LOG(x) (printf x) #define snew(type) ((type *)malloc(sizeof(type))) #define snewn(n, type) ((type *)malloc((n) * sizeof(type))) #define sresize(ptr, n, type) \ ((type *)realloc(sizeof((type *)0 == (ptr)) ? (ptr) : (ptr), \ (n) * sizeof(type))) #define sfree(ptr) free(ptr) #else #include "puttymem.h" #define LOG(x) #endif typedef struct node234_Tag node234; struct tree234_Tag { node234 *root; |
︙ | ︙ | |||
216 217 218 219 220 221 222 | LOG((" at %p: %p/%d [%p] %p/%d [%p] %p/%d [%p] %p/%d\n", n, n->kids[0], n->counts[0], n->elems[0], n->kids[1], n->counts[1], n->elems[1], n->kids[2], n->counts[2], n->elems[2], n->kids[3], n->counts[3])); LOG((" need to insert %p/%d [%p] %p/%d at position %d\n", | | | 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 | LOG((" at %p: %p/%d [%p] %p/%d [%p] %p/%d [%p] %p/%d\n", n, n->kids[0], n->counts[0], n->elems[0], n->kids[1], n->counts[1], n->elems[1], n->kids[2], n->counts[2], n->elems[2], n->kids[3], n->counts[3])); LOG((" need to insert %p/%d [%p] %p/%d at position %d\n", left, lcount, e, right, rcount, (int)(np - n->kids))); if (n->elems[1] == NULL) { /* * Insert in a 2-node; simple. */ if (np == &n->kids[0]) { LOG((" inserting on left of 2-node\n")); n->kids[2] = n->kids[1]; |
︙ | ︙ | |||
1465 1466 1467 1468 1469 1470 1471 | printf("adding string %s at index %d\n", strings[j], k); addpostest(strings[j], k); } while (count234(tree) > 0) { printf("cleanup: tree size %d\n", count234(tree)); j = randomnumber(&seed); j %= count234(tree); | | > | 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 | printf("adding string %s at index %d\n", strings[j], k); addpostest(strings[j], k); } while (count234(tree) > 0) { printf("cleanup: tree size %d\n", count234(tree)); j = randomnumber(&seed); j %= count234(tree); printf("deleting string %s from index %d\n", (const char *)array[j], j); delpostest(j); } return 0; } #endif |
Changes to unix/configure.ac.
1 2 3 4 5 6 | # To compile this into a configure script, you need: # * Autoconf 2.50 or newer # * Gtk (for $prefix/share/aclocal/gtk.m4) # * Automake (for aclocal) # If you've got them, running "autoreconf" should work. | > > > | > | > | | > > | < < > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > | > | > > > > > | > > | | | > > > > > > > > > | | > > > > > > < | > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 | # To compile this into a configure script, you need: # * Autoconf 2.50 or newer # * Gtk (for $prefix/share/aclocal/gtk.m4) # * Automake (for aclocal) # If you've got them, running "autoreconf" should work. # Version number is substituted by Buildscr for releases, snapshots # and custom builds out of svn; X.XX shows up in ad-hoc developer # builds, which shouldn't matter AC_INIT(putty, X.XX) AC_CONFIG_FILES([Makefile]) AC_CONFIG_HEADERS([uxconfig.h:uxconfig.in]) AM_INIT_AUTOMAKE([-Wall -Werror foreign]) AC_PROG_INSTALL AC_PROG_RANLIB ifdef([AM_PROG_AR],[AM_PROG_AR]) AM_PROG_CC_C_O # Mild abuse of the '--enable' option format to allow manual # specification of setuid or setgid setup in pterm. setidtype=none AC_ARG_ENABLE([setuid], [AS_HELP_STRING([--enable-setuid=USER], [make pterm setuid to a given user])], [case "$enableval" in no) setidtype=none;; *) setidtype=setuid; setidval="$enableval";; esac]) AC_ARG_ENABLE([setgid], [AS_HELP_STRING([--enable-setgid=GROUP], [make pterm setgid to a given group])], [case "$enableval" in no) setidtype=none;; *) setidtype=setgid; setidval="$enableval";; esac]) AM_CONDITIONAL(HAVE_SETID_CMD, [test "$setidtype" != "none"]) AS_IF([test "x$setidtype" = "xsetuid"], [SETID_CMD="chown $setidval"; SETID_MODE="4755"]) AS_IF([test "x$setidtype" = "xsetgid"], [SETID_CMD="chgrp $setidval"; SETID_MODE="2755"]) AC_SUBST(SETID_CMD) AC_SUBST(SETID_MODE) AC_ARG_WITH([gssapi], [AS_HELP_STRING([--without-gssapi], [disable GSSAPI support])], [], [with_gssapi=yes]) WITH_GSSAPI= AS_IF([test "x$with_gssapi" != xno], [AC_DEFINE([WITH_GSSAPI], [1], [Define if building with GSSAPI support.])]) AC_ARG_WITH([gtk], [AS_HELP_STRING([--with-gtk=VER], [specify GTK version to use (`1' or `2')]) AS_HELP_STRING([--without-gtk], [do not use GTK (build command-line tools only)])], [gtk_version_desired="$withval"], [gtk_version_desired="any"]) case "$gtk_version_desired" in 1 | 2 | any | no) ;; yes) gtk_version_desired="any" ;; *) AC_ERROR([Invalid GTK version specified]) esac AC_CHECK_HEADERS([utmpx.h sys/select.h],,,[ #include <sys/types.h> #include <utmp.h>]) # Look for both GTK 2 and GTK 1, in descending order of preference. If # we can't find either, have the makefile only build the CLI programs. gtk=none case "$gtk_version_desired:$gtk" in 2:none | any:none) ifdef([AM_PATH_GTK_2_0],[ AM_PATH_GTK_2_0([2.0.0], [gtk=2], []) ],[AC_WARNING([generating configure script without GTK 2 autodetection])]) ;; esac case "$gtk_version_desired:$gtk" in 1:none | any:none) ifdef([AM_PATH_GTK],[ AM_PATH_GTK([1.2.0], [gtk=1], []) ],[ # manual check for gtk1 AC_PATH_PROG(GTK1_CONFIG, gtk-config, absent) if test "$GTK1_CONFIG" != "absent"; then GTK_CFLAGS=`"$GTK1_CONFIG" --cflags` GTK_LIBS=`"$GTK1_CONFIG" --libs` gtk=1 fi ]) ;; esac AM_CONDITIONAL(HAVE_GTK, [test "$gtk" != "none"]) if test "$gtk" = "2"; then ac_save_CFLAGS="$CFLAGS" ac_save_LIBS="$LIBS" CFLAGS="$CFLAGS $GTK_CFLAGS" LIBS="$GTK_LIBS $LIBS" AC_CHECK_FUNCS([pango_font_family_is_monospace pango_font_map_list_families]) CFLAGS="$ac_save_CFLAGS" LIBS="$ac_save_LIBS" fi AC_SEARCH_LIBS([socket], [xnet]) AS_IF([test "x$with_gssapi" != xno], [AC_SEARCH_LIBS( [dlopen],[dl], [], [AC_DEFINE([NO_LIBDL], [1], [Define if we could not find libdl.]) AC_CHECK_HEADERS([gssapi/gssapi.h]) AC_SEARCH_LIBS( [gss_init_sec_context],[gssapi gssapi_krb5 gss], [], [AC_DEFINE([NO_GSSAPI_LIB], [1], [Define if we could not find a gssapi library])])])]) AC_CHECK_LIB(X11, XOpenDisplay, [GTK_LIBS="-lX11 $GTK_LIBS" AC_DEFINE([HAVE_LIBX11],[],[Define if libX11.a is available])]) AC_CHECK_FUNCS([getaddrinfo posix_openpt ptsname setresuid strsignal updwtmpx]) AC_CHECK_DECLS([CLOCK_MONOTONIC], [], [], [[#include <time.h>]]) AC_SEARCH_LIBS([clock_gettime], [rt], [AC_DEFINE([HAVE_CLOCK_GETTIME],[],[Define if clock_gettime() is available])]) if test "x$GCC" = "xyes"; then : AC_SUBST(WARNINGOPTS, ['-Wall -Werror']) else : AC_SUBST(WARNINGOPTS, []) fi AC_OUTPUT if test "$gtk_version_desired" = "no"; then cat <<EOF 'configure' was instructed not to build using GTK. Therefore, PuTTY itself and the other GUI utilities will not be built by the generated Makefile: only the command-line tools such as puttygen, plink and psftp will be built. EOF elif test "$gtk" = "none"; then cat <<EOF 'configure' was unable to find either the GTK 1 or GTK 2 libraries on your system. Therefore, PuTTY itself and the other GUI utilities will not be built by the generated Makefile: only the command-line tools such as puttygen, plink and psftp will be built. EOF fi AH_BOTTOM([ /* Convert autoconf definitions to ones that PuTTY wants. */ #ifndef HAVE_GETADDRINFO # define NO_IPV6 #endif |
︙ | ︙ |
Changes to unix/gtkcfg.c.
︙ | ︙ | |||
38 39 40 41 42 43 44 | * GTK makes it rather easier to put the scrollbar on the left * than Windows does! */ s = ctrl_getset(b, "Window", "scrollback", "Control the scrollback in the window"); ctrl_checkbox(s, "Scrollbar on left", 'l', HELPCTX(no_help), | | | | | 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 | * GTK makes it rather easier to put the scrollbar on the left * than Windows does! */ s = ctrl_getset(b, "Window", "scrollback", "Control the scrollback in the window"); ctrl_checkbox(s, "Scrollbar on left", 'l', HELPCTX(no_help), conf_checkbox_handler, I(CONF_scrollbar_on_left)); /* * Really this wants to go just after `Display scrollbar'. See * if we can find that control, and do some shuffling. */ for (i = 0; i < s->ncontrols; i++) { c = s->ctrls[i]; if (c->generic.type == CTRL_CHECKBOX && c->generic.context.i == CONF_scrollbar) { /* * Control i is the scrollbar checkbox. * Control s->ncontrols-1 is the scrollbar-on-left one. */ if (i < s->ncontrols-2) { c = s->ctrls[s->ncontrols-1]; memmove(s->ctrls+i+2, s->ctrls+i+1, |
︙ | ︙ | |||
85 86 87 88 89 90 91 | } } ctrl_settitle(b, "Window/Fonts", "Options controlling font usage"); s = ctrl_getset(b, "Window/Fonts", "font", "Fonts for displaying non-bold text"); ctrl_fontsel(s, "Font used for ordinary text", 'f', HELPCTX(no_help), | | | | | | | | | | | | | < | 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 | } } ctrl_settitle(b, "Window/Fonts", "Options controlling font usage"); s = ctrl_getset(b, "Window/Fonts", "font", "Fonts for displaying non-bold text"); ctrl_fontsel(s, "Font used for ordinary text", 'f', HELPCTX(no_help), conf_fontsel_handler, I(CONF_font)); ctrl_fontsel(s, "Font used for wide (CJK) text", 'w', HELPCTX(no_help), conf_fontsel_handler, I(CONF_widefont)); s = ctrl_getset(b, "Window/Fonts", "fontbold", "Fonts for displaying bolded text"); ctrl_fontsel(s, "Font used for bolded text", 'b', HELPCTX(no_help), conf_fontsel_handler, I(CONF_boldfont)); ctrl_fontsel(s, "Font used for bold wide text", 'i', HELPCTX(no_help), conf_fontsel_handler, I(CONF_wideboldfont)); ctrl_checkbox(s, "Use shadow bold instead of bold fonts", 'u', HELPCTX(no_help), conf_checkbox_handler, I(CONF_shadowbold)); ctrl_text(s, "(Note that bold fonts or shadow bolding are only" " used if you have not requested bolding to be done by" " changing the text colour.)", HELPCTX(no_help)); ctrl_editbox(s, "Horizontal offset for shadow bold:", 'z', 20, HELPCTX(no_help), conf_editbox_handler, I(CONF_shadowboldoffset), I(-1)); /* * Markus Kuhn feels, not totally unreasonably, that it's good * for all applications to shift into UTF-8 mode if they notice * that they've been started with a LANG setting dictating it, * so that people don't have to keep remembering a separate * UTF-8 option for every application they use. Therefore, * here's an override option in the Translation panel. */ s = ctrl_getset(b, "Window/Translation", "trans", "Character set translation on received data"); ctrl_checkbox(s, "Override with UTF-8 if locale says so", 'l', HELPCTX(translation_utf8_override), conf_checkbox_handler, I(CONF_utf8_override)); if (!midsession) { /* * Allow the user to specify the window class as part of the saved * configuration, so that they can have their window manager treat * different kinds of PuTTY and pterm differently if they want to. */ s = ctrl_getset(b, "Window/Behaviour", "x11", "X Window System settings"); ctrl_editbox(s, "Window class name:", 'z', 50, HELPCTX(no_help), conf_editbox_handler, I(CONF_winclass), I(1)); } } |
Changes to unix/gtkcols.h.
1 2 3 4 5 6 7 8 9 10 | /* * gtkcols.h - header file for a columns-based widget container * capable of supporting the PuTTY portable dialog box layout * mechanism. */ #ifndef COLUMNS_H #define COLUMNS_H #include <gdk/gdk.h> | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | /* * gtkcols.h - header file for a columns-based widget container * capable of supporting the PuTTY portable dialog box layout * mechanism. */ #ifndef COLUMNS_H #define COLUMNS_H #include <gdk/gdk.h> #include <gtk/gtk.h> #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ #define TYPE_COLUMNS (columns_get_type()) #define COLUMNS(obj) (GTK_CHECK_CAST((obj), TYPE_COLUMNS, Columns)) |
︙ | ︙ |
Changes to unix/gtkdlg.c.
︙ | ︙ | |||
326 327 328 329 330 331 332 | * operations: first delete the previous text leaving the empty * string, then insert the new text. This causes two calls to * the "changed" signal. * * The first call to "changed", if allowed to proceed normally, * will cause an EVENT_VALCHANGE event on the edit box, causing * a call to dlg_editbox_get() which will read the empty string | | | | | < | < | < < | | < < < | < < < | 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 | * operations: first delete the previous text leaving the empty * string, then insert the new text. This causes two calls to * the "changed" signal. * * The first call to "changed", if allowed to proceed normally, * will cause an EVENT_VALCHANGE event on the edit box, causing * a call to dlg_editbox_get() which will read the empty string * out of the GtkEntry - and promptly write it straight into the * Conf structure, which is precisely where our `text' pointer * is probably pointing, so the second editing operation will * insert that instead of the string we originally asked for. * * Hence, we must take our own copy of the text before we do * this. */ tmpstring = dupstr(text); gtk_entry_set_text(GTK_ENTRY(entry), tmpstring); sfree(tmpstring); } char *dlg_editbox_get(union control *ctrl, void *dlg) { struct dlgparam *dp = (struct dlgparam *)dlg; struct uctrl *uc = dlg_find_byctrl(dp, ctrl); assert(uc->ctrl->generic.type == CTRL_EDITBOX); #if GTK_CHECK_VERSION(2,4,0) if (uc->combo) { #if GTK_CHECK_VERSION(2,6,0) return dupstr(gtk_combo_box_get_active_text(GTK_COMBO_BOX(uc->combo))); #else return dupstr(gtk_entry_get_text (GTK_ENTRY(gtk_bin_get_child(GTK_BIN(uc->combo))))); #endif } #endif if (uc->entry) { return dupstr(gtk_entry_get_text(GTK_ENTRY(uc->entry))); } assert(!"We shouldn't get here"); } #if !GTK_CHECK_VERSION(2,4,0) static void container_remove_and_destroy(GtkWidget *w, gpointer data) |
︙ | ︙ | |||
912 913 914 915 916 917 918 | break; default: assert(!"This shouldn't happen"); break; } } | | > > > | > | | < < | > > > | > | | < < | 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 | break; default: assert(!"This shouldn't happen"); break; } } void dlg_filesel_set(union control *ctrl, void *dlg, Filename *fn) { struct dlgparam *dp = (struct dlgparam *)dlg; struct uctrl *uc = dlg_find_byctrl(dp, ctrl); /* We must copy fn->path before passing it to gtk_entry_set_text. * See comment in dlg_editbox_set() for the reasons. */ char *duppath = dupstr(fn->path); assert(uc->ctrl->generic.type == CTRL_FILESELECT); assert(uc->entry != NULL); gtk_entry_set_text(GTK_ENTRY(uc->entry), duppath); sfree(duppath); } Filename *dlg_filesel_get(union control *ctrl, void *dlg) { struct dlgparam *dp = (struct dlgparam *)dlg; struct uctrl *uc = dlg_find_byctrl(dp, ctrl); assert(uc->ctrl->generic.type == CTRL_FILESELECT); assert(uc->entry != NULL); return filename_from_str(gtk_entry_get_text(GTK_ENTRY(uc->entry))); } void dlg_fontsel_set(union control *ctrl, void *dlg, FontSpec *fs) { struct dlgparam *dp = (struct dlgparam *)dlg; struct uctrl *uc = dlg_find_byctrl(dp, ctrl); /* We must copy fs->name before passing it to gtk_entry_set_text. * See comment in dlg_editbox_set() for the reasons. */ char *dupname = dupstr(fs->name); assert(uc->ctrl->generic.type == CTRL_FONTSELECT); assert(uc->entry != NULL); gtk_entry_set_text(GTK_ENTRY(uc->entry), dupname); sfree(dupname); } FontSpec *dlg_fontsel_get(union control *ctrl, void *dlg) { struct dlgparam *dp = (struct dlgparam *)dlg; struct uctrl *uc = dlg_find_byctrl(dp, ctrl); assert(uc->ctrl->generic.type == CTRL_FONTSELECT); assert(uc->entry != NULL); return fontspec_new(gtk_entry_get_text(GTK_ENTRY(uc->entry))); } /* * Bracketing a large set of updates in these two functions will * cause the front end (if possible) to delay updating the screen * until it's all complete, thus avoiding flicker. */ |
︙ | ︙ | |||
2822 2823 2824 2825 2826 2827 2828 | gtk_box_pack_end(GTK_BOX(dlg->vbox), w, FALSE, TRUE, 0); gtk_widget_show(w); gtk_widget_hide(dlg->action_area); gtk_dialog_set_has_separator(dlg, FALSE); #endif } | | | | 2816 2817 2818 2819 2820 2821 2822 2823 2824 2825 2826 2827 2828 2829 2830 2831 2832 2833 2834 2835 | gtk_box_pack_end(GTK_BOX(dlg->vbox), w, FALSE, TRUE, 0); gtk_widget_show(w); gtk_widget_hide(dlg->action_area); gtk_dialog_set_has_separator(dlg, FALSE); #endif } int do_config_box(const char *title, Conf *conf, int midsession, int protcfginfo) { GtkWidget *window, *hbox, *vbox, *cols, *label, *tree, *treescroll, *panels, *panelvbox; int index, level, protocol; struct controlbox *ctrlbox; char *path; #if GTK_CHECK_VERSION(2,0,0) GtkTreeStore *treestore; GtkCellRenderer *treerenderer; GtkTreeViewColumn *treecolumn; GtkTreeSelection *treeselection; |
︙ | ︙ | |||
2855 2856 2857 2858 2859 2860 2861 | for (index = 0; index < lenof(scs.sc); index++) { scs.sc[index].action = SHORTCUT_EMPTY; } window = gtk_dialog_new(); ctrlbox = ctrl_new_box(); | > | | | 2849 2850 2851 2852 2853 2854 2855 2856 2857 2858 2859 2860 2861 2862 2863 2864 2865 | for (index = 0; index < lenof(scs.sc); index++) { scs.sc[index].action = SHORTCUT_EMPTY; } window = gtk_dialog_new(); ctrlbox = ctrl_new_box(); protocol = conf_get_int(conf, CONF_protocol); setup_config_box(ctrlbox, midsession, protocol, protcfginfo); unix_setup_config_box(ctrlbox, midsession, protocol); gtk_setup_config_box(ctrlbox, midsession, window); gtk_window_set_title(GTK_WINDOW(window), title); hbox = gtk_hbox_new(FALSE, 4); gtk_box_pack_start(GTK_BOX(GTK_DIALOG(window)->vbox), hbox, TRUE, TRUE, 0); gtk_container_set_border_width(GTK_CONTAINER(hbox), 10); gtk_widget_show(hbox); |
︙ | ︙ | |||
3091 3092 3093 3094 3095 3096 3097 | gtk_signal_connect(GTK_OBJECT(selparams[index].treeitem), "select", GTK_SIGNAL_FUNC(treeitem_sel), &selparams[index]); dp.treeitems[index] = selparams[index].treeitem; } #endif | | | 3086 3087 3088 3089 3090 3091 3092 3093 3094 3095 3096 3097 3098 3099 3100 | gtk_signal_connect(GTK_OBJECT(selparams[index].treeitem), "select", GTK_SIGNAL_FUNC(treeitem_sel), &selparams[index]); dp.treeitems[index] = selparams[index].treeitem; } #endif dp.data = conf; dlg_refresh(NULL, &dp); dp.shortcuts = &selparams[0].shortcuts; #if !GTK_CHECK_VERSION(2,0,0) dp.currtreeitem = dp.treeitems[0]; #endif dp.lastfocus = NULL; |
︙ | ︙ | |||
3263 3264 3265 3266 3267 3268 3269 | dlg_cleanup(&dp); ctrl_free_box(ctrlbox); return dp.retval; } | | | 3258 3259 3260 3261 3262 3263 3264 3265 3266 3267 3268 3269 3270 3271 3272 | dlg_cleanup(&dp); ctrl_free_box(ctrlbox); return dp.retval; } int string_width(char *text) { GtkWidget *label = gtk_label_new(text); GtkRequisition req; gtk_widget_size_request(label, &req); gtk_object_sink(GTK_OBJECT(label)); return req.width; } |
︙ | ︙ | |||
3389 3390 3391 3392 3393 3394 3395 3396 3397 3398 3399 3400 3401 3402 3403 3404 3405 3406 3407 3408 3409 3410 3411 3412 3413 3414 3415 3416 3417 3418 3419 3420 3421 | void fatal_message_box(void *window, char *msg) { messagebox(window, "PuTTY Fatal Error", msg, string_width("REASONABLY LONG LINE OF TEXT FOR BASIC SANITY"), "OK", 'o', 1, 1, NULL); } void fatalbox(char *p, ...) { va_list ap; char *msg; va_start(ap, p); msg = dupvprintf(p, ap); va_end(ap); fatal_message_box(NULL, msg); sfree(msg); cleanup_exit(1); } static GtkWidget *aboutbox = NULL; static void about_close_clicked(GtkButton *button, gpointer data) { gtk_widget_destroy(aboutbox); aboutbox = NULL; } static void licence_clicked(GtkButton *button, gpointer data) { char *title; char *licence = | > > > > > > > > > > > > > > > > > > | | 3384 3385 3386 3387 3388 3389 3390 3391 3392 3393 3394 3395 3396 3397 3398 3399 3400 3401 3402 3403 3404 3405 3406 3407 3408 3409 3410 3411 3412 3413 3414 3415 3416 3417 3418 3419 3420 3421 3422 3423 3424 3425 3426 3427 3428 3429 3430 3431 3432 3433 3434 3435 3436 3437 3438 3439 3440 3441 3442 | void fatal_message_box(void *window, char *msg) { messagebox(window, "PuTTY Fatal Error", msg, string_width("REASONABLY LONG LINE OF TEXT FOR BASIC SANITY"), "OK", 'o', 1, 1, NULL); } void nonfatal_message_box(void *window, char *msg) { messagebox(window, "PuTTY Error", msg, string_width("REASONABLY LONG LINE OF TEXT FOR BASIC SANITY"), "OK", 'o', 1, 1, NULL); } void fatalbox(char *p, ...) { va_list ap; char *msg; va_start(ap, p); msg = dupvprintf(p, ap); va_end(ap); fatal_message_box(NULL, msg); sfree(msg); cleanup_exit(1); } void nonfatal(char *p, ...) { va_list ap; char *msg; va_start(ap, p); msg = dupvprintf(p, ap); va_end(ap); nonfatal_message_box(NULL, msg); sfree(msg); } static GtkWidget *aboutbox = NULL; static void about_close_clicked(GtkButton *button, gpointer data) { gtk_widget_destroy(aboutbox); aboutbox = NULL; } static void licence_clicked(GtkButton *button, gpointer data) { char *title; char *licence = "Copyright 1997-2013 Simon Tatham.\n\n" "Portions copyright Robert de Bath, Joris van Rantwijk, Delian " "Delchev, Andreas Schultz, Jeroen Massar, Wez Furlong, Nicolas " "Barry, Justin Bradford, Ben Harris, Malcolm Smith, Ahmad Khalifa, " "Markus Kuhn, Colin Watson, and CORE SDI S.A.\n\n" "Permission is hereby granted, free of charge, to any person " |
︙ | ︙ | |||
3496 3497 3498 3499 3500 3501 3502 | gtk_widget_show(w); w = gtk_label_new(ver); gtk_box_pack_start(GTK_BOX(GTK_DIALOG(aboutbox)->vbox), w, FALSE, FALSE, 5); gtk_widget_show(w); | | | 3509 3510 3511 3512 3513 3514 3515 3516 3517 3518 3519 3520 3521 3522 3523 | gtk_widget_show(w); w = gtk_label_new(ver); gtk_box_pack_start(GTK_BOX(GTK_DIALOG(aboutbox)->vbox), w, FALSE, FALSE, 5); gtk_widget_show(w); w = gtk_label_new("Copyright 1997-2013 Simon Tatham. All rights reserved"); gtk_box_pack_start(GTK_BOX(GTK_DIALOG(aboutbox)->vbox), w, FALSE, FALSE, 5); gtk_widget_show(w); set_transient_window_pos(GTK_WIDGET(window), aboutbox); gtk_window_set_transient_for(GTK_WINDOW(aboutbox), GTK_WINDOW(window)); |
︙ | ︙ | |||
3748 3749 3750 3751 3752 3753 3754 | strcat(es->events[es->nevents], string); if (es->window) { dlg_listbox_add(es->listctrl, &es->dp, es->events[es->nevents]); } es->nevents++; } | | | | 3761 3762 3763 3764 3765 3766 3767 3768 3769 3770 3771 3772 3773 3774 3775 3776 3777 3778 3779 3780 3781 3782 3783 3784 3785 3786 3787 | strcat(es->events[es->nevents], string); if (es->window) { dlg_listbox_add(es->listctrl, &es->dp, es->events[es->nevents]); } es->nevents++; } int askappend(void *frontend, Filename *filename, void (*callback)(void *ctx, int result), void *ctx) { static const char msgtemplate[] = "The session log file \"%.*s\" already exists. " "You can overwrite it with a new session log, " "append your session log to the end of it, " "or disable session logging for this session."; char *message; char *mbtitle; int mbret; message = dupprintf(msgtemplate, FILENAME_MAX, filename->path); mbtitle = dupprintf("%s Log to File", appname); mbret = messagebox(get_window(frontend), mbtitle, message, string_width("LINE OF TEXT SUITABLE FOR THE" " ASKAPPEND WIDTH"), "Overwrite", 'o', 1, 2, "Append", 'a', 0, 1, |
︙ | ︙ |
Changes to unix/gtkfont.c.
︙ | ︙ | |||
26 27 28 29 30 31 32 | /* * Future work: * * - it would be nice to have a display of the current font name, * and in particular whether it's client- or server-side, * during the progress of the font selector. * | < < < < < < > > > > > | 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 | /* * Future work: * * - it would be nice to have a display of the current font name, * and in particular whether it's client- or server-side, * during the progress of the font selector. * * - it would be nice if we could move the processing of * underline and VT100 double width into this module, so that * instead of using the ghastly pixmap-stretching technique * everywhere we could tell the Pango backend to scale its * fonts to double size properly and at full resolution. * However, this requires me to learn how to make Pango stretch * text to an arbitrary aspect ratio (for double-width only * text, which perversely is harder than DW+DH), and right now * I haven't the energy. */ #if !GLIB_CHECK_VERSION(1,3,7) #define g_ascii_strcasecmp g_strcasecmp #define g_ascii_strncasecmp g_strncasecmp #endif /* * Ad-hoc vtable mechanism to allow font structures to be * polymorphic. * * Any instance of `unifont' used in the vtable functions will * actually be the first element of a larger structure containing |
︙ | ︙ | |||
73 74 75 76 77 78 79 80 81 | struct unifont_vtable { /* * `Methods' of the `class'. */ unifont *(*create)(GtkWidget *widget, const char *name, int wide, int bold, int shadowoffset, int shadowalways); void (*destroy)(unifont *font); void (*draw_text)(GdkDrawable *target, GdkGC *gc, unifont *font, | > > > | | > | | 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 | struct unifont_vtable { /* * `Methods' of the `class'. */ unifont *(*create)(GtkWidget *widget, const char *name, int wide, int bold, int shadowoffset, int shadowalways); unifont *(*create_fallback)(GtkWidget *widget, int height, int wide, int bold, int shadowoffset, int shadowalways); void (*destroy)(unifont *font); int (*has_glyph)(unifont *font, wchar_t glyph); void (*draw_text)(GdkDrawable *target, GdkGC *gc, unifont *font, int x, int y, const wchar_t *string, int len, int wide, int bold, int cellwidth); void (*enum_fonts)(GtkWidget *widget, fontsel_add_entry callback, void *callback_ctx); char *(*canonify_fontname)(GtkWidget *widget, const char *name, int *size, int *flags, int resolve_aliases); char *(*scale_fontname)(GtkWidget *widget, const char *name, int size); /* * `Static data members' of the `class'. */ const char *prefix; }; /* ---------------------------------------------------------------------- * X11 font implementation, directly using Xlib calls. */ static int x11font_has_glyph(unifont *font, wchar_t glyph); static void x11font_draw_text(GdkDrawable *target, GdkGC *gc, unifont *font, int x, int y, const wchar_t *string, int len, int wide, int bold, int cellwidth); static unifont *x11font_create(GtkWidget *widget, const char *name, int wide, int bold, int shadowoffset, int shadowalways); static void x11font_destroy(unifont *font); static void x11font_enum_fonts(GtkWidget *widget, fontsel_add_entry callback, void *callback_ctx); |
︙ | ︙ | |||
120 121 122 123 124 125 126 | * * The parallel array `allocated' indicates whether we've * tried to fetch a subfont already (thus distinguishing NULL * because we haven't tried yet from NULL because we tried and * failed, so that we don't keep trying and failing * subsequently). */ | | | > > > > > > > > > > | < | | 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 | * * The parallel array `allocated' indicates whether we've * tried to fetch a subfont already (thus distinguishing NULL * because we haven't tried yet from NULL because we tried and * failed, so that we don't keep trying and failing * subsequently). */ XFontStruct *fonts[4]; int allocated[4]; /* * `sixteen_bit' is true iff the font object is indexed by * values larger than a byte. That is, this flag tells us * whether we use XDrawString or XDrawString16, etc. */ int sixteen_bit; /* * `variable' is true iff the font is non-fixed-pitch. This * enables some code which takes greater care over character * positioning during text drawing. */ int variable; /* * real_charset is the charset used when translating text into the * font's internal encoding inside draw_text(). This need not be * the same as the public_charset provided to the client; for * example, public_charset might be CS_ISO8859_1 while * real_charset is CS_ISO8859_1_X11. */ int real_charset; /* * Data passed in to unifont_create(). */ int wide, bold, shadowoffset, shadowalways; }; static const struct unifont_vtable x11font_vtable = { x11font_create, NULL, /* no fallback fonts in X11 */ x11font_destroy, x11font_has_glyph, x11font_draw_text, x11font_enum_fonts, x11font_canonify_fontname, x11font_scale_fontname, "server", }; static char *x11_guess_derived_font_name(XFontStruct *xfs, int bold, int wide) { Display *disp = GDK_DISPLAY(); Atom fontprop = XInternAtom(disp, "FONT", False); unsigned long ret; if (XGetFontProperty(xfs, fontprop, &ret)) { char *name = XGetAtomName(disp, (Atom)ret); if (name && name[0] == '-') { char *strings[13]; char *dupname, *extrafree = NULL, *ret; |
︙ | ︙ | |||
175 176 177 178 179 180 181 | if (*p == '-') { *p = '\0'; strings[nstr++] = p+1; } p++; } | | > > | 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 | if (*p == '-') { *p = '\0'; strings[nstr++] = p+1; } p++; } if (nstr < lenof(strings)) { sfree(dupname); return NULL; /* XLFD was malformed */ } if (bold) strings[2] = "bold"; if (wide) { /* 4 is `wideness', which obviously may have changed. */ /* 5 is additional style, which may be e.g. `ja' or `ko'. */ |
︙ | ︙ | |||
202 203 204 205 206 207 208 | return ret; } } return NULL; } | | > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | > > < | | | < < < | 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 | return ret; } } return NULL; } static int x11_font_width(XFontStruct *xfs, int sixteen_bit) { if (sixteen_bit) { XChar2b space; space.byte1 = 0; space.byte2 = '0'; return XTextWidth16(xfs, &space, 1); } else { return XTextWidth(xfs, "0", 1); } } static int x11_font_has_glyph(XFontStruct *xfs, int byte1, int byte2) { int index; /* * Not to be confused with x11font_has_glyph, which is a method of * the x11font 'class' and hence takes a unifont as argument. This * is the low-level function which grubs about in an actual * XFontStruct to see if a given glyph exists. * * We must do this ourselves rather than letting Xlib's * XTextExtents16 do the job, because XTextExtents will helpfully * substitute the font's default_char for any missing glyph and * not tell us it did so, which precisely won't help us find out * which glyphs _are_ missing. * * The man page for XQueryFont is rather confusing about how the * per_char array in the XFontStruct is laid out, because it gives * formulae for determining the two-byte X character code _from_ * an index into the per_char array. Going the other way, it's * rather simpler: * * The valid character codes have byte1 between min_byte1 and * max_byte1 inclusive, and byte2 between min_char_or_byte2 and * max_char_or_byte2 inclusive. This gives a rectangle of size * (max_byte2-min_byte1+1) by * (max_char_or_byte2-min_char_or_byte2+1), which is precisely the * rectangle encoded in the per_char array. Hence, given a * character code which is valid in the sense that it falls * somewhere in that rectangle, its index in per_char is given by * setting * * x = byte2 - min_char_or_byte2 * y = byte1 - min_byte1 * index = y * (max_char_or_byte2-min_char_or_byte2+1) + x * * If min_byte1 and min_byte2 are both zero, that's a special case * which can be treated as if min_byte2 was 1 instead, i.e. the * per_char array just runs from min_char_or_byte2 to * max_char_or_byte2 inclusive, and byte1 should always be zero. */ if (byte2 < xfs->min_char_or_byte2 || byte2 > xfs->max_char_or_byte2) return FALSE; if (xfs->min_byte1 == 0 && xfs->max_byte1 == 0) { index = byte2 - xfs->min_char_or_byte2; } else { if (byte1 < xfs->min_byte1 || byte1 > xfs->max_byte1) return FALSE; index = ((byte2 - xfs->min_char_or_byte2) + ((byte1 - xfs->min_byte1) * (xfs->max_char_or_byte2 - xfs->min_char_or_byte2 + 1))); } if (!xfs->per_char) /* per_char NULL => everything in range exists */ return TRUE; return (xfs->per_char[index].ascent + xfs->per_char[index].descent > 0 || xfs->per_char[index].width > 0); } static unifont *x11font_create(GtkWidget *widget, const char *name, int wide, int bold, int shadowoffset, int shadowalways) { struct x11font *xfont; XFontStruct *xfs; Display *disp = GDK_DISPLAY(); Atom charset_registry, charset_encoding, spacing; unsigned long registry_ret, encoding_ret, spacing_ret; int pubcs, realcs, sixteen_bit, variable; int i; xfs = XLoadQueryFont(disp, name); if (!xfs) return NULL; charset_registry = XInternAtom(disp, "CHARSET_REGISTRY", False); charset_encoding = XInternAtom(disp, "CHARSET_ENCODING", False); pubcs = realcs = CS_NONE; sixteen_bit = FALSE; variable = TRUE; |
︙ | ︙ | |||
262 263 264 265 266 267 268 | */ if (!strcasecmp(encoding, "iso10646-1")) { sixteen_bit = TRUE; pubcs = realcs = CS_UTF8; } /* | | | | | < < < < < < | | | | < < | | | | | | > | | > | > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | | | > | | > > > > > > > | > | | > | | | > > | | | > > | < < | < < < < < < < < < < < < < | | | | | < | | < > > > > > > > < | > | 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 | */ if (!strcasecmp(encoding, "iso10646-1")) { sixteen_bit = TRUE; pubcs = realcs = CS_UTF8; } /* * Hack for X line-drawing characters: if the primary font * is encoded as ISO-8859-1, and has valid glyphs in the * low character positions, it is assumed that those * glyphs are the VT100 line-drawing character set. */ if (pubcs == CS_ISO8859_1) { int ch; for (ch = 1; ch < 32; ch++) if (!x11_font_has_glyph(xfs, 0, ch)) break; if (ch == 32) realcs = CS_ISO8859_1_X11; } sfree(encoding); } } spacing = XInternAtom(disp, "SPACING", False); if (XGetFontProperty(xfs, spacing, &spacing_ret)) { char *spc; spc = XGetAtomName(disp, (Atom)spacing_ret); if (spc && strchr("CcMm", spc[0])) variable = FALSE; } xfont = snew(struct x11font); xfont->u.vt = &x11font_vtable; xfont->u.width = x11_font_width(xfs, sixteen_bit); xfont->u.ascent = xfs->ascent; xfont->u.descent = xfs->descent; xfont->u.height = xfont->u.ascent + xfont->u.descent; xfont->u.public_charset = pubcs; xfont->u.want_fallback = TRUE; xfont->real_charset = realcs; xfont->fonts[0] = xfs; xfont->allocated[0] = TRUE; xfont->sixteen_bit = sixteen_bit; xfont->variable = variable; xfont->wide = wide; xfont->bold = bold; xfont->shadowoffset = shadowoffset; xfont->shadowalways = shadowalways; for (i = 1; i < lenof(xfont->fonts); i++) { xfont->fonts[i] = NULL; xfont->allocated[i] = FALSE; } return (unifont *)xfont; } static void x11font_destroy(unifont *font) { Display *disp = GDK_DISPLAY(); struct x11font *xfont = (struct x11font *)font; int i; for (i = 0; i < lenof(xfont->fonts); i++) if (xfont->fonts[i]) XFreeFont(disp, xfont->fonts[i]); sfree(font); } static void x11_alloc_subfont(struct x11font *xfont, int sfid) { Display *disp = GDK_DISPLAY(); char *derived_name = x11_guess_derived_font_name (xfont->fonts[0], sfid & 1, !!(sfid & 2)); xfont->fonts[sfid] = XLoadQueryFont(disp, derived_name); xfont->allocated[sfid] = TRUE; sfree(derived_name); /* Note that xfont->fonts[sfid] may still be NULL, if XLQF failed. */ } static int x11font_has_glyph(unifont *font, wchar_t glyph) { struct x11font *xfont = (struct x11font *)font; if (xfont->sixteen_bit) { /* * This X font has 16-bit character indices, which means * we can directly use our Unicode input value. */ return x11_font_has_glyph(xfont->fonts[0], glyph >> 8, glyph & 0xFF); } else { /* * This X font has 8-bit indices, so we must convert to the * appropriate character set. */ char sbstring[2]; int sblen = wc_to_mb(xfont->real_charset, 0, &glyph, 1, sbstring, 2, "", NULL, NULL); if (sblen == 0 || !sbstring[0]) return FALSE; /* not even in the charset */ return x11_font_has_glyph(xfont->fonts[0], 0, (unsigned char)sbstring[0]); } } #if !GTK_CHECK_VERSION(2,0,0) #define GDK_DRAWABLE_XID(d) GDK_WINDOW_XWINDOW(d) /* GTK1's name for this */ #endif static void x11font_really_draw_text_16(GdkDrawable *target, XFontStruct *xfs, GC gc, int x, int y, const XChar2b *string, int nchars, int shadowoffset, int fontvariable, int cellwidth) { Display *disp = GDK_DISPLAY(); int step, nsteps, centre; if (fontvariable) { /* * In a variable-pitch font, we draw one character at a * time, and centre it in the character cell. */ step = 1; nsteps = nchars; centre = TRUE; } else { /* * In a fixed-pitch font, we can draw the whole lot in one go. */ step = nchars; nsteps = 1; centre = FALSE; } while (nsteps-- > 0) { int X = x; if (centre) X += (cellwidth - XTextWidth16(xfs, string, step)) / 2; XDrawString16(disp, GDK_DRAWABLE_XID(target), gc, X, y, string, step); if (shadowoffset) XDrawString16(disp, GDK_DRAWABLE_XID(target), gc, X + shadowoffset, y, string, step); x += cellwidth; string += step; } } static void x11font_really_draw_text(GdkDrawable *target, XFontStruct *xfs, GC gc, int x, int y, const char *string, int nchars, int shadowoffset, int fontvariable, int cellwidth) { Display *disp = GDK_DISPLAY(); int step, nsteps, centre; if (fontvariable) { /* * In a variable-pitch font, we draw one character at a * time, and centre it in the character cell. */ step = 1; nsteps = nchars; centre = TRUE; } else { /* * In a fixed-pitch font, we can draw the whole lot in one go. */ step = nchars; nsteps = 1; centre = FALSE; } while (nsteps-- > 0) { int X = x; if (centre) X += (cellwidth - XTextWidth(xfs, string, step)) / 2; XDrawString(disp, GDK_DRAWABLE_XID(target), gc, X, y, string, step); if (shadowoffset) XDrawString(disp, GDK_DRAWABLE_XID(target), gc, X + shadowoffset, y, string, step); x += cellwidth; string += step; } } static void x11font_draw_text(GdkDrawable *target, GdkGC *gdkgc, unifont *font, int x, int y, const wchar_t *string, int len, int wide, int bold, int cellwidth) { Display *disp = GDK_DISPLAY(); struct x11font *xfont = (struct x11font *)font; GC gc = GDK_GC_XGC(gdkgc); int sfid; int shadowoffset = 0; int mult = (wide ? 2 : 1); wide -= xfont->wide; bold -= xfont->bold; /* * Decide which subfont we're using, and whether we have to * use shadow bold. */ if (xfont->shadowalways && bold) { shadowoffset = xfont->shadowoffset; bold = 0; } sfid = 2 * wide + bold; if (!xfont->allocated[sfid]) x11_alloc_subfont(xfont, sfid); if (bold && !xfont->fonts[sfid]) { bold = 0; shadowoffset = xfont->shadowoffset; sfid = 2 * wide + bold; if (!xfont->allocated[sfid]) x11_alloc_subfont(xfont, sfid); } if (!xfont->fonts[sfid]) return; /* we've tried our best, but no luck */ XSetFont(disp, gc, xfont->fonts[sfid]->fid); if (xfont->sixteen_bit) { /* * This X font has 16-bit character indices, which means * we can directly use our Unicode input string. */ XChar2b *xcs; int i; xcs = snewn(len, XChar2b); for (i = 0; i < len; i++) { xcs[i].byte1 = string[i] >> 8; xcs[i].byte2 = string[i]; } x11font_really_draw_text_16(target, xfont->fonts[sfid], gc, x, y, xcs, len, shadowoffset, xfont->variable, cellwidth * mult); sfree(xcs); } else { /* * This X font has 8-bit indices, so we must convert to the * appropriate character set. */ char *sbstring = snewn(len+1, char); int sblen = wc_to_mb(xfont->real_charset, 0, string, len, sbstring, len+1, ".", NULL, NULL); x11font_really_draw_text(target, xfont->fonts[sfid], gc, x, y, sbstring, sblen, shadowoffset, xfont->variable, cellwidth * mult); sfree(sbstring); } } static void x11font_enum_fonts(GtkWidget *widget, fontsel_add_entry callback, void *callback_ctx) { char **fontnames; |
︙ | ︙ | |||
520 521 522 523 524 525 526 | /* * Style is a mixture of quite a lot of the fields, * with some strange formatting. */ style = p; p += sprintf(p, "%s", components[2][0] ? components[2] : "regular"); | | | | | | | | | | | | | | | | | | | 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 | /* * Style is a mixture of quite a lot of the fields, * with some strange formatting. */ style = p; p += sprintf(p, "%s", components[2][0] ? components[2] : "regular"); if (!g_ascii_strcasecmp(components[3], "i")) p += sprintf(p, " italic"); else if (!g_ascii_strcasecmp(components[3], "o")) p += sprintf(p, " oblique"); else if (!g_ascii_strcasecmp(components[3], "ri")) p += sprintf(p, " reverse italic"); else if (!g_ascii_strcasecmp(components[3], "ro")) p += sprintf(p, " reverse oblique"); else if (!g_ascii_strcasecmp(components[3], "ot")) p += sprintf(p, " other-slant"); if (components[4][0] && g_ascii_strcasecmp(components[4], "normal")) p += sprintf(p, " %s", components[4]); if (!g_ascii_strcasecmp(components[10], "m")) p += sprintf(p, " [M]"); if (!g_ascii_strcasecmp(components[10], "c")) p += sprintf(p, " [C]"); if (components[5][0]) p += sprintf(p, " %s", components[5]); /* * Style key is the same stuff as above, but with a * couple of transformations done on it to make it * sort more sensibly. */ p++; stylekey = p; if (!g_ascii_strcasecmp(components[2], "medium") || !g_ascii_strcasecmp(components[2], "regular") || !g_ascii_strcasecmp(components[2], "normal") || !g_ascii_strcasecmp(components[2], "book")) weightkey = 0; else if (!g_ascii_strncasecmp(components[2], "demi", 4) || !g_ascii_strncasecmp(components[2], "semi", 4)) weightkey = 1; else weightkey = 2; if (!g_ascii_strcasecmp(components[3], "r")) slantkey = 0; else if (!g_ascii_strncasecmp(components[3], "r", 1)) slantkey = 2; else slantkey = 1; if (!g_ascii_strcasecmp(components[4], "normal")) setwidthkey = 0; else setwidthkey = 1; p += sprintf(p, "%04d%04d%s%04d%04d%s%04d%04d%s%04d%s%04d%s", weightkey, (int)strlen(components[2]), components[2], |
︙ | ︙ | |||
634 635 636 637 638 639 640 | * property, which should give its full XLFD even if what we * originally had was a wildcard. * * However, we must carefully avoid canonifying font * _aliases_, unless specifically asked to, because the font * selector treats them as worthwhile in their own right. */ | < | > > | < < < < | > | > | > > > | 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 | * property, which should give its full XLFD even if what we * originally had was a wildcard. * * However, we must carefully avoid canonifying font * _aliases_, unless specifically asked to, because the font * selector treats them as worthwhile in their own right. */ XFontStruct *xfs; Display *disp = GDK_DISPLAY(); Atom fontprop, fontprop2; unsigned long ret; xfs = XLoadQueryFont(disp, name); if (!xfs) return NULL; /* didn't make sense to us, sorry */ fontprop = XInternAtom(disp, "FONT", False); if (XGetFontProperty(xfs, fontprop, &ret)) { char *newname = XGetAtomName(disp, (Atom)ret); if (newname) { unsigned long fsize = 12; fontprop2 = XInternAtom(disp, "PIXEL_SIZE", False); if (XGetFontProperty(xfs, fontprop2, &fsize) && fsize > 0) { *size = fsize; XFreeFont(disp, xfs); if (flags) { if (name[0] == '-' || resolve_aliases) *flags = FONTFLAG_SERVERSIDE; else *flags = FONTFLAG_SERVERALIAS; } return dupstr(name[0] == '-' || resolve_aliases ? newname : name); } } } XFreeFont(disp, xfs); return NULL; /* something went wrong */ } static char *x11font_scale_fontname(GtkWidget *widget, const char *name, int size) { return NULL; /* shan't */ } #if GTK_CHECK_VERSION(2,0,0) /* ---------------------------------------------------------------------- * Pango font implementation (for GTK 2 only). */ #if defined PANGO_PRE_1POINT4 && !defined PANGO_PRE_1POINT6 #define PANGO_PRE_1POINT6 /* make life easier for pre-1.4 folk */ #endif static int pangofont_has_glyph(unifont *font, wchar_t glyph); static void pangofont_draw_text(GdkDrawable *target, GdkGC *gc, unifont *font, int x, int y, const wchar_t *string, int len, int wide, int bold, int cellwidth); static unifont *pangofont_create(GtkWidget *widget, const char *name, int wide, int bold, int shadowoffset, int shadowalways); static unifont *pangofont_create_fallback(GtkWidget *widget, int height, int wide, int bold, int shadowoffset, int shadowalways); static void pangofont_destroy(unifont *font); static void pangofont_enum_fonts(GtkWidget *widget, fontsel_add_entry callback, void *callback_ctx); static char *pangofont_canonify_fontname(GtkWidget *widget, const char *name, int *size, int *flags, int resolve_aliases); static char *pangofont_scale_fontname(GtkWidget *widget, const char *name, |
︙ | ︙ | |||
724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 | * Data passed in to unifont_create(). */ int bold, shadowoffset, shadowalways; }; static const struct unifont_vtable pangofont_vtable = { pangofont_create, pangofont_destroy, pangofont_draw_text, pangofont_enum_fonts, pangofont_canonify_fontname, pangofont_scale_fontname, "client", }; | > > | 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 | * Data passed in to unifont_create(). */ int bold, shadowoffset, shadowalways; }; static const struct unifont_vtable pangofont_vtable = { pangofont_create, pangofont_create_fallback, pangofont_destroy, pangofont_has_glyph, pangofont_draw_text, pangofont_enum_fonts, pangofont_canonify_fontname, pangofont_scale_fontname, "client", }; |
︙ | ︙ | |||
770 771 772 773 774 775 776 | pango_font_map_list_families(map, &families, &nfamilies); #else pango_context_list_families(ctx, &families, &nfamilies); #endif matched = FALSE; for (i = 0; i < nfamilies; i++) { | | | | > > | | < < < < < < < < < < < < < < | 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 | pango_font_map_list_families(map, &families, &nfamilies); #else pango_context_list_families(ctx, &families, &nfamilies); #endif matched = FALSE; for (i = 0; i < nfamilies; i++) { if (!g_ascii_strcasecmp(pango_font_family_get_name(families[i]), pango_font_description_get_family(desc))) { matched = TRUE; break; } } g_free(families); return matched; } static unifont *pangofont_create_internal(GtkWidget *widget, PangoContext *ctx, PangoFontDescription *desc, int wide, int bold, int shadowoffset, int shadowalways) { struct pangofont *pfont; #ifndef PANGO_PRE_1POINT6 PangoFontMap *map; #endif PangoFontset *fset; PangoFontMetrics *metrics; #ifndef PANGO_PRE_1POINT6 map = pango_context_get_font_map(ctx); if (!map) { pango_font_description_free(desc); return NULL; } fset = pango_font_map_load_fontset(map, ctx, desc, |
︙ | ︙ | |||
837 838 839 840 841 842 843 844 845 | pfont = snew(struct pangofont); pfont->u.vt = &pangofont_vtable; pfont->u.width = PANGO_PIXELS(pango_font_metrics_get_approximate_digit_width(metrics)); pfont->u.ascent = PANGO_PIXELS(pango_font_metrics_get_ascent(metrics)); pfont->u.descent = PANGO_PIXELS(pango_font_metrics_get_descent(metrics)); pfont->u.height = pfont->u.ascent + pfont->u.descent; /* The Pango API is hardwired to UTF-8 */ pfont->u.public_charset = CS_UTF8; | > < > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > | | 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 | pfont = snew(struct pangofont); pfont->u.vt = &pangofont_vtable; pfont->u.width = PANGO_PIXELS(pango_font_metrics_get_approximate_digit_width(metrics)); pfont->u.ascent = PANGO_PIXELS(pango_font_metrics_get_ascent(metrics)); pfont->u.descent = PANGO_PIXELS(pango_font_metrics_get_descent(metrics)); pfont->u.height = pfont->u.ascent + pfont->u.descent; pfont->u.want_fallback = FALSE; /* The Pango API is hardwired to UTF-8 */ pfont->u.public_charset = CS_UTF8; pfont->desc = desc; pfont->fset = fset; pfont->widget = widget; pfont->bold = bold; pfont->shadowoffset = shadowoffset; pfont->shadowalways = shadowalways; pango_font_metrics_unref(metrics); return (unifont *)pfont; } static unifont *pangofont_create(GtkWidget *widget, const char *name, int wide, int bold, int shadowoffset, int shadowalways) { PangoContext *ctx; PangoFontDescription *desc; desc = pango_font_description_from_string(name); if (!desc) return NULL; ctx = gtk_widget_get_pango_context(widget); if (!ctx) { pango_font_description_free(desc); return NULL; } if (!pangofont_check_desc_makes_sense(ctx, desc)) { pango_font_description_free(desc); return NULL; } return pangofont_create_internal(widget, ctx, desc, wide, bold, shadowoffset, shadowalways); } static unifont *pangofont_create_fallback(GtkWidget *widget, int height, int wide, int bold, int shadowoffset, int shadowalways) { PangoContext *ctx; PangoFontDescription *desc; desc = pango_font_description_from_string("Monospace"); if (!desc) return NULL; ctx = gtk_widget_get_pango_context(widget); if (!ctx) { pango_font_description_free(desc); return NULL; } pango_font_description_set_absolute_size(desc, height * PANGO_SCALE); return pangofont_create_internal(widget, ctx, desc, wide, bold, shadowoffset, shadowalways); } static void pangofont_destroy(unifont *font) { struct pangofont *pfont = (struct pangofont *)font; pango_font_description_free(pfont->desc); g_object_unref(pfont->fset); sfree(font); } static int pangofont_has_glyph(unifont *font, wchar_t glyph) { /* Pango implements font fallback, so assume it has everything */ return TRUE; } static void pangofont_draw_text(GdkDrawable *target, GdkGC *gc, unifont *font, int x, int y, const wchar_t *string, int len, int wide, int bold, int cellwidth) { struct pangofont *pfont = (struct pangofont *)font; PangoLayout *layout; PangoRectangle rect; char *utfstring, *utfptr; int utflen; int shadowbold = FALSE; if (wide) cellwidth *= 2; y -= pfont->u.ascent; layout = pango_layout_new(gtk_widget_get_pango_context(pfont->widget)); pango_layout_set_font_description(layout, pfont->desc); if (bold > pfont->bold) { if (pfont->shadowalways) shadowbold = TRUE; else { PangoFontDescription *desc2 = pango_font_description_copy_static(pfont->desc); pango_font_description_set_weight(desc2, PANGO_WEIGHT_BOLD); pango_layout_set_font_description(layout, desc2); } } /* * Pango always expects UTF-8, so convert the input wide character * string to UTF-8. */ utfstring = snewn(len*6+1, char); /* UTF-8 has max 6 bytes/char */ utflen = wc_to_mb(CS_UTF8, 0, string, len, utfstring, len*6+1, ".", NULL, NULL); utfptr = utfstring; while (utflen > 0) { int clen, n; /* * We want to display every character from this string in * the centre of its own character cell. In the worst case, * this requires a separate text-drawing call for each * character; but in the common case where the font is |
︙ | ︙ | |||
919 920 921 922 923 924 925 | */ /* * Start by extracting a single UTF-8 character from the * string. */ clen = 1; | | | | | > > > > > > > | | | | | | | | | | | | | | | | | | | | | | | | | | | > | | > | > > | 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 | */ /* * Start by extracting a single UTF-8 character from the * string. */ clen = 1; while (clen < utflen && (unsigned char)utfptr[clen] >= 0x80 && (unsigned char)utfptr[clen] < 0xC0) clen++; n = 1; /* * If it's a right-to-left character, we must display it on * its own, to stop Pango helpfully re-reversing our already * reversed text. */ if (!is_rtl(string[0])) { /* * See if that character has the width we expect. */ pango_layout_set_text(layout, utfptr, clen); pango_layout_get_pixel_extents(layout, NULL, &rect); if (rect.width == cellwidth) { /* * Try extracting more characters, for as long as they * stay well-behaved. */ while (clen < utflen) { int oldclen = clen; clen++; /* skip UTF-8 introducer byte */ while (clen < utflen && (unsigned char)utfptr[clen] >= 0x80 && (unsigned char)utfptr[clen] < 0xC0) clen++; n++; pango_layout_set_text(layout, utfptr, clen); pango_layout_get_pixel_extents(layout, NULL, &rect); if (rect.width != n * cellwidth) { clen = oldclen; n--; break; } } } } pango_layout_set_text(layout, utfptr, clen); pango_layout_get_pixel_extents(layout, NULL, &rect); gdk_draw_layout(target, gc, x + (n*cellwidth - rect.width)/2, y + (pfont->u.height - rect.height)/2, layout); if (shadowbold) gdk_draw_layout(target, gc, x + (n*cellwidth - rect.width)/2 + pfont->shadowoffset, y + (pfont->u.height - rect.height)/2, layout); utflen -= clen; utfptr += clen; string += n; x += n * cellwidth; } sfree(utfstring); g_object_unref(layout); } /* * Dummy size value to be used when converting a * PangoFontDescription of a scalable font to a string for |
︙ | ︙ | |||
1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 | /* * Complete list of font-type subclasses. Listed in preference * order for unifont_create(). (That is, in the extremely unlikely * event that the same font name is valid as both a Pango and an * X11 font, it will be interpreted as the former in the absence * of an explicit type-disambiguating prefix.) */ static const struct unifont_vtable *unifont_types[] = { #if GTK_CHECK_VERSION(2,0,0) &pangofont_vtable, #endif &x11font_vtable, }; | > > | 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 | /* * Complete list of font-type subclasses. Listed in preference * order for unifont_create(). (That is, in the extremely unlikely * event that the same font name is valid as both a Pango and an * X11 font, it will be interpreted as the former in the absence * of an explicit type-disambiguating prefix.) * * The 'multifont' subclass is omitted here, as discussed above. */ static const struct unifont_vtable *unifont_types[] = { #if GTK_CHECK_VERSION(2,0,0) &pangofont_vtable, #endif &x11font_vtable, }; |
︙ | ︙ | |||
1305 1306 1307 1308 1309 1310 1311 | void unifont_destroy(unifont *font) { font->vt->destroy(font); } void unifont_draw_text(GdkDrawable *target, GdkGC *gc, unifont *font, | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 | void unifont_destroy(unifont *font) { font->vt->destroy(font); } void unifont_draw_text(GdkDrawable *target, GdkGC *gc, unifont *font, int x, int y, const wchar_t *string, int len, int wide, int bold, int cellwidth) { font->vt->draw_text(target, gc, font, x, y, string, len, wide, bold, cellwidth); } /* ---------------------------------------------------------------------- * Multiple-font wrapper. This is a type of unifont which encapsulates * up to two other unifonts, permitting missing glyphs in the main * font to be filled in by a fallback font. * * This is a type of unifont just like the previous two, but it has a * separate constructor which is manually called by the client, so it * doesn't appear in the list of available font types enumerated by * unifont_create. This means it's not used by unifontsel either, so * it doesn't need to support any methods except draw_text and * destroy. */ static void multifont_draw_text(GdkDrawable *target, GdkGC *gc, unifont *font, int x, int y, const wchar_t *string, int len, int wide, int bold, int cellwidth); static void multifont_destroy(unifont *font); struct multifont { struct unifont u; unifont *main; unifont *fallback; }; static const struct unifont_vtable multifont_vtable = { NULL, /* creation is done specially */ NULL, multifont_destroy, NULL, multifont_draw_text, NULL, NULL, NULL, "client", }; unifont *multifont_create(GtkWidget *widget, const char *name, int wide, int bold, int shadowoffset, int shadowalways) { int i; unifont *font, *fallback; struct multifont *mfont; font = unifont_create(widget, name, wide, bold, shadowoffset, shadowalways); if (!font) return NULL; fallback = NULL; if (font->want_fallback) { for (i = 0; i < lenof(unifont_types); i++) { if (unifont_types[i]->create_fallback) { fallback = unifont_types[i]->create_fallback (widget, font->height, wide, bold, shadowoffset, shadowalways); if (fallback) break; } } } /* * Construct our multifont. Public members are all copied from the * primary font we're wrapping. */ mfont = snew(struct multifont); mfont->u.vt = &multifont_vtable; mfont->u.width = font->width; mfont->u.ascent = font->ascent; mfont->u.descent = font->descent; mfont->u.height = font->height; mfont->u.public_charset = font->public_charset; mfont->u.want_fallback = FALSE; /* shouldn't be needed, but just in case */ mfont->main = font; mfont->fallback = fallback; return (unifont *)mfont; } static void multifont_destroy(unifont *font) { struct multifont *mfont = (struct multifont *)font; unifont_destroy(mfont->main); if (mfont->fallback) unifont_destroy(mfont->fallback); sfree(font); } static void multifont_draw_text(GdkDrawable *target, GdkGC *gc, unifont *font, int x, int y, const wchar_t *string, int len, int wide, int bold, int cellwidth) { struct multifont *mfont = (struct multifont *)font; int ok, i; while (len > 0) { /* * Find a maximal sequence of characters which are, or are * not, supported by our main font. */ ok = mfont->main->vt->has_glyph(mfont->main, string[0]); for (i = 1; i < len && !mfont->main->vt->has_glyph(mfont->main, string[i]) == !ok; i++); /* * Now display it. */ unifont_draw_text(target, gc, ok ? mfont->main : mfont->fallback, x, y, string, i, wide, bold, cellwidth); string += i; len -= i; x += i * cellwidth; } } #if GTK_CHECK_VERSION(2,0,0) /* ---------------------------------------------------------------------- * Implementation of a unified font selector. Used on GTK 2 only; * for GTK 1 we still use the standard font selector. */ |
︙ | ︙ | |||
1389 1390 1391 1392 1393 1394 1395 | */ if (!a) return 0; /* * Otherwise, ordinary strcasecmp. */ | | | 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 | */ if (!a) return 0; /* * Otherwise, ordinary strcasecmp. */ return g_ascii_strcasecmp(a, b); } static int fontinfo_realname_compare(void *av, void *bv) { fontinfo *a = (fontinfo *)av; fontinfo *b = (fontinfo *)bv; int i; |
︙ | ︙ | |||
1688 1689 1690 1691 1692 1693 1694 | * that they find out the problems with using * proportional fonts in terminal windows here than * that they go to the effort of selecting their font * and _then_ realise it was a mistake. */ info->fontclass->draw_text(fs->preview_pixmap, gc, font, 0, font->ascent, | | | | | 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 | * that they find out the problems with using * proportional fonts in terminal windows here than * that they go to the effort of selecting their font * and _then_ realise it was a mistake. */ info->fontclass->draw_text(fs->preview_pixmap, gc, font, 0, font->ascent, L"bankrupt jilted showmen quiz convex fogey", 41, FALSE, FALSE, font->width); info->fontclass->draw_text(fs->preview_pixmap, gc, font, 0, font->ascent + font->height, L"BANKRUPT JILTED SHOWMEN QUIZ CONVEX FOGEY", 41, FALSE, FALSE, font->width); /* * The ordering of punctuation here is also selected * with some specific aims in mind. I put ` and ' * together because some software (and people) still * use them as matched quotes no matter what Unicode * might say on the matter, so people can quickly * check whether they look silly in a candidate font. * The sequence #_@ is there to let people judge the * suitability of the underscore as an effectively * alphabetic character (since that's how it's often * used in practice, at least by programmers). */ info->fontclass->draw_text(fs->preview_pixmap, gc, font, 0, font->ascent + font->height * 2, L"0123456789!?,.:;<>()[]{}\\/`'\"+*-=~#_@|%&^$", 42, FALSE, FALSE, font->width); } gdk_gc_unref(gc); gdk_window_invalidate_rect(fs->preview_area->window, NULL, FALSE); } if (font) info->fontclass->destroy(font); |
︙ | ︙ | |||
1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 | /* * Search in the tree to find the fontinfo structure which * best approximates the size the user last requested. */ below = findrelpos234(fs->fonts_by_selorder, &info2, NULL, REL234_LE, &pos); above = index234(fs->fonts_by_selorder, pos+1); /* * See if we've found it exactly, which is an easy special * case. If we have, it'll be in `below' and not `above', * because we did a REL234_LE rather than REL234_LT search. */ | > > | | 2286 2287 2288 2289 2290 2291 2292 2293 2294 2295 2296 2297 2298 2299 2300 2301 2302 2303 2304 2305 2306 2307 2308 2309 | /* * Search in the tree to find the fontinfo structure which * best approximates the size the user last requested. */ below = findrelpos234(fs->fonts_by_selorder, &info2, NULL, REL234_LE, &pos); if (!below) pos = -1; above = index234(fs->fonts_by_selorder, pos+1); /* * See if we've found it exactly, which is an easy special * case. If we have, it'll be in `below' and not `above', * because we did a REL234_LE rather than REL234_LT search. */ if (below && !fontinfo_selorder_compare(&info2, below)) return below; /* * Now we've either found two suitable fonts, one smaller and * one larger, or we're at one or other extreme end of the * scale. Find out which, by NULLing out either of below and * above if it differs from this one in any respect but size |
︙ | ︙ |
Changes to unix/gtkfont.h.
︙ | ︙ | |||
17 18 19 20 21 22 23 | * `Non-static data members' of the `class', accessible to * external code. */ /* * public_charset is the charset used when the user asks for * `Use font encoding'. | < < < < < < | > > > > > > > > | > > > > > > > > > > > > | 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 | * `Non-static data members' of the `class', accessible to * external code. */ /* * public_charset is the charset used when the user asks for * `Use font encoding'. */ int public_charset; /* * Font dimensions needed by clients. */ int width, height, ascent, descent; /* * Indicates whether this font is capable of handling all glyphs * (Pango fonts can do this because Pango automatically supplies * missing glyphs from other fonts), or whether it would like a * fallback font to cope with missing glyphs. */ int want_fallback; } unifont; unifont *unifont_create(GtkWidget *widget, const char *name, int wide, int bold, int shadowoffset, int shadowalways); void unifont_destroy(unifont *font); void unifont_draw_text(GdkDrawable *target, GdkGC *gc, unifont *font, int x, int y, const wchar_t *string, int len, int wide, int bold, int cellwidth); /* * This function behaves exactly like the low-level unifont_create, * except that as well as the requested font it also allocates (if * necessary) a fallback font for filling in replacement glyphs. * * Return value is usable with unifont_destroy and unifont_draw_text * as if it were an ordinary unifont. */ unifont *multifont_create(GtkWidget *widget, const char *name, int wide, int bold, int shadowoffset, int shadowalways); /* * Unified font selector dialog. I can't be bothered to do a * proper GTK subclassing today, so this will just be an ordinary * data structure with some useful members. * * (Of course, these aren't the only members; this structure is * contained within a bigger one which holds data visible only to |
︙ | ︙ |
Changes to unix/gtkwin.c.
︙ | ︙ | |||
9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | #include <assert.h> #include <stdlib.h> #include <string.h> #include <signal.h> #include <stdio.h> #include <time.h> #include <errno.h> #include <fcntl.h> #include <unistd.h> #include <sys/types.h> #include <sys/wait.h> #include <gtk/gtk.h> #include <gdk/gdkkeysyms.h> #include <gdk/gdkx.h> #include <X11/Xlib.h> #include <X11/Xutil.h> #include <X11/Xatom.h> #define PUTTY_DO_GLOBALS /* actually _define_ globals */ #include "putty.h" #include "terminal.h" #include "gtkfont.h" #define CAT2(x,y) x ## y #define CAT(x,y) CAT2(x,y) #define ASSERT(x) enum {CAT(assertion_,__LINE__) = 1 / (x)} #if GTK_CHECK_VERSION(2,0,0) ASSERT(sizeof(long) <= sizeof(gsize)); #define LONG_TO_GPOINTER(l) GSIZE_TO_POINTER(l) #define GPOINTER_TO_LONG(p) GPOINTER_TO_SIZE(p) #else /* Gtk 1.2 */ ASSERT(sizeof(long) <= sizeof(gpointer)); #define LONG_TO_GPOINTER(l) ((gpointer)(long)(l)) #define GPOINTER_TO_LONG(p) ((long)(p)) #endif /* Colours come in two flavours: configurable, and xterm-extended. */ | > > > > > > > < | 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 | #include <assert.h> #include <stdlib.h> #include <string.h> #include <signal.h> #include <stdio.h> #include <time.h> #include <errno.h> #include <locale.h> #include <fcntl.h> #include <unistd.h> #include <sys/types.h> #include <sys/wait.h> #include <gtk/gtk.h> #include <gdk/gdkkeysyms.h> #include <gdk/gdkx.h> #include <X11/Xlib.h> #include <X11/Xutil.h> #include <X11/Xatom.h> #if GTK_CHECK_VERSION(2,0,0) #include <gtk/gtkimmodule.h> #endif #define PUTTY_DO_GLOBALS /* actually _define_ globals */ #define MAY_REFER_TO_GTK_IN_HEADERS #include "putty.h" #include "terminal.h" #include "gtkfont.h" #define CAT2(x,y) x ## y #define CAT(x,y) CAT2(x,y) #define ASSERT(x) enum {CAT(assertion_,__LINE__) = 1 / (x)} #if GTK_CHECK_VERSION(2,0,0) ASSERT(sizeof(long) <= sizeof(gsize)); #define LONG_TO_GPOINTER(l) GSIZE_TO_POINTER(l) #define GPOINTER_TO_LONG(p) GPOINTER_TO_SIZE(p) #else /* Gtk 1.2 */ ASSERT(sizeof(long) <= sizeof(gpointer)); #define LONG_TO_GPOINTER(l) ((gpointer)(long)(l)) #define GPOINTER_TO_LONG(p) ((long)(p)) #endif /* Colours come in two flavours: configurable, and xterm-extended. */ #define NEXTCOLOURS 240 /* 216 colour-cube plus 24 shades of grey */ #define NALLCOLOURS (NCFGCOLOURS + NEXTCOLOURS) GdkAtom compound_text_atom, utf8_string_atom; extern char **pty_argv; /* declared in pty.c */ extern int use_pty_argv; |
︙ | ︙ | |||
65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 | GtkWidget *window, *area, *sbar; GtkBox *hbox; GtkAdjustment *sbar_adjust; GtkWidget *menu, *specialsmenu, *specialsitem1, *specialsitem2, *restartitem; GtkWidget *sessionsmenu; GdkPixmap *pixmap; unifont *fonts[4]; /* normal, bold, wide, widebold */ int xpos, ypos, gotpos, gravity; GdkCursor *rawcursor, *textcursor, *blankcursor, *waitcursor, *currcursor; GdkColor cols[NALLCOLOURS]; GdkColormap *colmap; wchar_t *pastein_data; int direct_to_font; int pastein_data_len; char *pasteout_data, *pasteout_data_ctext, *pasteout_data_utf8; int pasteout_data_len, pasteout_data_ctext_len, pasteout_data_utf8_len; int font_width, font_height; int width, height; int ignore_sbar; int mouseptr_visible; int busy_status; | > > > | < | | | > > > > > > > > > > > > < | | | < | < | | < | < | | 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 | GtkWidget *window, *area, *sbar; GtkBox *hbox; GtkAdjustment *sbar_adjust; GtkWidget *menu, *specialsmenu, *specialsitem1, *specialsitem2, *restartitem; GtkWidget *sessionsmenu; GdkPixmap *pixmap; #if GTK_CHECK_VERSION(2,0,0) GtkIMContext *imc; #endif unifont *fonts[4]; /* normal, bold, wide, widebold */ int xpos, ypos, gotpos, gravity; GdkCursor *rawcursor, *textcursor, *blankcursor, *waitcursor, *currcursor; GdkColor cols[NALLCOLOURS]; GdkColormap *colmap; wchar_t *pastein_data; int direct_to_font; int pastein_data_len; char *pasteout_data, *pasteout_data_ctext, *pasteout_data_utf8; int pasteout_data_len, pasteout_data_ctext_len, pasteout_data_utf8_len; int font_width, font_height; int width, height; int ignore_sbar; int mouseptr_visible; int busy_status; guint toplevel_callback_idle_id; int alt_keycode; int alt_digits; char *wintitle; char *icontitle; int master_fd, master_func_id; void *ldisc; Backend *back; void *backhandle; Terminal *term; void *logctx; int exited; struct unicode_data ucsdata; Conf *conf; void *eventlogstuff; char *progname, **gtkargvstart; int ngtkargs; guint32 input_event_time; /* Timestamp of the most recent input event. */ int reconfiguring; /* Cached things out of conf that we refer to a lot */ int bold_style; int window_border; int cursor_type; }; static void cache_conf_values(struct gui_data *inst) { inst->bold_style = conf_get_int(inst->conf, CONF_bold_style); inst->window_border = conf_get_int(inst->conf, CONF_window_border); inst->cursor_type = conf_get_int(inst->conf, CONF_cursor_type); } struct draw_ctx { GdkGC *gc; struct gui_data *inst; }; static int send_raw_mouse; static char *app_name = "pterm"; static void start_backend(struct gui_data *inst); static void exit_callback(void *vinst); char *x_get_default(const char *key) { return XGetDefault(GDK_DISPLAY(), app_name, key); } void connection_fatal(void *frontend, char *p, ...) { struct gui_data *inst = (struct gui_data *)frontend; va_list ap; char *msg; va_start(ap, p); msg = dupvprintf(p, ap); va_end(ap); fatal_message_box(inst->window, msg); sfree(msg); queue_toplevel_callback(exit_callback, inst); } /* * Default settings that are specific to pterm. */ FontSpec *platform_default_fontspec(const char *name) { if (!strcmp(name, "Font")) return fontspec_new("server:fixed"); else return fontspec_new(""); } Filename *platform_default_filename(const char *name) { if (!strcmp(name, "LogFileName")) return filename_from_str("putty.log"); else return filename_from_str(""); } char *platform_default_s(const char *name) { if (!strcmp(name, "SerialLine")) return dupstr("/dev/ttyS0"); return NULL; |
︙ | ︙ | |||
195 196 197 198 199 200 201 202 203 204 205 206 207 208 | } int from_backend_untrusted(void *frontend, const char *data, int len) { struct gui_data *inst = (struct gui_data *)frontend; return term_data_untrusted(inst->term, data, len); } int get_userpass_input(prompts_t *p, unsigned char *in, int inlen) { struct gui_data *inst = (struct gui_data *)p->frontend; int ret; ret = cmdline_get_passwd_input(p, in, inlen); if (ret == -1) | > > > > > | 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 | } int from_backend_untrusted(void *frontend, const char *data, int len) { struct gui_data *inst = (struct gui_data *)frontend; return term_data_untrusted(inst->term, data, len); } int from_backend_eof(void *frontend) { return TRUE; /* do respond to incoming EOF with outgoing */ } int get_userpass_input(prompts_t *p, unsigned char *in, int inlen) { struct gui_data *inst = (struct gui_data *)p->frontend; int ret; ret = cmdline_get_passwd_input(p, in, inlen); if (ret == -1) |
︙ | ︙ | |||
390 391 392 393 394 395 396 | struct gui_data *inst = (struct gui_data *)frontend; return icon ? inst->icontitle : inst->wintitle; } gint delete_window(GtkWidget *widget, GdkEvent *event, gpointer data) { struct gui_data *inst = (struct gui_data *)data; | | | 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 | struct gui_data *inst = (struct gui_data *)frontend; return icon ? inst->icontitle : inst->wintitle; } gint delete_window(GtkWidget *widget, GdkEvent *event, gpointer data) { struct gui_data *inst = (struct gui_data *)data; if (!inst->exited && conf_get_int(inst->conf, CONF_warn_on_close)) { if (!reallyclose(inst)) return TRUE; } return FALSE; } static void update_mouseptr(struct gui_data *inst) |
︙ | ︙ | |||
421 422 423 424 425 426 427 | default: assert(0); } } static void show_mouseptr(struct gui_data *inst, int show) { | | | | | | | | > > | < | < | > > > > | 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 | default: assert(0); } } static void show_mouseptr(struct gui_data *inst, int show) { if (!conf_get_int(inst->conf, CONF_hide_mouseptr)) show = 1; inst->mouseptr_visible = show; update_mouseptr(inst); } void draw_backing_rect(struct gui_data *inst) { GdkGC *gc = gdk_gc_new(inst->area->window); gdk_gc_set_foreground(gc, &inst->cols[258]); /* default background */ gdk_draw_rectangle(inst->pixmap, gc, 1, 0, 0, inst->width * inst->font_width + 2*inst->window_border, inst->height * inst->font_height + 2*inst->window_border); gdk_gc_unref(gc); } gint configure_area(GtkWidget *widget, GdkEventConfigure *event, gpointer data) { struct gui_data *inst = (struct gui_data *)data; int w, h, need_size = 0; /* * See if the terminal size has changed, in which case we must * let the terminal know. */ w = (event->width - 2*inst->window_border) / inst->font_width; h = (event->height - 2*inst->window_border) / inst->font_height; if (w != inst->width || h != inst->height) { inst->width = w; inst->height = h; conf_set_int(inst->conf, CONF_width, inst->width); conf_set_int(inst->conf, CONF_height, inst->height); need_size = 1; } if (inst->pixmap) { gdk_pixmap_unref(inst->pixmap); inst->pixmap = NULL; } inst->pixmap = gdk_pixmap_new(widget->window, (w * inst->font_width + 2*inst->window_border), (h * inst->font_height + 2*inst->window_border), -1); draw_backing_rect(inst); if (need_size && inst->term) { term_size(inst->term, h, w, conf_get_int(inst->conf, CONF_savelines)); } if (inst->term) term_invalidate(inst->term); #if GTK_CHECK_VERSION(2,0,0) gtk_im_context_set_client_window(inst->imc, widget->window); #endif return TRUE; } gint expose_area(GtkWidget *widget, GdkEventExpose *event, gpointer data) { struct gui_data *inst = (struct gui_data *)data; |
︙ | ︙ | |||
505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 | gint key_event(GtkWidget *widget, GdkEventKey *event, gpointer data) { struct gui_data *inst = (struct gui_data *)data; char output[256]; wchar_t ucsoutput[2]; int ucsval, start, end, special, output_charset, use_ucsoutput; /* Remember the timestamp. */ inst->input_event_time = event->time; /* By default, nothing is generated. */ end = start = 0; special = use_ucsoutput = FALSE; output_charset = CS_ISO8859_1; /* * If Alt is being released after typing an Alt+numberpad * sequence, we should generate the code that was typed. * * Note that we only do this if more than one key was actually * pressed - I don't think Alt+NumPad4 should be ^D or that * Alt+NumPad3 should be ^C, for example. There's no serious * inconvenience in having to type a zero before a single-digit * character code. */ | > | | | | | | | | | | | | | > > > > > | 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 | gint key_event(GtkWidget *widget, GdkEventKey *event, gpointer data) { struct gui_data *inst = (struct gui_data *)data; char output[256]; wchar_t ucsoutput[2]; int ucsval, start, end, special, output_charset, use_ucsoutput; int nethack_mode, app_keypad_mode; /* Remember the timestamp. */ inst->input_event_time = event->time; /* By default, nothing is generated. */ end = start = 0; special = use_ucsoutput = FALSE; output_charset = CS_ISO8859_1; /* * If Alt is being released after typing an Alt+numberpad * sequence, we should generate the code that was typed. * * Note that we only do this if more than one key was actually * pressed - I don't think Alt+NumPad4 should be ^D or that * Alt+NumPad3 should be ^C, for example. There's no serious * inconvenience in having to type a zero before a single-digit * character code. */ if (event->type == GDK_KEY_RELEASE) { if ((event->keyval == GDK_Meta_L || event->keyval == GDK_Alt_L || event->keyval == GDK_Meta_R || event->keyval == GDK_Alt_R) && inst->alt_keycode >= 0 && inst->alt_digits > 1) { #ifdef KEY_DEBUGGING printf("Alt key up, keycode = %d\n", inst->alt_keycode); #endif /* * FIXME: we might usefully try to do something clever here * about interpreting the generated key code in a way that's * appropriate to the line code page. */ output[0] = inst->alt_keycode; end = 1; goto done; } #if GTK_CHECK_VERSION(2,0,0) if (gtk_im_context_filter_keypress(inst->imc, event)) return TRUE; #endif } if (event->type == GDK_KEY_PRESS) { #ifdef KEY_DEBUGGING { int i; printf("keypress: keyval = %04x, state = %08x; string =", |
︙ | ︙ | |||
614 615 616 617 618 619 620 | } /* * Shift-PgUp and Shift-PgDn don't even generate keystrokes * at all. */ if (event->keyval == GDK_Page_Up && (event->state & GDK_SHIFT_MASK)) { | | | > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > < < < | > | 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 | } /* * Shift-PgUp and Shift-PgDn don't even generate keystrokes * at all. */ if (event->keyval == GDK_Page_Up && (event->state & GDK_SHIFT_MASK)) { term_scroll(inst->term, 0, -inst->height/2); return TRUE; } if (event->keyval == GDK_Page_Up && (event->state & GDK_CONTROL_MASK)) { term_scroll(inst->term, 0, -1); return TRUE; } if (event->keyval == GDK_Page_Down && (event->state & GDK_SHIFT_MASK)) { term_scroll(inst->term, 0, +inst->height/2); return TRUE; } if (event->keyval == GDK_Page_Down && (event->state & GDK_CONTROL_MASK)) { term_scroll(inst->term, 0, +1); return TRUE; } /* * Neither does Shift-Ins. */ if (event->keyval == GDK_Insert && (event->state & GDK_SHIFT_MASK)) { request_paste(inst); return TRUE; } special = FALSE; use_ucsoutput = FALSE; nethack_mode = conf_get_int(inst->conf, CONF_nethack_keypad); app_keypad_mode = (inst->term->app_keypad_keys && !conf_get_int(inst->conf, CONF_no_applic_k)); /* ALT+things gives leading Escape. */ output[0] = '\033'; #if !GTK_CHECK_VERSION(2,0,0) /* * In vanilla X, and hence also GDK 1.2, the string received * as part of a keyboard event is assumed to be in * ISO-8859-1. (Seems woefully shortsighted in i18n terms, * but it's true: see the man page for XLookupString(3) for * confirmation.) */ output_charset = CS_ISO8859_1; strncpy(output+1, event->string, lenof(output)-1); #else /* * Most things can now be passed to * gtk_im_context_filter_keypress without breaking anything * below this point. An exception is the numeric keypad if * we're in Nethack or application mode: the IM will eat * numeric keypad presses if Num Lock is on, but we don't want * it to. */ if (app_keypad_mode && (event->keyval == GDK_Num_Lock || event->keyval == GDK_KP_Divide || event->keyval == GDK_KP_Multiply || event->keyval == GDK_KP_Subtract || event->keyval == GDK_KP_Add || event->keyval == GDK_KP_Enter || event->keyval == GDK_KP_0 || event->keyval == GDK_KP_Insert || event->keyval == GDK_KP_1 || event->keyval == GDK_KP_End || event->keyval == GDK_KP_2 || event->keyval == GDK_KP_Down || event->keyval == GDK_KP_3 || event->keyval == GDK_KP_Page_Down || event->keyval == GDK_KP_4 || event->keyval == GDK_KP_Left || event->keyval == GDK_KP_5 || event->keyval == GDK_KP_Begin || event->keyval == GDK_KP_6 || event->keyval == GDK_KP_Right || event->keyval == GDK_KP_7 || event->keyval == GDK_KP_Home || event->keyval == GDK_KP_8 || event->keyval == GDK_KP_Up || event->keyval == GDK_KP_9 || event->keyval == GDK_KP_Page_Up || event->keyval == GDK_KP_Decimal || event->keyval == GDK_KP_Delete)) { /* app keypad; do nothing */ } else if (nethack_mode && (event->keyval == GDK_KP_1 || event->keyval == GDK_KP_End || event->keyval == GDK_KP_2 || event->keyval == GDK_KP_Down || event->keyval == GDK_KP_3 || event->keyval == GDK_KP_Page_Down || event->keyval == GDK_KP_4 || event->keyval == GDK_KP_Left || event->keyval == GDK_KP_5 || event->keyval == GDK_KP_Begin || event->keyval == GDK_KP_6 || event->keyval == GDK_KP_Right || event->keyval == GDK_KP_7 || event->keyval == GDK_KP_Home || event->keyval == GDK_KP_8 || event->keyval == GDK_KP_Up || event->keyval == GDK_KP_9 || event->keyval == GDK_KP_Page_Up)) { /* nethack mode; do nothing */ } else { if (gtk_im_context_filter_keypress(inst->imc, event)) return TRUE; } /* * GDK 2.0 arranges to have done some translation for us: in * GDK 2.0, event->string is encoded in the current locale. * * So we use the standard C library function mbstowcs() to * convert from the current locale into Unicode; from there * we can convert to whatever PuTTY is currently working in. * (In fact I convert straight back to UTF-8 from * wide-character Unicode, for the sake of simplicity: that * way we can still use exactly the same code to manipulate * the string, such as prefixing ESC.) */ output_charset = CS_UTF8; { wchar_t widedata[32]; const wchar_t *wp; int wlen; int ulen; wlen = mb_to_wc(DEFAULT_CODEPAGE, 0, event->string, strlen(event->string), widedata, lenof(widedata)-1); |
︙ | ︙ | |||
751 752 753 754 755 756 757 | use_ucsoutput = FALSE; end = 2; } /* We don't let GTK tell us what Backspace is! We know better. */ if (event->keyval == GDK_BackSpace && !(event->state & GDK_SHIFT_MASK)) { | > | > | | 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 | use_ucsoutput = FALSE; end = 2; } /* We don't let GTK tell us what Backspace is! We know better. */ if (event->keyval == GDK_BackSpace && !(event->state & GDK_SHIFT_MASK)) { output[1] = conf_get_int(inst->conf, CONF_bksp_is_delete) ? '\x7F' : '\x08'; use_ucsoutput = FALSE; end = 2; special = TRUE; } /* For Shift Backspace, do opposite of what is configured. */ if (event->keyval == GDK_BackSpace && (event->state & GDK_SHIFT_MASK)) { output[1] = conf_get_int(inst->conf, CONF_bksp_is_delete) ? '\x08' : '\x7F'; use_ucsoutput = FALSE; end = 2; special = TRUE; } /* Shift-Tab is ESC [ Z */ if (event->keyval == GDK_ISO_Left_Tab || |
︙ | ︙ | |||
782 783 784 785 786 787 788 | output[1] = '\t'; end = 2; } /* * NetHack keypad mode. */ | | | 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 | output[1] = '\t'; end = 2; } /* * NetHack keypad mode. */ if (nethack_mode) { char *keys = NULL; switch (event->keyval) { case GDK_KP_1: case GDK_KP_End: keys = "bB\002"; break; case GDK_KP_2: case GDK_KP_Down: keys = "jJ\012"; break; case GDK_KP_3: case GDK_KP_Page_Down: keys = "nN\016"; break; case GDK_KP_4: case GDK_KP_Left: keys = "hH\010"; break; case GDK_KP_5: case GDK_KP_Begin: keys = "..."; break; |
︙ | ︙ | |||
811 812 813 814 815 816 817 | goto done; } } /* * Application keypad mode. */ | | | | 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 | goto done; } } /* * Application keypad mode. */ if (app_keypad_mode) { int xkey = 0; switch (event->keyval) { case GDK_Num_Lock: xkey = 'P'; break; case GDK_KP_Divide: xkey = 'Q'; break; case GDK_KP_Multiply: xkey = 'R'; break; case GDK_KP_Subtract: xkey = 'S'; break; /* * Keypad + is tricky. It covers a space that would * be taken up on the VT100 by _two_ keys; so we * let Shift select between the two. Worse still, * in xterm function key mode we change which two... */ case GDK_KP_Add: if (conf_get_int(inst->conf, CONF_funky_type) == FUNKY_XTERM) { if (event->state & GDK_SHIFT_MASK) xkey = 'l'; else xkey = 'k'; } else if (event->state & GDK_SHIFT_MASK) xkey = 'm'; else |
︙ | ︙ | |||
872 873 874 875 876 877 878 879 880 881 882 883 884 885 | * We also deal with the weird ones here. Linux VCs replace F1 * to F5 by ESC [ [ A to ESC [ [ E. rxvt doesn't do _that_, but * does replace Home and End (1~ and 4~) by ESC [ H and ESC O w * respectively. */ { int code = 0; switch (event->keyval) { case GDK_F1: code = (event->state & GDK_SHIFT_MASK ? 23 : 11); break; case GDK_F2: code = (event->state & GDK_SHIFT_MASK ? 24 : 12); break; | > | 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 | * We also deal with the weird ones here. Linux VCs replace F1 * to F5 by ESC [ [ A to ESC [ [ E. rxvt doesn't do _that_, but * does replace Home and End (1~ and 4~) by ESC [ H and ESC O w * respectively. */ { int code = 0; int funky_type = conf_get_int(inst->conf, CONF_funky_type); switch (event->keyval) { case GDK_F1: code = (event->state & GDK_SHIFT_MASK ? 23 : 11); break; case GDK_F2: code = (event->state & GDK_SHIFT_MASK ? 24 : 12); break; |
︙ | ︙ | |||
955 956 957 958 959 960 961 | code = 5; break; case GDK_Page_Down: case GDK_KP_Page_Down: code = 6; break; } /* Reorder edit keys to physical order */ | | | | 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 | code = 5; break; case GDK_Page_Down: case GDK_KP_Page_Down: code = 6; break; } /* Reorder edit keys to physical order */ if (funky_type == FUNKY_VT400 && code <= 6) code = "\0\2\1\4\5\3\6"[code]; if (inst->term->vt52_mode && code > 0 && code <= 6) { end = 1 + sprintf(output+1, "\x1B%c", " HLMEIG"[code]); use_ucsoutput = FALSE; goto done; } if (funky_type == FUNKY_SCO && /* SCO function keys */ code >= 11 && code <= 34) { char codes[] = "MNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz@[\\]^_`{"; int index = 0; switch (event->keyval) { case GDK_F1: index = 0; break; case GDK_F2: index = 1; break; case GDK_F3: index = 2; break; |
︙ | ︙ | |||
988 989 990 991 992 993 994 | } if (event->state & GDK_SHIFT_MASK) index += 12; if (event->state & GDK_CONTROL_MASK) index += 24; end = 1 + sprintf(output+1, "\x1B[%c", codes[index]); use_ucsoutput = FALSE; goto done; } | | | | | | > | 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 | } if (event->state & GDK_SHIFT_MASK) index += 12; if (event->state & GDK_CONTROL_MASK) index += 24; end = 1 + sprintf(output+1, "\x1B[%c", codes[index]); use_ucsoutput = FALSE; goto done; } if (funky_type == FUNKY_SCO && /* SCO small keypad */ code >= 1 && code <= 6) { char codes[] = "HL.FIG"; if (code == 3) { output[1] = '\x7F'; end = 2; } else { end = 1 + sprintf(output+1, "\x1B[%c", codes[code-1]); } use_ucsoutput = FALSE; goto done; } if ((inst->term->vt52_mode || funky_type == FUNKY_VT100P) && code >= 11 && code <= 24) { int offt = 0; if (code > 15) offt++; if (code > 21) offt++; if (inst->term->vt52_mode) end = 1 + sprintf(output+1, "\x1B%c", code + 'P' - 11 - offt); else end = 1 + sprintf(output+1, "\x1BO%c", code + 'P' - 11 - offt); use_ucsoutput = FALSE; goto done; } if (funky_type == FUNKY_LINUX && code >= 11 && code <= 15) { end = 1 + sprintf(output+1, "\x1B[[%c", code + 'A' - 11); use_ucsoutput = FALSE; goto done; } if (funky_type == FUNKY_XTERM && code >= 11 && code <= 14) { if (inst->term->vt52_mode) end = 1 + sprintf(output+1, "\x1B%c", code + 'P' - 11); else end = 1 + sprintf(output+1, "\x1BO%c", code + 'P' - 11); use_ucsoutput = FALSE; goto done; } if ((code == 1 || code == 4) && conf_get_int(inst->conf, CONF_rxvt_homeend)) { end = 1 + sprintf(output+1, code == 1 ? "\x1B[H" : "\x1BOw"); use_ucsoutput = FALSE; goto done; } if (code) { end = 1 + sprintf(output+1, "\x1B[%d~", code); use_ucsoutput = FALSE; |
︙ | ︙ | |||
1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 | show_mouseptr(inst, 0); term_seen_key_event(inst->term); } return TRUE; } gboolean button_internal(struct gui_data *inst, guint32 timestamp, GdkEventType type, guint ebutton, guint state, gdouble ex, gdouble ey) { int shift, ctrl, alt, x, y, button, act; | > > > > > > > > > > > | 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 | show_mouseptr(inst, 0); term_seen_key_event(inst->term); } return TRUE; } #if GTK_CHECK_VERSION(2,0,0) void input_method_commit_event(GtkIMContext *imc, gchar *str, gpointer data) { struct gui_data *inst = (struct gui_data *)data; if (inst->ldisc) lpage_send(inst->ldisc, CS_UTF8, str, strlen(str), 1); show_mouseptr(inst, 0); term_seen_key_event(inst->term); } #endif gboolean button_internal(struct gui_data *inst, guint32 timestamp, GdkEventType type, guint ebutton, guint state, gdouble ex, gdouble ey) { int shift, ctrl, alt, x, y, button, act; |
︙ | ︙ | |||
1162 1163 1164 1165 1166 1167 1168 | case GDK_BUTTON_PRESS: act = MA_CLICK; break; case GDK_BUTTON_RELEASE: act = MA_RELEASE; break; case GDK_2BUTTON_PRESS: act = MA_2CLK; break; case GDK_3BUTTON_PRESS: act = MA_3CLK; break; default: return FALSE; /* don't know this event type */ } | | > | | | 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 | case GDK_BUTTON_PRESS: act = MA_CLICK; break; case GDK_BUTTON_RELEASE: act = MA_RELEASE; break; case GDK_2BUTTON_PRESS: act = MA_2CLK; break; case GDK_3BUTTON_PRESS: act = MA_3CLK; break; default: return FALSE; /* don't know this event type */ } if (send_raw_mouse && !(shift && conf_get_int(inst->conf, CONF_mouse_override)) && act != MA_CLICK && act != MA_RELEASE) return TRUE; /* we ignore these in raw mouse mode */ x = (ex - inst->window_border) / inst->font_width; y = (ey - inst->window_border) / inst->font_height; term_mouse(inst->term, button, translate_button(button), act, x, y, shift, ctrl, alt); return TRUE; } |
︙ | ︙ | |||
1227 1228 1229 1230 1231 1232 1233 | else if (event->state & GDK_BUTTON2_MASK) button = MBT_MIDDLE; else if (event->state & GDK_BUTTON3_MASK) button = MBT_RIGHT; else return FALSE; /* don't even know what button! */ | | | | | | | > | | < | | | | | < < < < > > | > | > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | > > > > > | | | | 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 | else if (event->state & GDK_BUTTON2_MASK) button = MBT_MIDDLE; else if (event->state & GDK_BUTTON3_MASK) button = MBT_RIGHT; else return FALSE; /* don't even know what button! */ x = (event->x - inst->window_border) / inst->font_width; y = (event->y - inst->window_border) / inst->font_height; term_mouse(inst->term, button, translate_button(button), MA_DRAG, x, y, shift, ctrl, alt); return TRUE; } void frontend_keypress(void *handle) { struct gui_data *inst = (struct gui_data *)handle; /* * If our child process has exited but not closed, terminate on * any keypress. */ if (inst->exited) cleanup_exit(0); } static void exit_callback(void *vinst) { struct gui_data *inst = (struct gui_data *)vinst; int exitcode, close_on_exit; if (!inst->exited && (exitcode = inst->back->exitcode(inst->backhandle)) >= 0) { inst->exited = TRUE; close_on_exit = conf_get_int(inst->conf, CONF_close_on_exit); if (close_on_exit == FORCE_ON || (close_on_exit == AUTO && exitcode == 0)) gtk_main_quit(); /* just go */ if (inst->ldisc) { ldisc_free(inst->ldisc); inst->ldisc = NULL; } inst->back->free(inst->backhandle); inst->backhandle = NULL; inst->back = NULL; term_provide_resize_fn(inst->term, NULL, NULL); update_specials_menu(inst); gtk_widget_set_sensitive(inst->restartitem, TRUE); } } void notify_remote_exit(void *frontend) { struct gui_data *inst = (struct gui_data *)frontend; queue_toplevel_callback(exit_callback, inst); } static void notify_toplevel_callback(void *frontend); static gint quit_toplevel_callback_func(gpointer data) { struct gui_data *inst = (struct gui_data *)data; notify_toplevel_callback(inst); return 0; } static gint idle_toplevel_callback_func(gpointer data) { struct gui_data *inst = (struct gui_data *)data; if (gtk_main_level() > 1) { /* * We don't run the callbacks if we're in the middle of a * subsidiary gtk_main. Instead, ask for a callback when we * get back out of the subsidiary main loop, so we can * reschedule ourself then. */ gtk_quit_add(2, quit_toplevel_callback_func, inst); } else { run_toplevel_callbacks(); } if (!toplevel_callback_pending()) gtk_idle_remove(inst->toplevel_callback_idle_id); return TRUE; } static void notify_toplevel_callback(void *frontend) { struct gui_data *inst = (struct gui_data *)frontend; inst->toplevel_callback_idle_id = gtk_idle_add(idle_toplevel_callback_func, inst); } static gint timer_trigger(gpointer data) { unsigned long now = GPOINTER_TO_LONG(data); unsigned long next, then; long ticks; if (run_timers(now, &next)) { then = now; now = GETTICKCOUNT(); if (now - then > next - then) ticks = 0; else ticks = next - now; timer_id = gtk_timeout_add(ticks, timer_trigger, LONG_TO_GPOINTER(next)); } /* * Never let a timer resume. If we need another one, we've * asked for it explicitly above. */ return FALSE; } void timer_change_notify(unsigned long next) { long ticks; if (timer_id) gtk_timeout_remove(timer_id); ticks = next - GETTICKCOUNT(); |
︙ | ︙ | |||
1360 1361 1362 1363 1364 1365 1366 | /* * set or clear the "raw mouse message" mode */ void set_raw_mouse_mode(void *frontend, int activate) { struct gui_data *inst = (struct gui_data *)frontend; | | | 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 | /* * set or clear the "raw mouse message" mode */ void set_raw_mouse_mode(void *frontend, int activate) { struct gui_data *inst = (struct gui_data *)frontend; activate = activate && !conf_get_int(inst->conf, CONF_no_mouse_rep); send_raw_mouse = activate; update_mouseptr(inst); } void request_resize(void *frontend, int w, int h) { struct gui_data *inst = (struct gui_data *)frontend; |
︙ | ︙ | |||
1408 1409 1410 1411 1412 1413 1414 | #endif gtk_widget_size_request(inst->area, &inner); gtk_widget_size_request(inst->window, &outer); offset_x = outer.width - inner.width; offset_y = outer.height - inner.height; | | | | 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 | #endif gtk_widget_size_request(inst->area, &inner); gtk_widget_size_request(inst->window, &outer); offset_x = outer.width - inner.width; offset_y = outer.height - inner.height; area_x = inst->font_width * w + 2*inst->window_border; area_y = inst->font_height * h + 2*inst->window_border; /* * Now we must set the size request on the drawing area back to * something sensible before we commit the real resize. Best * way to do this, I think, is to set it to what the size is * really going to end up being. */ |
︙ | ︙ | |||
1476 1477 1478 1479 1480 1481 1482 | } void palette_set(void *frontend, int n, int r, int g, int b) { struct gui_data *inst = (struct gui_data *)frontend; if (n >= 16) n += 256 - 16; | | | | > | > | > | 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 | } void palette_set(void *frontend, int n, int r, int g, int b) { struct gui_data *inst = (struct gui_data *)frontend; if (n >= 16) n += 256 - 16; if (n >= NALLCOLOURS) return; real_palette_set(inst, n, r, g, b); if (n == 258) { /* Default Background changed. Ensure space between text area and * window border is redrawn */ set_window_background(inst); draw_backing_rect(inst); gtk_widget_queue_draw(inst->area); } } void palette_reset(void *frontend) { struct gui_data *inst = (struct gui_data *)frontend; /* This maps colour indices in inst->conf to those used in inst->cols. */ static const int ww[] = { 256, 257, 258, 259, 260, 261, 0, 8, 1, 9, 2, 10, 3, 11, 4, 12, 5, 13, 6, 14, 7, 15 }; gboolean success[NALLCOLOURS]; int i; assert(lenof(ww) == NCFGCOLOURS); if (!inst->colmap) { inst->colmap = gdk_colormap_get_system(); } else { gdk_colormap_free_colors(inst->colmap, inst->cols, NALLCOLOURS); } for (i = 0; i < NCFGCOLOURS; i++) { inst->cols[ww[i]].red = conf_get_int_int(inst->conf, CONF_colours, i*3+0) * 0x0101; inst->cols[ww[i]].green = conf_get_int_int(inst->conf, CONF_colours, i*3+1) * 0x0101; inst->cols[ww[i]].blue = conf_get_int_int(inst->conf, CONF_colours, i*3+2) * 0x0101; } for (i = 0; i < NEXTCOLOURS; i++) { if (i < 216) { 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; |
︙ | ︙ | |||
1533 1534 1535 1536 1537 1538 1539 | } gdk_colormap_alloc_colors(inst->colmap, inst->cols, NALLCOLOURS, FALSE, TRUE, success); for (i = 0; i < NALLCOLOURS; i++) { if (!success[i]) g_error("%s: couldn't allocate colour %d (#%02x%02x%02x)\n", | | | > > | 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 | } gdk_colormap_alloc_colors(inst->colmap, inst->cols, NALLCOLOURS, FALSE, TRUE, success); for (i = 0; i < NALLCOLOURS; i++) { if (!success[i]) g_error("%s: couldn't allocate colour %d (#%02x%02x%02x)\n", appname, i, conf_get_int_int(inst->conf, CONF_colours, i*3+0), conf_get_int_int(inst->conf, CONF_colours, i*3+1), conf_get_int_int(inst->conf, CONF_colours, i*3+2)); } /* Since Default Background may have changed, ensure that space * between text area and window border is refreshed. */ set_window_background(inst); if (inst->area && inst->area->window) { draw_backing_rect(inst); |
︙ | ︙ | |||
1607 1608 1609 1610 1611 1612 1613 | sfree(inst->pasteout_data_utf8); /* * Set up UTF-8 and compound text paste data. This only happens * if we aren't in direct-to-font mode using the D800 hack. */ if (!inst->direct_to_font) { | | | 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 | sfree(inst->pasteout_data_utf8); /* * Set up UTF-8 and compound text paste data. This only happens * if we aren't in direct-to-font mode using the D800 hack. */ if (!inst->direct_to_font) { const wchar_t *tmp = data; int tmplen = len; XTextProperty tp; char *list[1]; inst->pasteout_data_utf8 = snewn(len*6, char); inst->pasteout_data_utf8_len = len*6; inst->pasteout_data_utf8_len = |
︙ | ︙ | |||
1854 1855 1856 1857 1858 1859 1860 | inst->pastein_data_len = length; inst->pastein_data_len = mb_to_wc(charset, 0, text, length, inst->pastein_data, inst->pastein_data_len); term_do_paste(inst->term); | < < < < < < < < < < < < < < < < | | | | > > > > > > > > > | > | | | 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 2060 2061 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 | inst->pastein_data_len = length; inst->pastein_data_len = mb_to_wc(charset, 0, text, length, inst->pastein_data, inst->pastein_data_len); term_do_paste(inst->term); if (free_list_required) XFreeStringList(list); if (free_required) XFree(text); } void get_clip(void *frontend, wchar_t ** p, int *len) { struct gui_data *inst = (struct gui_data *)frontend; if (p) { *p = inst->pastein_data; *len = inst->pastein_data_len; } } static void set_window_titles(struct gui_data *inst) { /* * We must always call set_icon_name after calling set_title, * since set_title will write both names. Irritating, but such * is life. */ gtk_window_set_title(GTK_WINDOW(inst->window), inst->wintitle); if (!conf_get_int(inst->conf, CONF_win_name_always)) gdk_window_set_icon_name(inst->window->window, inst->icontitle); } void set_title(void *frontend, char *title) { struct gui_data *inst = (struct gui_data *)frontend; sfree(inst->wintitle); inst->wintitle = dupstr(title); set_window_titles(inst); } void set_icon(void *frontend, char *title) { struct gui_data *inst = (struct gui_data *)frontend; sfree(inst->icontitle); inst->icontitle = dupstr(title); set_window_titles(inst); } void set_title_and_icon(void *frontend, char *title, char *icon) { struct gui_data *inst = (struct gui_data *)frontend; sfree(inst->wintitle); inst->wintitle = dupstr(title); sfree(inst->icontitle); inst->icontitle = dupstr(icon); set_window_titles(inst); } void set_sbar(void *frontend, int total, int start, int page) { struct gui_data *inst = (struct gui_data *)frontend; if (!conf_get_int(inst->conf, CONF_scrollbar)) return; inst->sbar_adjust->lower = 0; inst->sbar_adjust->upper = total; inst->sbar_adjust->value = start; inst->sbar_adjust->page_size = page; inst->sbar_adjust->step_increment = 1; inst->sbar_adjust->page_increment = page/2; inst->ignore_sbar = TRUE; gtk_adjustment_changed(inst->sbar_adjust); inst->ignore_sbar = FALSE; } void scrollbar_moved(GtkAdjustment *adj, gpointer data) { struct gui_data *inst = (struct gui_data *)data; if (!conf_get_int(inst->conf, CONF_scrollbar)) return; if (!inst->ignore_sbar) term_scroll(inst->term, 1, (int)adj->value); } void sys_cursor(void *frontend, int x, int y) { |
︙ | ︙ | |||
2023 2024 2025 2026 2027 2028 2029 | nfg = ((monochrome ? ATTR_DEFFG : (attr & ATTR_FGMASK)) >> ATTR_FGSHIFT); nbg = ((monochrome ? ATTR_DEFBG : (attr & ATTR_BGMASK)) >> ATTR_BGSHIFT); if (!!(attr & ATTR_REVERSE) ^ (monochrome && (attr & TATTR_ACTCURS))) { t = nfg; nfg = nbg; nbg = t; } | | | | | 2175 2176 2177 2178 2179 2180 2181 2182 2183 2184 2185 2186 2187 2188 2189 2190 2191 2192 2193 2194 2195 2196 2197 2198 2199 2200 2201 2202 2203 2204 2205 2206 2207 2208 2209 2210 2211 | nfg = ((monochrome ? ATTR_DEFFG : (attr & ATTR_FGMASK)) >> ATTR_FGSHIFT); nbg = ((monochrome ? ATTR_DEFBG : (attr & ATTR_BGMASK)) >> ATTR_BGSHIFT); if (!!(attr & ATTR_REVERSE) ^ (monochrome && (attr & TATTR_ACTCURS))) { t = nfg; nfg = nbg; nbg = t; } if ((inst->bold_style & 2) && (attr & ATTR_BOLD)) { if (nfg < 16) nfg |= 8; else if (nfg >= 256) nfg |= 1; } if ((inst->bold_style & 2) && (attr & ATTR_BLINK)) { if (nbg < 16) nbg |= 8; else if (nbg >= 256) nbg |= 1; } if ((attr & TATTR_ACTCURS) && !monochrome) { nfg = 260; nbg = 261; } fontid = shadow = 0; if (attr & ATTR_WIDE) { widefactor = 2; fontid |= 2; } else { widefactor = 1; } if ((attr & ATTR_BOLD) && (inst->bold_style & 1)) { bold = 1; fontid |= 1; } else { bold = 0; } if (!inst->fonts[fontid]) { |
︙ | ︙ | |||
2082 2083 2084 2085 2086 2087 2088 | rlen = len * 2; } else rlen = len; { GdkRectangle r; | | | | | < < < < < < < < < < < | < < < | | | | < | < | | | | | | | | | | | | | 2234 2235 2236 2237 2238 2239 2240 2241 2242 2243 2244 2245 2246 2247 2248 2249 2250 2251 2252 2253 2254 2255 2256 2257 2258 2259 2260 2261 2262 2263 2264 2265 2266 2267 2268 2269 2270 2271 2272 2273 2274 2275 2276 2277 2278 2279 2280 2281 2282 2283 2284 2285 2286 2287 2288 2289 2290 2291 2292 2293 2294 2295 2296 2297 2298 2299 2300 2301 2302 2303 2304 2305 2306 2307 2308 2309 2310 2311 | rlen = len * 2; } else rlen = len; { GdkRectangle r; r.x = x*inst->font_width+inst->window_border; r.y = y*inst->font_height+inst->window_border; r.width = rlen*widefactor*inst->font_width; r.height = inst->font_height; gdk_gc_set_clip_rectangle(gc, &r); } gdk_gc_set_foreground(gc, &inst->cols[nbg]); gdk_draw_rectangle(inst->pixmap, gc, 1, x*inst->font_width+inst->window_border, y*inst->font_height+inst->window_border, rlen*widefactor*inst->font_width, inst->font_height); gdk_gc_set_foreground(gc, &inst->cols[nfg]); for (combining = 0; combining < ncombining; combining++) { unifont_draw_text(inst->pixmap, gc, inst->fonts[fontid], x*inst->font_width+inst->window_border, y*inst->font_height+inst->window_border+inst->fonts[0]->ascent, text + combining, len, widefactor > 1, bold, inst->font_width); } if (attr & ATTR_UNDER) { int uheight = inst->fonts[0]->ascent + 1; if (uheight >= inst->font_height) uheight = inst->font_height - 1; gdk_draw_line(inst->pixmap, gc, x*inst->font_width+inst->window_border, y*inst->font_height + uheight + inst->window_border, (x+len)*widefactor*inst->font_width-1+inst->window_border, y*inst->font_height + uheight + inst->window_border); } if ((lattr & LATTR_MODE) != LATTR_NORM) { /* * I can't find any plausible StretchBlt equivalent in the * X server, so I'm going to do this the slow and painful * way. This will involve repeated calls to * gdk_draw_pixmap() to stretch the text horizontally. It's * O(N^2) in time and O(N) in network bandwidth, but you * try thinking of a better way. :-( */ int i; for (i = 0; i < len * widefactor * inst->font_width; i++) { gdk_draw_pixmap(inst->pixmap, gc, inst->pixmap, x*inst->font_width+inst->window_border + 2*i, y*inst->font_height+inst->window_border, x*inst->font_width+inst->window_border + 2*i+1, y*inst->font_height+inst->window_border, len * widefactor * inst->font_width - i, inst->font_height); } len *= 2; if ((lattr & LATTR_MODE) != LATTR_WIDE) { int dt, db; /* Now stretch vertically, in the same way. */ if ((lattr & LATTR_MODE) == LATTR_BOT) dt = 0, db = 1; else dt = 1, db = 0; for (i = 0; i < inst->font_height; i+=2) { gdk_draw_pixmap(inst->pixmap, gc, inst->pixmap, x*inst->font_width+inst->window_border, y*inst->font_height+inst->window_border+dt*i+db, x*inst->font_width+inst->window_border, y*inst->font_height+inst->window_border+dt*(i+1), len * widefactor * inst->font_width, inst->font_height-i-1); } } } } void do_text(Context ctx, int x, int y, wchar_t *text, int len, |
︙ | ︙ | |||
2194 2195 2196 2197 2198 2199 2200 | return; if (x + len*2*widefactor > inst->term->cols) len = (inst->term->cols-x)/2/widefactor;/* trim to LH half */ len *= 2; } gdk_draw_pixmap(inst->area->window, gc, inst->pixmap, | | | | | | | 2330 2331 2332 2333 2334 2335 2336 2337 2338 2339 2340 2341 2342 2343 2344 2345 2346 2347 2348 2349 2350 2351 2352 2353 2354 2355 2356 2357 2358 2359 2360 2361 2362 2363 2364 2365 | return; if (x + len*2*widefactor > inst->term->cols) len = (inst->term->cols-x)/2/widefactor;/* trim to LH half */ len *= 2; } gdk_draw_pixmap(inst->area->window, gc, inst->pixmap, x*inst->font_width+inst->window_border, y*inst->font_height+inst->window_border, x*inst->font_width+inst->window_border, y*inst->font_height+inst->window_border, len*widefactor*inst->font_width, inst->font_height); } void do_cursor(Context ctx, int x, int y, wchar_t *text, int len, unsigned long attr, int lattr) { struct draw_ctx *dctx = (struct draw_ctx *)ctx; struct gui_data *inst = dctx->inst; GdkGC *gc = dctx->gc; int active, passive, widefactor; if (attr & TATTR_PASCURS) { attr &= ~TATTR_PASCURS; passive = 1; } else passive = 0; if ((attr & TATTR_ACTCURS) && inst->cursor_type != 0) { attr &= ~TATTR_ACTCURS; active = 1; } else active = 0; do_text_internal(ctx, x, y, text, len, attr, lattr); if (attr & TATTR_COMBINING) |
︙ | ︙ | |||
2240 2241 2242 2243 2244 2245 2246 | if (x >= inst->term->cols) return; if (x + len*2*widefactor > inst->term->cols) len = (inst->term->cols-x)/2/widefactor;/* trim to LH half */ len *= 2; } | | | | | | | | | | 2376 2377 2378 2379 2380 2381 2382 2383 2384 2385 2386 2387 2388 2389 2390 2391 2392 2393 2394 2395 2396 2397 2398 2399 2400 2401 2402 2403 2404 2405 2406 2407 2408 2409 2410 2411 2412 2413 2414 2415 2416 2417 2418 2419 2420 2421 2422 2423 2424 2425 2426 2427 2428 2429 | if (x >= inst->term->cols) return; if (x + len*2*widefactor > inst->term->cols) len = (inst->term->cols-x)/2/widefactor;/* trim to LH half */ len *= 2; } if (inst->cursor_type == 0) { /* * An active block cursor will already have been done by * the above do_text call, so we only need to do anything * if it's passive. */ if (passive) { gdk_gc_set_foreground(gc, &inst->cols[261]); gdk_draw_rectangle(inst->pixmap, gc, 0, x*inst->font_width+inst->window_border, y*inst->font_height+inst->window_border, len*widefactor*inst->font_width-1, inst->font_height-1); } } else { int uheight; int startx, starty, dx, dy, length, i; int char_width; if ((attr & ATTR_WIDE) || (lattr & LATTR_MODE) != LATTR_NORM) char_width = 2*inst->font_width; else char_width = inst->font_width; if (inst->cursor_type == 1) { uheight = inst->fonts[0]->ascent + 1; if (uheight >= inst->font_height) uheight = inst->font_height - 1; startx = x * inst->font_width + inst->window_border; starty = y * inst->font_height + inst->window_border + uheight; dx = 1; dy = 0; length = len * widefactor * char_width; } else { int xadjust = 0; if (attr & TATTR_RIGHTCURS) xadjust = char_width - 1; startx = x * inst->font_width + inst->window_border + xadjust; starty = y * inst->font_height + inst->window_border; dx = 0; dy = 1; length = inst->font_height; } gdk_gc_set_foreground(gc, &inst->cols[261]); if (passive) { |
︙ | ︙ | |||
2301 2302 2303 2304 2305 2306 2307 | } else if (active) { gdk_draw_line(inst->pixmap, gc, startx, starty, startx + (length-1) * dx, starty + (length-1) * dy); } /* else no cursor (e.g., blinked off) */ } gdk_draw_pixmap(inst->area->window, gc, inst->pixmap, | | | | | > > > > > > > > > > > | 2437 2438 2439 2440 2441 2442 2443 2444 2445 2446 2447 2448 2449 2450 2451 2452 2453 2454 2455 2456 2457 2458 2459 2460 2461 2462 2463 2464 2465 2466 | } else if (active) { gdk_draw_line(inst->pixmap, gc, startx, starty, startx + (length-1) * dx, starty + (length-1) * dy); } /* else no cursor (e.g., blinked off) */ } gdk_draw_pixmap(inst->area->window, gc, inst->pixmap, x*inst->font_width+inst->window_border, y*inst->font_height+inst->window_border, x*inst->font_width+inst->window_border, y*inst->font_height+inst->window_border, len*widefactor*inst->font_width, inst->font_height); #if GTK_CHECK_VERSION(2,0,0) { GdkRectangle cursorrect; cursorrect.x = x*inst->font_width+inst->window_border; cursorrect.y = y*inst->font_height+inst->window_border; cursorrect.width = len*widefactor*inst->font_width; cursorrect.height = inst->font_height; gtk_im_context_set_cursor_location(inst->imc, &cursorrect); } #endif } GdkCursor *make_mouse_ptr(struct gui_data *inst, int cursor_val) { /* * Truly hideous hack: GTK doesn't allow us to set the mouse * cursor foreground and background colours unless we've _also_ |
︙ | ︙ | |||
2462 2463 2464 2465 2466 2467 2468 2469 2470 | " -xrm RESOURCE-STRING Set an X resource\n" " -e COMMAND [ARGS...] Execute command (consumes all remaining args)\n" ) < 0 || fflush(fp) < 0) { perror("output error"); exit(1); } } int do_cmdline(int argc, char **argv, int do_everything, int *allow_launch, | > > > > > > > | | 2609 2610 2611 2612 2613 2614 2615 2616 2617 2618 2619 2620 2621 2622 2623 2624 2625 2626 2627 2628 2629 2630 2631 2632 | " -xrm RESOURCE-STRING Set an X resource\n" " -e COMMAND [ARGS...] Execute command (consumes all remaining args)\n" ) < 0 || fflush(fp) < 0) { perror("output error"); exit(1); } } static void version(FILE *fp) { if(fprintf(fp, "%s: %s\n", appname, ver) < 0 || fflush(fp) < 0) { perror("output error"); exit(1); } } int do_cmdline(int argc, char **argv, int do_everything, int *allow_launch, struct gui_data *inst, Conf *conf) { int err = 0; char *val; /* * Macros to make argument handling easier. Note that because * they need to call `continue', they cannot be contained in |
︙ | ︙ | |||
2503 2504 2505 2506 2507 2508 2509 | * only, we convert -T into -title. */ if ((cmdline_tooltype & TOOLTYPE_NONNETWORK) && !strcmp(p, "-T")) p = "-title"; ret = cmdline_process_param(p, (argc > 1 ? argv[1] : NULL), | | > | > | > | | > > | | > > > | | | < | | | | 2657 2658 2659 2660 2661 2662 2663 2664 2665 2666 2667 2668 2669 2670 2671 2672 2673 2674 2675 2676 2677 2678 2679 2680 2681 2682 2683 2684 2685 2686 2687 2688 2689 2690 2691 2692 2693 2694 2695 2696 2697 2698 2699 2700 2701 2702 2703 2704 2705 2706 2707 2708 2709 2710 2711 2712 2713 2714 2715 2716 2717 2718 2719 2720 2721 2722 2723 2724 2725 2726 2727 2728 2729 2730 2731 2732 2733 2734 2735 2736 2737 2738 2739 2740 2741 2742 | * only, we convert -T into -title. */ if ((cmdline_tooltype & TOOLTYPE_NONNETWORK) && !strcmp(p, "-T")) p = "-title"; ret = cmdline_process_param(p, (argc > 1 ? argv[1] : NULL), do_everything ? 1 : -1, conf); if (ret == -2) { cmdline_error("option \"%s\" requires an argument", p); } else if (ret == 2) { --argc, ++argv; /* skip next argument */ continue; } else if (ret == 1) { continue; } if (!strcmp(p, "-fn") || !strcmp(p, "-font")) { FontSpec *fs; EXPECTS_ARG; SECOND_PASS_ONLY; fs = fontspec_new(val); conf_set_fontspec(conf, CONF_font, fs); fontspec_free(fs); } else if (!strcmp(p, "-fb")) { FontSpec *fs; EXPECTS_ARG; SECOND_PASS_ONLY; fs = fontspec_new(val); conf_set_fontspec(conf, CONF_boldfont, fs); fontspec_free(fs); } else if (!strcmp(p, "-fw")) { FontSpec *fs; EXPECTS_ARG; SECOND_PASS_ONLY; fs = fontspec_new(val); conf_set_fontspec(conf, CONF_widefont, fs); fontspec_free(fs); } else if (!strcmp(p, "-fwb")) { FontSpec *fs; EXPECTS_ARG; SECOND_PASS_ONLY; fs = fontspec_new(val); conf_set_fontspec(conf, CONF_wideboldfont, fs); fontspec_free(fs); } else if (!strcmp(p, "-cs")) { EXPECTS_ARG; SECOND_PASS_ONLY; conf_set_str(conf, CONF_line_codepage, val); } else if (!strcmp(p, "-geometry")) { int flags, x, y; unsigned int w, h; EXPECTS_ARG; SECOND_PASS_ONLY; flags = XParseGeometry(val, &x, &y, &w, &h); if (flags & WidthValue) conf_set_int(conf, CONF_width, w); if (flags & HeightValue) conf_set_int(conf, CONF_height, h); if (flags & (XValue | YValue)) { inst->xpos = x; inst->ypos = y; inst->gotpos = TRUE; inst->gravity = ((flags & XNegative ? 1 : 0) | (flags & YNegative ? 2 : 0)); } } else if (!strcmp(p, "-sl")) { EXPECTS_ARG; SECOND_PASS_ONLY; conf_set_int(conf, CONF_savelines, atoi(val)); } else if (!strcmp(p, "-fg") || !strcmp(p, "-bg") || !strcmp(p, "-bfg") || !strcmp(p, "-bbg") || !strcmp(p, "-cfg") || !strcmp(p, "-cbg")) { GdkColor col; EXPECTS_ARG; |
︙ | ︙ | |||
2589 2590 2591 2592 2593 2594 2595 | index = (!strcmp(p, "-fg") ? 0 : !strcmp(p, "-bg") ? 2 : !strcmp(p, "-bfg") ? 1 : !strcmp(p, "-bbg") ? 3 : !strcmp(p, "-cfg") ? 4 : !strcmp(p, "-cbg") ? 5 : -1); assert(index != -1); | | | | | 2750 2751 2752 2753 2754 2755 2756 2757 2758 2759 2760 2761 2762 2763 2764 2765 2766 | index = (!strcmp(p, "-fg") ? 0 : !strcmp(p, "-bg") ? 2 : !strcmp(p, "-bfg") ? 1 : !strcmp(p, "-bbg") ? 3 : !strcmp(p, "-cfg") ? 4 : !strcmp(p, "-cbg") ? 5 : -1); assert(index != -1); conf_set_int_int(conf, CONF_colours, index*3+0, col.red / 256); conf_set_int_int(conf, CONF_colours, index*3+1,col.green/ 256); conf_set_int_int(conf, CONF_colours, index*3+2, col.blue/ 256); } } else if (use_pty_argv && !strcmp(p, "-e")) { /* This option swallows all further arguments. */ if (!do_everything) break; |
︙ | ︙ | |||
2614 2615 2616 2617 2618 2619 2620 | } else err = 1, fprintf(stderr, "%s: -e expects an argument\n", appname); } else if (!strcmp(p, "-title")) { EXPECTS_ARG; SECOND_PASS_ONLY; | | < > | | | > | | | | | | | > > > > | | 2775 2776 2777 2778 2779 2780 2781 2782 2783 2784 2785 2786 2787 2788 2789 2790 2791 2792 2793 2794 2795 2796 2797 2798 2799 2800 2801 2802 2803 2804 2805 2806 2807 2808 2809 2810 2811 2812 2813 2814 2815 2816 2817 2818 2819 2820 2821 2822 2823 2824 2825 2826 2827 2828 2829 2830 2831 2832 2833 2834 2835 2836 2837 2838 2839 2840 2841 2842 2843 2844 2845 2846 2847 2848 2849 | } else err = 1, fprintf(stderr, "%s: -e expects an argument\n", appname); } else if (!strcmp(p, "-title")) { EXPECTS_ARG; SECOND_PASS_ONLY; conf_set_str(conf, CONF_wintitle, val); } else if (!strcmp(p, "-log")) { Filename *fn; EXPECTS_ARG; SECOND_PASS_ONLY; fn = filename_from_str(val); conf_set_filename(conf, CONF_logfilename, fn); conf_set_int(conf, CONF_logtype, LGTYP_DEBUG); filename_free(fn); } else if (!strcmp(p, "-ut-") || !strcmp(p, "+ut")) { SECOND_PASS_ONLY; conf_set_int(conf, CONF_stamp_utmp, 0); } else if (!strcmp(p, "-ut")) { SECOND_PASS_ONLY; conf_set_int(conf, CONF_stamp_utmp, 1); } else if (!strcmp(p, "-ls-") || !strcmp(p, "+ls")) { SECOND_PASS_ONLY; conf_set_int(conf, CONF_login_shell, 0); } else if (!strcmp(p, "-ls")) { SECOND_PASS_ONLY; conf_set_int(conf, CONF_login_shell, 1); } else if (!strcmp(p, "-nethack")) { SECOND_PASS_ONLY; conf_set_int(conf, CONF_nethack_keypad, 1); } else if (!strcmp(p, "-sb-") || !strcmp(p, "+sb")) { SECOND_PASS_ONLY; conf_set_int(conf, CONF_scrollbar, 0); } else if (!strcmp(p, "-sb")) { SECOND_PASS_ONLY; conf_set_int(conf, CONF_scrollbar, 1); } else if (!strcmp(p, "-name")) { EXPECTS_ARG; app_name = val; } else if (!strcmp(p, "-xrm")) { EXPECTS_ARG; provide_xrm_string(val); } else if(!strcmp(p, "-help") || !strcmp(p, "--help")) { help(stdout); exit(0); } else if(!strcmp(p, "-version") || !strcmp(p, "--version")) { version(stdout); exit(0); } else if (!strcmp(p, "-pgpfp")) { pgp_fingerprints(); exit(1); } else if(p[0] != '-' && (!do_everything || process_nonoption_arg(p, conf, allow_launch))) { /* do nothing */ } else { err = 1; fprintf(stderr, "%s: unrecognized option '%s'\n", appname, p); } |
︙ | ︙ | |||
2695 2696 2697 2698 2699 2700 2701 | return gdk_input_add(fd, flags, fd_input_func, NULL); } void uxsel_input_remove(int id) { gdk_input_remove(id); } | | | | | < | < | < | | | < | | < < > | | | < | < | > > | < < > | | < | < | > > > | < < | > | | | < | < | > > > | > > | < > > > > | > > > > | > | > | > | | | | | 2861 2862 2863 2864 2865 2866 2867 2868 2869 2870 2871 2872 2873 2874 2875 2876 2877 2878 2879 2880 2881 2882 2883 2884 2885 2886 2887 2888 2889 2890 2891 2892 2893 2894 2895 2896 2897 2898 2899 2900 2901 2902 2903 2904 2905 2906 2907 2908 2909 2910 2911 2912 2913 2914 2915 2916 2917 2918 2919 2920 2921 2922 2923 2924 2925 2926 2927 2928 2929 2930 2931 2932 2933 2934 2935 2936 2937 2938 2939 2940 2941 2942 2943 2944 2945 2946 2947 2948 2949 2950 2951 2952 2953 2954 2955 2956 2957 2958 2959 2960 2961 | return gdk_input_add(fd, flags, fd_input_func, NULL); } void uxsel_input_remove(int id) { gdk_input_remove(id); } char *setup_fonts_ucs(struct gui_data *inst) { int shadowbold = conf_get_int(inst->conf, CONF_shadowbold); int shadowboldoffset = conf_get_int(inst->conf, CONF_shadowboldoffset); FontSpec *fs; unifont *fonts[4]; int i; fs = conf_get_fontspec(inst->conf, CONF_font); fonts[0] = multifont_create(inst->area, fs->name, FALSE, FALSE, shadowboldoffset, shadowbold); if (!fonts[0]) { return dupprintf("unable to load font \"%s\"", fs->name); } fs = conf_get_fontspec(inst->conf, CONF_boldfont); if (shadowbold || !fs->name[0]) { fonts[1] = NULL; } else { fonts[1] = multifont_create(inst->area, fs->name, FALSE, TRUE, shadowboldoffset, shadowbold); if (!fonts[1]) { if (fonts[0]) unifont_destroy(fonts[0]); return dupprintf("unable to load bold font \"%s\"", fs->name); } } fs = conf_get_fontspec(inst->conf, CONF_widefont); if (fs->name[0]) { fonts[2] = multifont_create(inst->area, fs->name, TRUE, FALSE, shadowboldoffset, shadowbold); if (!fonts[2]) { for (i = 0; i < 2; i++) if (fonts[i]) unifont_destroy(fonts[i]); return dupprintf("unable to load wide font \"%s\"", fs->name); } } else { fonts[2] = NULL; } fs = conf_get_fontspec(inst->conf, CONF_wideboldfont); if (shadowbold || !fs->name[0]) { fonts[3] = NULL; } else { fonts[3] = multifont_create(inst->area, fs->name, TRUE, TRUE, shadowboldoffset, shadowbold); if (!fonts[3]) { for (i = 0; i < 3; i++) if (fonts[i]) unifont_destroy(fonts[i]); return dupprintf("unable to load wide bold font \"%s\"", fs->name); } } /* * Now we've got past all the possible error conditions, we can * actually update our state. */ for (i = 0; i < 4; i++) { if (inst->fonts[i]) unifont_destroy(inst->fonts[i]); inst->fonts[i] = fonts[i]; } inst->font_width = inst->fonts[0]->width; inst->font_height = inst->fonts[0]->height; inst->direct_to_font = init_ucs(&inst->ucsdata, conf_get_str(inst->conf, CONF_line_codepage), conf_get_int(inst->conf, CONF_utf8_override), inst->fonts[0]->public_charset, conf_get_int(inst->conf, CONF_vtmode)); return NULL; } void set_geom_hints(struct gui_data *inst) { GdkGeometry geom; geom.min_width = inst->font_width + 2*inst->window_border; geom.min_height = inst->font_height + 2*inst->window_border; geom.max_width = geom.max_height = -1; geom.base_width = 2*inst->window_border; geom.base_height = 2*inst->window_border; geom.width_inc = inst->font_width; geom.height_inc = inst->font_height; geom.min_aspect = geom.max_aspect = 0; gtk_window_set_geometry_hints(GTK_WINDOW(inst->window), inst->area, &geom, GDK_HINT_MIN_SIZE | GDK_HINT_BASE_SIZE | GDK_HINT_RESIZE_INC); } |
︙ | ︙ | |||
2827 2828 2829 2830 2831 2832 2833 | { struct gui_data *inst = (struct gui_data *)data; showeventlog(inst->eventlogstuff, inst->window); } void change_settings_menuitem(GtkMenuItem *item, gpointer data) { | | | | | > | > > | | < < | | > > | | > > | | | | < > > | > | | | | > | | | > | > | > | > | > | > | > | > | > > | > > | > | > > > | > > > > > > > > > | | | > > > > | > > > | | > > | | > > > > | 3000 3001 3002 3003 3004 3005 3006 3007 3008 3009 3010 3011 3012 3013 3014 3015 3016 3017 3018 3019 3020 3021 3022 3023 3024 3025 3026 3027 3028 3029 3030 3031 3032 3033 3034 3035 3036 3037 3038 3039 3040 3041 3042 3043 3044 3045 3046 3047 3048 3049 3050 3051 3052 3053 3054 3055 3056 3057 3058 3059 3060 3061 3062 3063 3064 3065 3066 3067 3068 3069 3070 3071 3072 3073 3074 3075 3076 3077 3078 3079 3080 3081 3082 3083 3084 3085 3086 3087 3088 3089 3090 3091 3092 3093 3094 3095 3096 3097 3098 3099 3100 3101 3102 3103 3104 3105 3106 3107 3108 3109 3110 3111 3112 3113 3114 3115 3116 3117 3118 3119 3120 3121 3122 3123 3124 3125 3126 3127 3128 3129 3130 3131 3132 3133 3134 3135 3136 3137 3138 3139 3140 3141 3142 3143 3144 3145 3146 3147 3148 3149 3150 3151 3152 3153 3154 3155 3156 3157 3158 3159 3160 3161 3162 3163 3164 3165 3166 3167 3168 3169 3170 3171 3172 3173 3174 3175 3176 3177 3178 3179 3180 3181 3182 3183 3184 3185 3186 3187 3188 3189 3190 3191 3192 | { struct gui_data *inst = (struct gui_data *)data; showeventlog(inst->eventlogstuff, inst->window); } void change_settings_menuitem(GtkMenuItem *item, gpointer data) { /* This maps colour indices in inst->conf to those used in inst->cols. */ static const int ww[] = { 256, 257, 258, 259, 260, 261, 0, 8, 1, 9, 2, 10, 3, 11, 4, 12, 5, 13, 6, 14, 7, 15 }; struct gui_data *inst = (struct gui_data *)data; char *title; Conf *oldconf, *newconf; int i, j, need_size; assert(lenof(ww) == NCFGCOLOURS); if (inst->reconfiguring) return; else inst->reconfiguring = TRUE; title = dupcat(appname, " Reconfiguration", NULL); oldconf = inst->conf; newconf = conf_copy(inst->conf); if (do_config_box(title, newconf, 1, inst->back?inst->back->cfg_info(inst->backhandle):0)) { inst->conf = newconf; /* Pass new config data to the logging module */ log_reconfig(inst->logctx, inst->conf); /* * Flush the line discipline's edit buffer in the case * where local editing has just been disabled. */ if (inst->ldisc) { ldisc_configure(inst->ldisc, inst->conf); ldisc_send(inst->ldisc, NULL, 0, 0); } /* Pass new config data to the terminal */ term_reconfig(inst->term, inst->conf); /* Pass new config data to the back end */ if (inst->back) inst->back->reconfig(inst->backhandle, inst->conf); cache_conf_values(inst); /* * Just setting inst->conf is sufficient to cause colour * setting changes to appear on the next ESC]R palette * reset. But we should also check whether any colour * settings have been changed, and revert the ones that have * to the new default, on the assumption that the user is * most likely to want an immediate update. */ for (i = 0; i < NCFGCOLOURS; i++) { for (j = 0; j < 3; j++) if (conf_get_int_int(oldconf, CONF_colours, i*3+j) != conf_get_int_int(newconf, CONF_colours, i*3+j)) break; if (j < 3) { real_palette_set(inst, ww[i], conf_get_int_int(newconf,CONF_colours,i*3+0), conf_get_int_int(newconf,CONF_colours,i*3+1), conf_get_int_int(newconf,CONF_colours,i*3+2)); /* * If the default background has changed, we must * repaint the space in between the window border * and the text area. */ if (ww[i] == 258) { set_window_background(inst); draw_backing_rect(inst); } } } /* * If the scrollbar needs to be shown, hidden, or moved * from one end to the other of the window, do so now. */ if (conf_get_int(oldconf, CONF_scrollbar) != conf_get_int(newconf, CONF_scrollbar)) { if (conf_get_int(newconf, CONF_scrollbar)) gtk_widget_show(inst->sbar); else gtk_widget_hide(inst->sbar); } if (conf_get_int(oldconf, CONF_scrollbar_on_left) != conf_get_int(newconf, CONF_scrollbar_on_left)) { gtk_box_reorder_child(inst->hbox, inst->sbar, conf_get_int(newconf, CONF_scrollbar_on_left) ? 0 : 1); } /* * Change the window title, if required. */ if (strcmp(conf_get_str(oldconf, CONF_wintitle), conf_get_str(newconf, CONF_wintitle))) set_title(inst, conf_get_str(newconf, CONF_wintitle)); set_window_titles(inst); /* * Redo the whole tangled fonts and Unicode mess if * necessary. */ need_size = FALSE; if (strcmp(conf_get_fontspec(oldconf, CONF_font)->name, conf_get_fontspec(newconf, CONF_font)->name) || strcmp(conf_get_fontspec(oldconf, CONF_boldfont)->name, conf_get_fontspec(newconf, CONF_boldfont)->name) || strcmp(conf_get_fontspec(oldconf, CONF_widefont)->name, conf_get_fontspec(newconf, CONF_widefont)->name) || strcmp(conf_get_fontspec(oldconf, CONF_wideboldfont)->name, conf_get_fontspec(newconf, CONF_wideboldfont)->name) || strcmp(conf_get_str(oldconf, CONF_line_codepage), conf_get_str(newconf, CONF_line_codepage)) || conf_get_int(oldconf, CONF_utf8_override) != conf_get_int(newconf, CONF_utf8_override) || conf_get_int(oldconf, CONF_vtmode) != conf_get_int(newconf, CONF_vtmode) || conf_get_int(oldconf, CONF_shadowbold) != conf_get_int(newconf, CONF_shadowbold) || conf_get_int(oldconf, CONF_shadowboldoffset) != conf_get_int(newconf, CONF_shadowboldoffset)) { char *errmsg = setup_fonts_ucs(inst); if (errmsg) { char *msgboxtext = dupprintf("Could not change fonts in terminal window: %s\n", errmsg); messagebox(inst->window, "Font setup error", msgboxtext, string_width("Could not change fonts in terminal window:"), "OK", 'o', +1, 1, NULL); sfree(msgboxtext); sfree(errmsg); } else { need_size = TRUE; } } /* * Resize the window. */ if (conf_get_int(oldconf, CONF_width) != conf_get_int(newconf, CONF_width) || conf_get_int(oldconf, CONF_height) != conf_get_int(newconf, CONF_height) || conf_get_int(oldconf, CONF_window_border) != conf_get_int(newconf, CONF_window_border) || need_size) { set_geom_hints(inst); request_resize(inst, conf_get_int(newconf, CONF_width), conf_get_int(newconf, CONF_height)); } else { /* * The above will have caused a call to term_size() for * us if it happened. If the user has fiddled with only * the scrollback size, the above will not have * happened and we will need an explicit term_size() * here. */ if (conf_get_int(oldconf, CONF_savelines) != conf_get_int(newconf, CONF_savelines)) term_size(inst->term, inst->term->rows, inst->term->cols, conf_get_int(newconf, CONF_savelines)); } term_invalidate(inst->term); /* * We do an explicit full redraw here to ensure the window * border has been redrawn as well as the text area. */ gtk_widget_queue_draw(inst->area); conf_free(oldconf); } else { conf_free(newconf); } sfree(title); inst->reconfiguring = FALSE; } void fork_and_exec_self(struct gui_data *inst, int fd_to_close, ...) { |
︙ | ︙ | |||
3010 3011 3012 3013 3014 3015 3016 3017 3018 3019 3020 3021 3022 3023 | /* * Do the double fork. */ pid = fork(); if (pid < 0) { perror("fork"); return; } if (pid == 0) { int pid2 = fork(); if (pid2 < 0) { perror("fork"); | > | 3229 3230 3231 3232 3233 3234 3235 3236 3237 3238 3239 3240 3241 3242 3243 | /* * Do the double fork. */ pid = fork(); if (pid < 0) { perror("fork"); sfree(args); return; } if (pid == 0) { int pid2 = fork(); if (pid2 < 0) { perror("fork"); |
︙ | ︙ | |||
3043 3044 3045 3046 3047 3048 3049 3050 3051 3052 3053 3054 3055 3056 3057 3058 | execv("/proc/self/exe", args); execvp(inst->progname, args); perror("exec"); _exit(127); } else { int status; waitpid(pid, &status, 0); } } void dup_session_menuitem(GtkMenuItem *item, gpointer gdata) { struct gui_data *inst = (struct gui_data *)gdata; /* | > | | | | | | | | | | | | > > | 3263 3264 3265 3266 3267 3268 3269 3270 3271 3272 3273 3274 3275 3276 3277 3278 3279 3280 3281 3282 3283 3284 3285 3286 3287 3288 3289 3290 3291 3292 3293 3294 3295 3296 3297 3298 3299 3300 3301 3302 3303 3304 3305 3306 3307 3308 3309 3310 3311 3312 3313 3314 3315 3316 3317 3318 3319 3320 3321 3322 3323 3324 3325 3326 3327 3328 3329 3330 3331 3332 3333 3334 3335 3336 3337 3338 3339 3340 3341 3342 3343 3344 3345 3346 3347 3348 3349 3350 3351 3352 3353 3354 3355 3356 3357 3358 3359 3360 3361 3362 3363 3364 3365 3366 3367 3368 3369 3370 3371 3372 3373 3374 3375 3376 3377 3378 3379 3380 3381 3382 | execv("/proc/self/exe", args); execvp(inst->progname, args); perror("exec"); _exit(127); } else { int status; sfree(args); waitpid(pid, &status, 0); } } void dup_session_menuitem(GtkMenuItem *item, gpointer gdata) { struct gui_data *inst = (struct gui_data *)gdata; /* * For this feature we must marshal conf and (possibly) pty_argv * into a byte stream, create a pipe, and send this byte stream * to the child through the pipe. */ int i, ret, sersize, size; char *data; char option[80]; int pipefd[2]; if (pipe(pipefd) < 0) { perror("pipe"); return; } size = sersize = conf_serialised_size(inst->conf); if (use_pty_argv && pty_argv) { for (i = 0; pty_argv[i]; i++) size += strlen(pty_argv[i]) + 1; } data = snewn(size, char); conf_serialise(inst->conf, data); if (use_pty_argv && pty_argv) { int p = sersize; for (i = 0; pty_argv[i]; i++) { strcpy(data + p, pty_argv[i]); p += strlen(pty_argv[i]) + 1; } assert(p == size); } sprintf(option, "---[%d,%d]", pipefd[0], size); noncloexec(pipefd[0]); fork_and_exec_self(inst, pipefd[1], option, NULL); close(pipefd[0]); i = ret = 0; while (i < size && (ret = write(pipefd[1], data + i, size - i)) > 0) i += ret; if (ret < 0) perror("write to pipe"); close(pipefd[1]); sfree(data); } int read_dupsession_data(struct gui_data *inst, Conf *conf, char *arg) { int fd, i, ret, size, size_used; char *data; if (sscanf(arg, "---[%d,%d]", &fd, &size) != 2) { fprintf(stderr, "%s: malformed magic argument `%s'\n", appname, arg); exit(1); } data = snewn(size, char); i = ret = 0; while (i < size && (ret = read(fd, data + i, size - i)) > 0) i += ret; if (ret < 0) { perror("read from pipe"); exit(1); } else if (i < size) { fprintf(stderr, "%s: unexpected EOF in Duplicate Session data\n", appname); exit(1); } size_used = conf_deserialise(conf, data, size); if (use_pty_argv && size > size_used) { int n = 0; i = size_used; while (i < size) { while (i < size && data[i]) i++; if (i >= size) { fprintf(stderr, "%s: malformed Duplicate Session data\n", appname); exit(1); } i++; n++; } pty_argv = snewn(n+1, char *); pty_argv[n] = NULL; n = 0; i = size_used; while (i < size) { char *p = data + i; while (i < size && data[i]) i++; assert(i < size); i++; pty_argv[n++] = dupstr(p); } } sfree(data); return 0; } void new_session_menuitem(GtkMenuItem *item, gpointer data) { struct gui_data *inst = (struct gui_data *)data; |
︙ | ︙ | |||
3316 3317 3318 3319 3320 3321 3322 | gtk_widget_hide(inst->specialsitem1); gtk_widget_hide(inst->specialsitem2); } } static void start_backend(struct gui_data *inst) { | | > | | > > | > | | > | < | < | | | > > > > | 3539 3540 3541 3542 3543 3544 3545 3546 3547 3548 3549 3550 3551 3552 3553 3554 3555 3556 3557 3558 3559 3560 3561 3562 3563 3564 3565 3566 3567 3568 3569 3570 3571 3572 3573 3574 3575 3576 3577 3578 3579 3580 3581 3582 3583 3584 3585 3586 3587 3588 3589 3590 3591 3592 3593 3594 3595 3596 3597 3598 3599 3600 3601 3602 3603 3604 3605 3606 3607 3608 3609 3610 3611 3612 3613 | gtk_widget_hide(inst->specialsitem1); gtk_widget_hide(inst->specialsitem2); } } static void start_backend(struct gui_data *inst) { extern Backend *select_backend(Conf *conf); char *realhost; const char *error; char *s; inst->back = select_backend(inst->conf); error = inst->back->init((void *)inst, &inst->backhandle, inst->conf, conf_get_str(inst->conf, CONF_host), conf_get_int(inst->conf, CONF_port), &realhost, conf_get_int(inst->conf, CONF_tcp_nodelay), conf_get_int(inst->conf, CONF_tcp_keepalives)); if (error) { char *msg = dupprintf("Unable to open connection to %s:\n%s", conf_get_str(inst->conf, CONF_host), error); inst->exited = TRUE; fatal_message_box(inst->window, msg); sfree(msg); exit(0); } s = conf_get_str(inst->conf, CONF_wintitle); if (s[0]) { set_title_and_icon(inst, s, s); } else { char *title = make_default_wintitle(realhost); set_title_and_icon(inst, title, title); sfree(title); } sfree(realhost); inst->back->provide_logctx(inst->backhandle, inst->logctx); term_provide_resize_fn(inst->term, inst->back->size, inst->backhandle); inst->ldisc = ldisc_create(inst->conf, inst->term, inst->back, inst->backhandle, inst); gtk_widget_set_sensitive(inst->restartitem, FALSE); } int pt_main(int argc, char **argv) { extern int cfgbox(Conf *conf); struct gui_data *inst; setlocale(LC_CTYPE, ""); /* * Create an instance structure and initialise to zeroes */ inst = snew(struct gui_data); memset(inst, 0, sizeof(*inst)); inst->alt_keycode = -1; /* this one needs _not_ to be zero */ inst->busy_status = BUSY_NOT; inst->conf = conf_new(); inst->wintitle = inst->icontitle = NULL; /* defer any child exit handling until we're ready to deal with * it */ block_signal(SIGCHLD, 1); inst->progname = argv[0]; /* |
︙ | ︙ | |||
3391 3392 3393 3394 3395 3396 3397 | inst->gtkargvstart[i-1] = dupstr(argv[i]); oldargc = argc; gtk_init(&argc, &argv); inst->ngtkargs = oldargc - argc; } if (argc > 1 && !strncmp(argv[1], "---", 3)) { | | | | | | | | | > > > > > | > > > > > > > | | | > | | > | | | | | | 3621 3622 3623 3624 3625 3626 3627 3628 3629 3630 3631 3632 3633 3634 3635 3636 3637 3638 3639 3640 3641 3642 3643 3644 3645 3646 3647 3648 3649 3650 3651 3652 3653 3654 3655 3656 3657 3658 3659 3660 3661 3662 3663 3664 3665 3666 3667 3668 3669 3670 3671 3672 3673 3674 3675 3676 3677 3678 3679 3680 3681 3682 3683 3684 3685 3686 3687 3688 3689 3690 3691 3692 3693 3694 3695 3696 3697 3698 3699 3700 3701 3702 3703 3704 3705 3706 3707 3708 3709 3710 3711 3712 3713 3714 3715 3716 3717 3718 | inst->gtkargvstart[i-1] = dupstr(argv[i]); oldargc = argc; gtk_init(&argc, &argv); inst->ngtkargs = oldargc - argc; } if (argc > 1 && !strncmp(argv[1], "---", 3)) { read_dupsession_data(inst, inst->conf, argv[1]); /* Splatter this argument so it doesn't clutter a ps listing */ smemclr(argv[1], strlen(argv[1])); } else { /* 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->conf)) exit(1); /* pre-defaults pass to get -class */ do_defaults(NULL, inst->conf); if (do_cmdline(argc, argv, 1, &allow_launch, inst, inst->conf)) exit(1); /* post-defaults, do everything */ cmdline_run_saved(inst->conf); if (loaded_session) allow_launch = TRUE; if ((!allow_launch || !conf_launchable(inst->conf)) && !cfgbox(inst->conf)) exit(0); /* config box hit Cancel */ } if (!compound_text_atom) compound_text_atom = gdk_atom_intern("COMPOUND_TEXT", FALSE); if (!utf8_string_atom) utf8_string_atom = gdk_atom_intern("UTF8_STRING", FALSE); inst->area = gtk_drawing_area_new(); #if GTK_CHECK_VERSION(2,0,0) inst->imc = gtk_im_multicontext_new(); #endif { char *errmsg = setup_fonts_ucs(inst); if (errmsg) { fprintf(stderr, "%s: %s\n", appname, errmsg); exit(1); } } init_cutbuffers(); inst->window = gtk_window_new(GTK_WINDOW_TOPLEVEL); { const char *winclass = conf_get_str(inst->conf, CONF_winclass); if (*winclass) gtk_window_set_wmclass(GTK_WINDOW(inst->window), winclass, winclass); } /* * Set up the colour map. */ palette_reset(inst); inst->width = conf_get_int(inst->conf, CONF_width); inst->height = conf_get_int(inst->conf, CONF_height); cache_conf_values(inst); gtk_drawing_area_size(GTK_DRAWING_AREA(inst->area), inst->font_width * inst->width + 2*inst->window_border, inst->font_height * inst->height + 2*inst->window_border); inst->sbar_adjust = GTK_ADJUSTMENT(gtk_adjustment_new(0,0,0,0,0,0)); inst->sbar = gtk_vscrollbar_new(inst->sbar_adjust); inst->hbox = GTK_BOX(gtk_hbox_new(FALSE, 0)); /* * We always create the scrollbar; it remains invisible if * unwanted, so we can pop it up quickly if it suddenly becomes * desirable. */ if (conf_get_int(inst->conf, CONF_scrollbar_on_left)) gtk_box_pack_start(inst->hbox, inst->sbar, FALSE, FALSE, 0); gtk_box_pack_start(inst->hbox, inst->area, TRUE, TRUE, 0); if (!conf_get_int(inst->conf, CONF_scrollbar_on_left)) gtk_box_pack_start(inst->hbox, inst->sbar, FALSE, FALSE, 0); gtk_container_add(GTK_CONTAINER(inst->window), GTK_WIDGET(inst->hbox)); set_geom_hints(inst); gtk_widget_show(inst->area); if (conf_get_int(inst->conf, CONF_scrollbar)) gtk_widget_show(inst->sbar); else gtk_widget_hide(inst->sbar); gtk_widget_show(GTK_WIDGET(inst->hbox)); if (inst->gotpos) { int x = inst->xpos, y = inst->ypos; |
︙ | ︙ | |||
3508 3509 3510 3511 3512 3513 3514 | GTK_SIGNAL_FUNC(motion_event), inst); gtk_signal_connect(GTK_OBJECT(inst->area), "selection_received", GTK_SIGNAL_FUNC(selection_received), inst); gtk_signal_connect(GTK_OBJECT(inst->area), "selection_get", GTK_SIGNAL_FUNC(selection_get), inst); gtk_signal_connect(GTK_OBJECT(inst->area), "selection_clear_event", GTK_SIGNAL_FUNC(selection_clear), inst); | > > > > | | 3752 3753 3754 3755 3756 3757 3758 3759 3760 3761 3762 3763 3764 3765 3766 3767 3768 3769 3770 | GTK_SIGNAL_FUNC(motion_event), inst); gtk_signal_connect(GTK_OBJECT(inst->area), "selection_received", GTK_SIGNAL_FUNC(selection_received), inst); gtk_signal_connect(GTK_OBJECT(inst->area), "selection_get", GTK_SIGNAL_FUNC(selection_get), inst); gtk_signal_connect(GTK_OBJECT(inst->area), "selection_clear_event", GTK_SIGNAL_FUNC(selection_clear), inst); #if GTK_CHECK_VERSION(2,0,0) g_signal_connect(G_OBJECT(inst->imc), "commit", G_CALLBACK(input_method_commit_event), inst); #endif if (conf_get_int(inst->conf, CONF_scrollbar)) gtk_signal_connect(GTK_OBJECT(inst->sbar_adjust), "value_changed", GTK_SIGNAL_FUNC(scrollbar_moved), inst); gtk_widget_add_events(GTK_WIDGET(inst->area), GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK | GDK_BUTTON_MOTION_MASK); |
︙ | ︙ | |||
3608 3609 3610 3611 3612 3613 3614 | inst->blankcursor = make_mouse_ptr(inst, -1); make_mouse_ptr(inst, -2); /* clean up cursor font */ inst->currcursor = inst->textcursor; show_mouseptr(inst, 1); inst->eventlogstuff = eventlogstuff_new(); | > > | | | > | 3856 3857 3858 3859 3860 3861 3862 3863 3864 3865 3866 3867 3868 3869 3870 3871 3872 3873 3874 3875 3876 3877 3878 3879 | inst->blankcursor = make_mouse_ptr(inst, -1); make_mouse_ptr(inst, -2); /* clean up cursor font */ inst->currcursor = inst->textcursor; show_mouseptr(inst, 1); inst->eventlogstuff = eventlogstuff_new(); request_callback_notifications(notify_toplevel_callback, inst); inst->term = term_init(inst->conf, &inst->ucsdata, inst); inst->logctx = log_init(inst, inst->conf); term_provide_logctx(inst->term, inst->logctx); uxsel_init(); term_size(inst->term, inst->height, inst->width, conf_get_int(inst->conf, CONF_savelines)); start_backend(inst); ldisc_send(inst->ldisc, NULL, 0, 0);/* cause ldisc to notice changes */ /* now we're reday to deal with the child exit handler being * called */ |
︙ | ︙ |
Changes to unix/unix.h.
︙ | ︙ | |||
9 10 11 12 13 14 15 | #include <stdint.h> /* C99 int types */ #ifndef NO_LIBDL #include <dlfcn.h> /* Dynamic library loading */ #endif /* NO_LIBDL */ #include "charset.h" struct Filename { | | | | > < < < | 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | #include <stdint.h> /* C99 int types */ #ifndef NO_LIBDL #include <dlfcn.h> /* Dynamic library loading */ #endif /* NO_LIBDL */ #include "charset.h" struct Filename { char *path; }; FILE *f_open(const struct Filename *, char const *, int); struct FontSpec { char *name; /* may be "" to indicate no selected font at all */ }; struct FontSpec *fontspec_new(const char *name); typedef void *Context; /* FIXME: probably needs changing */ extern Backend pty_backend; typedef uint32_t uint32; /* C99: uint32_t defined in stdint.h */ #define PUTTY_UINT32_DEFINED /* * Under GTK, we send MA_CLICK _and_ MA_2CLK, or MA_CLICK _and_ |
︙ | ︙ | |||
55 56 57 58 59 60 61 | #define SEL_NL { 10 } /* Simple wraparound timer function */ unsigned long getticks(void); /* based on gettimeofday(2) */ #define GETTICKCOUNT getticks #define TICKSPERSEC 1000 /* we choose to use milliseconds */ #define CURSORBLINK 450 /* no standard way to set this */ | < < < < < | 53 54 55 56 57 58 59 60 61 62 63 64 65 66 | #define SEL_NL { 10 } /* Simple wraparound timer function */ unsigned long getticks(void); /* based on gettimeofday(2) */ #define GETTICKCOUNT getticks #define TICKSPERSEC 1000 /* we choose to use milliseconds */ #define CURSORBLINK 450 /* no standard way to set this */ #define WCHAR wchar_t #define BYTE unsigned char /* * Unix-specific global flag * |
︙ | ︙ | |||
85 86 87 88 89 90 91 | int font_dimension(void *frontend, int which);/* 0 for width, 1 for height */ long get_windowid(void *frontend); /* Things gtkdlg.c needs from pterm.c */ void *get_window(void *frontend); /* void * to avoid depending on gtk.h */ /* Things pterm.c needs from gtkdlg.c */ | | > > > > > | | 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 | int font_dimension(void *frontend, int which);/* 0 for width, 1 for height */ long get_windowid(void *frontend); /* Things gtkdlg.c needs from pterm.c */ void *get_window(void *frontend); /* void * to avoid depending on gtk.h */ /* Things pterm.c needs from gtkdlg.c */ int do_config_box(const char *title, Conf *conf, int midsession, int protcfginfo); void fatal_message_box(void *window, char *msg); void nonfatal_message_box(void *window, char *msg); void about_box(void *window); void *eventlogstuff_new(void); void showeventlog(void *estuff, void *parentwin); void logevent_dlg(void *estuff, const char *string); int reallyclose(void *frontend); #ifdef MAY_REFER_TO_GTK_IN_HEADERS int messagebox(GtkWidget *parentwin, char *title, char *msg, int minwid, ...); int string_width(char *text); #endif /* Things pterm.c needs from {ptermm,uxputty}.c */ char *make_default_wintitle(char *hostname); int process_nonoption_arg(char *arg, Conf *conf, int *allow_launch); /* pterm.c needs this special function in xkeysym.c */ int keysym_to_unicode(int keysym); /* Things uxstore.c needs from pterm.c */ char *x_get_default(const char *key); |
︙ | ︙ | |||
151 152 153 154 155 156 157 | #define stricmp strcasecmp /* BSD-semantics version of signal(), and another helpful function */ void (*putty_signal(int sig, void (*func)(int)))(int); void block_signal(int sig, int block_it); /* uxmisc.c */ | | > > > | 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 | #define stricmp strcasecmp /* BSD-semantics version of signal(), and another helpful function */ void (*putty_signal(int sig, void (*func)(int)))(int); void block_signal(int sig, int block_it); /* uxmisc.c */ void cloexec(int); void noncloexec(int); int nonblock(int); int no_nonblock(int); /* * Exports from unicode.c. */ struct unicode_data; int init_ucs(struct unicode_data *ucsdata, char *line_codepage, int utf8_override, int font_charset, int vtmode); |
︙ | ︙ |
Changes to unix/ux_x11.c.
︙ | ︙ | |||
8 9 10 11 12 13 14 | #include <stdlib.h> #include <errno.h> #include "putty.h" #include "ssh.h" #include "network.h" | | | 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | #include <stdlib.h> #include <errno.h> #include "putty.h" #include "ssh.h" #include "network.h" void platform_get_x11_auth(struct X11Display *disp, Conf *conf) { char *xauthfile; int needs_free; /* * Find the .Xauthority file. */ |
︙ | ︙ |
Changes to unix/uxagentc.c.
︙ | ︙ | |||
13 14 15 16 17 18 19 | #include "putty.h" #include "misc.h" #include "tree234.h" #include "puttymem.h" int agent_exists(void) { | | > | 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | #include "putty.h" #include "misc.h" #include "tree234.h" #include "puttymem.h" int agent_exists(void) { const char *p = getenv("SSH_AUTH_SOCK"); if (p && *p) return TRUE; return FALSE; } static tree234 *agent_connections; struct agent_connection { int fd; |
︙ | ︙ | |||
70 71 72 73 74 75 76 | if (conn->retbuf != conn->sizebuf) sfree(conn->retbuf); conn->retbuf = NULL; conn->retlen = 0; goto done; } conn->retlen += ret; if (conn->retsize == 4 && conn->retlen == 4) { | | < | 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 | if (conn->retbuf != conn->sizebuf) sfree(conn->retbuf); conn->retbuf = NULL; conn->retlen = 0; goto done; } conn->retlen += ret; if (conn->retsize == 4 && conn->retlen == 4) { conn->retsize = toint(GET_32BIT(conn->retbuf) + 4); if (conn->retsize <= 0) { conn->retbuf = NULL; conn->retlen = 0; goto done; } assert(conn->retbuf == conn->sizebuf); conn->retbuf = snewn(conn->retsize, char); memcpy(conn->retbuf, conn->sizebuf, 4); } if (conn->retlen < conn->retsize) return 0; /* more data to come */ |
︙ | ︙ |
Changes to unix/uxcfg.c.
︙ | ︙ | |||
12 13 14 15 16 17 18 | void unix_setup_config_box(struct controlbox *b, int midsession, int protocol) { struct controlset *s; union control *c; /* | | | | | | | | | < | | 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 | void unix_setup_config_box(struct controlbox *b, int midsession, int protocol) { struct controlset *s; union control *c; /* * The Conf structure contains two Unix-specific elements which * are not configured in here: stamp_utmp and login_shell. This * is because pterm does not put up a configuration box right at * the start, which is the only time when these elements would * be useful to configure. */ /* * On Unix, we don't have a drop-down list for the printer * control. */ s = ctrl_getset(b, "Terminal", "printing", "Remote-controlled printing"); assert(s->ncontrols == 1 && s->ctrls[0]->generic.type == CTRL_EDITBOX); s->ctrls[0]->editbox.has_list = 0; /* * Unix supports a local-command proxy. This also means we must * adjust the text on the `Telnet command' control. */ if (!midsession) { int i; s = ctrl_getset(b, "Connection/Proxy", "basics", NULL); for (i = 0; i < s->ncontrols; i++) { c = s->ctrls[i]; if (c->generic.type == CTRL_RADIO && c->generic.context.i == CONF_proxy_type) { assert(c->generic.handler == conf_radiobutton_handler); c->radio.nbuttons++; c->radio.buttons = sresize(c->radio.buttons, c->radio.nbuttons, char *); c->radio.buttons[c->radio.nbuttons-1] = dupstr("Local"); c->radio.buttondata = sresize(c->radio.buttondata, c->radio.nbuttons, intorptr); c->radio.buttondata[c->radio.nbuttons-1] = I(PROXY_CMD); break; } } for (i = 0; i < s->ncontrols; i++) { c = s->ctrls[i]; if (c->generic.type == CTRL_EDITBOX && c->generic.context.i == CONF_proxy_telnet_command) { assert(c->generic.handler == conf_editbox_handler); sfree(c->generic.label); c->generic.label = dupstr("Telnet command, or local" " proxy command"); break; } } } |
︙ | ︙ |
Changes to unix/uxcons.c.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | /* * uxcons.c: various interactive-prompt routines shared between the * Unix console PuTTY tools */ #include <stdio.h> #include <stdlib.h> #include <stdarg.h> #include <assert.h> #include <termios.h> #include <unistd.h> #include "putty.h" #include "storage.h" #include "ssh.h" int console_batch_mode = FALSE; | > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | /* * uxcons.c: various interactive-prompt routines shared between the * Unix console PuTTY tools */ #include <stdio.h> #include <stdlib.h> #include <stdarg.h> #include <assert.h> #include <termios.h> #include <unistd.h> #include <fcntl.h> #include "putty.h" #include "storage.h" #include "ssh.h" int console_batch_mode = FALSE; |
︙ | ︙ | |||
64 65 66 67 68 69 70 | { } void notify_remote_exit(void *frontend) { } | | | 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 | { } void notify_remote_exit(void *frontend) { } void timer_change_notify(unsigned long next) { } int verify_ssh_host_key(void *frontend, char *host, int port, char *keytype, char *keystr, char *fingerprint, void (*callback)(void *ctx, int result), void *ctx) { |
︙ | ︙ | |||
229 230 231 232 233 234 235 | } } /* * Ask whether to wipe a session log file before writing to it. * Returns 2 for wipe, 1 for append, 0 for cancel (don't log). */ | | | | | 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 | } } /* * Ask whether to wipe a session log file before writing to it. * Returns 2 for wipe, 1 for append, 0 for cancel (don't log). */ int askappend(void *frontend, Filename *filename, void (*callback)(void *ctx, int result), void *ctx) { static const char msgtemplate[] = "The session log file \"%.*s\" already exists.\n" "You can overwrite it with a new session log,\n" "append your session log to the end of it,\n" "or disable session logging for this session.\n" "Enter \"y\" to wipe the file, \"n\" to append to it,\n" "or just press Return to disable logging.\n" "Wipe the log file? (y/n, Return cancels logging) "; static const char msgtemplate_batch[] = "The session log file \"%.*s\" already exists.\n" "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); return 0; } fprintf(stderr, msgtemplate, FILENAME_MAX, filename->path); fflush(stderr); { struct termios oldmode, newmode; tcgetattr(0, &oldmode); newmode = oldmode; newmode.c_lflag |= ECHO | ISIG | ICANON; |
︙ | ︙ | |||
315 316 317 318 319 320 321 | { console_logctx = logctx; } void logevent(void *frontend, const char *string) { struct termios cf; | > | > | | | | | | < | > > > > | | > > > > > > > > > > | | | > | > > | | | | | | | | | > > < > > > > > | > | | > > > | > | > > > | > > | | | 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 | { console_logctx = logctx; } void logevent(void *frontend, const char *string) { struct termios cf; if ((flags & FLAG_STDERR) && (flags & FLAG_VERBOSE)) premsg(&cf); if (console_logctx) log_eventlog(console_logctx, string); if ((flags & FLAG_STDERR) && (flags & FLAG_VERBOSE)) postmsg(&cf); } /* * Special functions to read and print to the console for password * prompts and the like. Uses /dev/tty or stdin/stderr, in that order * of preference; also sanitises escape sequences out of the text, on * the basis that it might have been sent by a hostile SSH server * doing malicious keyboard-interactive. */ static void console_open(FILE **outfp, int *infd) { int fd; if ((fd = open("/dev/tty", O_RDWR)) >= 0) { *infd = fd; *outfp = fdopen(*infd, "w"); } else { *infd = 0; *outfp = stderr; } } static void console_close(FILE *outfp, int infd) { if (outfp != stderr) fclose(outfp); /* will automatically close infd too */ } static void console_prompt_text(FILE *outfp, const char *data, int len) { int i; for (i = 0; i < len; i++) if ((data[i] & 0x60) || (data[i] == '\n')) fputc(data[i], outfp); fflush(outfp); } int console_get_userpass_input(prompts_t *p, unsigned char *in, int inlen) { size_t curr_prompt; FILE *outfp = NULL; int infd; /* * Zero all the results, in case we abort half-way through. */ { int i; for (i = 0; i < p->n_prompts; i++) prompt_set_result(p->prompts[i], ""); } if (p->n_prompts && console_batch_mode) return 0; console_open(&outfp, &infd); /* * Preamble. */ /* We only print the `name' caption if we have to... */ if (p->name_reqd && p->name) { size_t l = strlen(p->name); console_prompt_text(outfp, p->name, l); if (p->name[l-1] != '\n') console_prompt_text(outfp, "\n", 1); } /* ...but we always print any `instruction'. */ if (p->instruction) { size_t l = strlen(p->instruction); console_prompt_text(outfp, p->instruction, l); if (p->instruction[l-1] != '\n') console_prompt_text(outfp, "\n", 1); } for (curr_prompt = 0; curr_prompt < p->n_prompts; curr_prompt++) { struct termios oldmode, newmode; int len; prompt_t *pr = p->prompts[curr_prompt]; tcgetattr(infd, &oldmode); newmode = oldmode; newmode.c_lflag |= ISIG | ICANON; if (!pr->echo) newmode.c_lflag &= ~ECHO; else newmode.c_lflag |= ECHO; tcsetattr(infd, TCSANOW, &newmode); console_prompt_text(outfp, pr->prompt, strlen(pr->prompt)); len = 0; while (1) { int ret; prompt_ensure_result_size(pr, len * 5 / 4 + 512); ret = read(infd, pr->result + len, pr->resultsize - len - 1); if (ret <= 0) { len = -1; break; } len += ret; if (pr->result[len - 1] == '\n') { len--; break; } } tcsetattr(infd, TCSANOW, &oldmode); if (!pr->echo) console_prompt_text(outfp, "\n", 1); if (len < 0) { console_close(outfp, infd); return 0; /* failure due to read error */ } pr->result[len] = '\0'; } console_close(outfp, infd); return 1; /* success */ } void frontend_keypress(void *handle) { /* |
︙ | ︙ |
Changes to unix/uxgen.c.
︙ | ︙ | |||
22 23 24 25 26 27 28 29 30 31 32 33 34 35 | } ngot = 0; while (ngot < len) { ret = read(fd, buf+ngot, len-ngot); if (ret < 0) { close(fd); perror("puttygen: unable to read /dev/random"); return NULL; } ngot += ret; } close(fd); | > | 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | } ngot = 0; while (ngot < len) { ret = read(fd, buf+ngot, len-ngot); if (ret < 0) { close(fd); sfree(buf); perror("puttygen: unable to read /dev/random"); return NULL; } ngot += ret; } close(fd); |
︙ | ︙ |
Changes to unix/uxgss.c.
︙ | ︙ | |||
49 50 51 52 53 54 55 | #undef BIND_GSS_FN ssh_gssapi_bind_fns(lib); } /* Dynamically load gssapi libs. */ | | > | 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 | #undef BIND_GSS_FN ssh_gssapi_bind_fns(lib); } /* Dynamically load gssapi libs. */ struct ssh_gss_liblist *ssh_gss_setup(Conf *conf) { void *gsslib; char *gsspath; struct ssh_gss_liblist *list = snew(struct ssh_gss_liblist); list->libraries = snewn(4, struct ssh_gss_library); list->nlibraries = 0; /* Heimdal's GSSAPI Library */ if ((gsslib = dlopen("libgssapi.so.2", RTLD_LAZY)) != NULL) |
︙ | ︙ | |||
73 74 75 76 77 78 79 | /* Sun's GSSAPI Library */ if ((gsslib = dlopen("libgss.so.1", RTLD_LAZY)) != NULL) gss_init(&list->libraries[list->nlibraries++], gsslib, 2, "Using GSSAPI from libgss.so.1"); /* User-specified GSSAPI library */ | | | | | 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 | /* Sun's GSSAPI Library */ if ((gsslib = dlopen("libgss.so.1", RTLD_LAZY)) != NULL) gss_init(&list->libraries[list->nlibraries++], gsslib, 2, "Using GSSAPI from libgss.so.1"); /* User-specified GSSAPI library */ gsspath = conf_get_filename(conf, CONF_ssh_gss_custom)->path; if (*gsspath && (gsslib = dlopen(gsspath, RTLD_LAZY)) != NULL) gss_init(&list->libraries[list->nlibraries++], gsslib, 3, dupprintf("Using GSSAPI from user-specified" " library '%s'", gsspath)); return list; } void ssh_gss_cleanup(struct ssh_gss_liblist *list) { int i; |
︙ | ︙ | |||
125 126 127 128 129 130 131 | * library structure containing pointers to the functions we linked * against. */ #include <gssapi/gssapi.h> /* Dynamically load gssapi libs. */ | | | 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 | * library structure containing pointers to the functions we linked * against. */ #include <gssapi/gssapi.h> /* Dynamically load gssapi libs. */ struct ssh_gss_liblist *ssh_gss_setup(Conf *conf) { struct ssh_gss_liblist *list = snew(struct ssh_gss_liblist); list->libraries = snew(struct ssh_gss_library); list->nlibraries = 1; list->libraries[0].gsslogmsg = "Using statically linked GSSAPI"; |
︙ | ︙ |
Changes to unix/uxmisc.c.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | /* * PuTTY miscellaneous Unix stuff */ #include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <assert.h> #include <unistd.h> #include <sys/time.h> #include <sys/types.h> #include <pwd.h> #include "putty.h" | > > < < < < | > | | > > > > > > > > > > > > > | | | > | | < | > > > > > | | | > > > > > > > > > > > > > > > > | > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 | /* * PuTTY miscellaneous Unix stuff */ #include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <assert.h> #include <errno.h> #include <unistd.h> #include <time.h> #include <sys/time.h> #include <sys/types.h> #include <pwd.h> #include "putty.h" unsigned long getticks(void) { /* * We want to use milliseconds rather than the microseconds or * nanoseconds given by the underlying clock functions, because we * need a decent number of them to fit into a 32-bit word so it * can be used for keepalives. */ #if defined HAVE_CLOCK_GETTIME && defined HAVE_DECL_CLOCK_MONOTONIC { /* Use CLOCK_MONOTONIC if available, so as to be unconfused if * the system clock changes. */ struct timespec ts; if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) return ts.tv_sec * TICKSPERSEC + ts.tv_nsec / (1000000000 / TICKSPERSEC); } #endif { struct timeval tv; gettimeofday(&tv, NULL); return tv.tv_sec * TICKSPERSEC + tv.tv_usec / (1000000 / TICKSPERSEC); } } Filename *filename_from_str(const char *str) { Filename *ret = snew(Filename); ret->path = dupstr(str); return ret; } Filename *filename_copy(const Filename *fn) { return filename_from_str(fn->path); } const char *filename_to_str(const Filename *fn) { return fn->path; } int filename_equal(const Filename *f1, const Filename *f2) { return !strcmp(f1->path, f2->path); } int filename_is_null(const Filename *fn) { return !fn->path[0]; } void filename_free(Filename *fn) { sfree(fn->path); sfree(fn); } int filename_serialise(const Filename *f, void *vdata) { char *data = (char *)vdata; int len = strlen(f->path) + 1; /* include trailing NUL */ if (data) { strcpy(data, f->path); } return len; } Filename *filename_deserialise(void *vdata, int maxsize, int *used) { char *data = (char *)vdata; char *end; end = memchr(data, '\0', maxsize); if (!end) return NULL; end++; *used = end - data; return filename_from_str(data); } #ifdef DEBUG static FILE *debug_fp = NULL; void dputs(char *buf) { |
︙ | ︙ | |||
121 122 123 124 125 126 127 | "PuTTY Master Key (RSA), 1024-bit:\n" " " PGP_RSA_MASTER_KEY_FP "\n" "PuTTY Master Key (DSA), 1024-bit:\n" " " PGP_DSA_MASTER_KEY_FP "\n", stdout); } /* | | > > > > > > > | | > > > | > > | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | < > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 | "PuTTY Master Key (RSA), 1024-bit:\n" " " PGP_RSA_MASTER_KEY_FP "\n" "PuTTY Master Key (DSA), 1024-bit:\n" " " PGP_DSA_MASTER_KEY_FP "\n", stdout); } /* * Set and clear fcntl options on a file descriptor. We don't * realistically expect any of these operations to fail (the most * plausible error condition is EBADF, but we always believe ourselves * to be passing a valid fd so even that's an assertion-fail sort of * response), so we don't make any effort to return sensible error * codes to the caller - we just log to standard error and die * unceremoniously. However, nonblock and no_nonblock do return the * previous state of O_NONBLOCK. */ void cloexec(int fd) { int fdflags; fdflags = fcntl(fd, F_GETFD); if (fdflags < 0) { fprintf(stderr, "%d: fcntl(F_GETFD): %s\n", fd, strerror(errno)); exit(1); } if (fcntl(fd, F_SETFD, fdflags | FD_CLOEXEC) < 0) { fprintf(stderr, "%d: fcntl(F_SETFD): %s\n", fd, strerror(errno)); exit(1); } } void noncloexec(int fd) { int fdflags; fdflags = fcntl(fd, F_GETFD); if (fdflags < 0) { fprintf(stderr, "%d: fcntl(F_GETFD): %s\n", fd, strerror(errno)); exit(1); } if (fcntl(fd, F_SETFD, fdflags & ~FD_CLOEXEC) < 0) { fprintf(stderr, "%d: fcntl(F_SETFD): %s\n", fd, strerror(errno)); exit(1); } } int nonblock(int fd) { int fdflags; fdflags = fcntl(fd, F_GETFL); if (fdflags < 0) { fprintf(stderr, "%d: fcntl(F_GETFL): %s\n", fd, strerror(errno)); exit(1); } if (fcntl(fd, F_SETFL, fdflags | O_NONBLOCK) < 0) { fprintf(stderr, "%d: fcntl(F_SETFL): %s\n", fd, strerror(errno)); exit(1); } return fdflags & O_NONBLOCK; } int no_nonblock(int fd) { int fdflags; fdflags = fcntl(fd, F_GETFL); if (fdflags < 0) { fprintf(stderr, "%d: fcntl(F_GETFL): %s\n", fd, strerror(errno)); exit(1); } if (fcntl(fd, F_SETFL, fdflags & ~O_NONBLOCK) < 0) { fprintf(stderr, "%d: fcntl(F_SETFL): %s\n", fd, strerror(errno)); exit(1); } return fdflags & O_NONBLOCK; } FILE *f_open(const Filename *filename, char const *mode, int is_private) { if (!is_private) { return fopen(filename->path, mode); } else { int fd; assert(mode[0] == 'w'); /* is_private is meaningless for read, and tricky for append */ fd = open(filename->path, O_WRONLY | O_CREAT | O_TRUNC, 0600); if (fd < 0) return NULL; return fdopen(fd, mode); } } FontSpec *fontspec_new(const char *name) { FontSpec *f = snew(FontSpec); f->name = dupstr(name); return f; } FontSpec *fontspec_copy(const FontSpec *f) { return fontspec_new(f->name); } void fontspec_free(FontSpec *f) { sfree(f->name); sfree(f); } int fontspec_serialise(FontSpec *f, void *data) { int len = strlen(f->name); if (data) strcpy(data, f->name); return len + 1; /* include trailing NUL */ } FontSpec *fontspec_deserialise(void *vdata, int maxsize, int *used) { char *data = (char *)vdata; char *end = memchr(data, '\0', maxsize); if (!end) return NULL; *used = end - data + 1; return fontspec_new(data); } |
Changes to unix/uxnet.c.
︙ | ︙ | |||
71 72 73 74 75 76 77 | struct Socket_tag { struct socket_function_table *fn; /* the above variable absolutely *must* be the first in this structure */ const char *error; int s; Plug plug; | < < < > > | 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 | struct Socket_tag { struct socket_function_table *fn; /* the above variable absolutely *must* be the first in this structure */ const char *error; int s; Plug plug; bufchain output_data; int connected; /* irrelevant for listening sockets */ int writable; int frozen; /* this causes readability notifications to be ignored */ int localhost_only; /* for listening sockets */ char oobdata[1]; int sending_oob; int oobpending; /* is there OOB data available to read? */ int oobinline; enum { EOF_NO, EOF_PENDING, EOF_SENT } outgoingeof; int incomingeof; int pending_error; /* in case send() returns error */ int listener; int nodelay, keepalive; /* for connect()-type sockets */ int privport, port; /* and again */ SockAddr addr; SockAddrStep step; /* |
︙ | ︙ | |||
312 313 314 315 316 317 318 | return FALSE; } #endif } void sk_getaddr(SockAddr addr, char *buf, int buflen) { | < < < | | 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 | return FALSE; } #endif } void sk_getaddr(SockAddr addr, char *buf, int buflen) { if (addr->superfamily == UNRESOLVED || addr->superfamily == UNIX) { strncpy(buf, addr->hostname, buflen); buf[buflen-1] = '\0'; } else { #ifndef NO_IPV6 if (getnameinfo(addr->ais->ai_addr, addr->ais->ai_addrlen, buf, buflen, NULL, 0, NI_NUMERICHOST) != 0) { buf[0] = '\0'; |
︙ | ︙ | |||
337 338 339 340 341 342 343 | a.s_addr = htonl(addr->addresses[0]); strncpy(buf, inet_ntoa(a), buflen); buf[buflen-1] = '\0'; #endif } } | > > > > > > > > > | | 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 | a.s_addr = htonl(addr->addresses[0]); strncpy(buf, inet_ntoa(a), buflen); buf[buflen-1] = '\0'; #endif } } int sk_addr_needs_port(SockAddr addr) { if (addr->superfamily == UNRESOLVED || addr->superfamily == UNIX) { return FALSE; } else { return TRUE; } } int sk_hostname_is_local(const char *name) { return !strcmp(name, "localhost") || !strcmp(name, "::1") || !strncmp(name, "127.", 4); } #define ipv4_is_loopback(addr) \ |
︙ | ︙ | |||
383 384 385 386 387 388 389 390 391 392 393 394 395 396 | START_STEP(addr, step); assert(SOCKADDR_FAMILY(addr, step) == AF_INET); a.s_addr = htonl(addr->addresses[0]); return ipv4_is_loopback(a); #endif } } int sk_addrtype(SockAddr addr) { SockAddrStep step; int family; START_STEP(addr, step); family = SOCKADDR_FAMILY(addr, step); | > > > > > | 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 | START_STEP(addr, step); assert(SOCKADDR_FAMILY(addr, step) == AF_INET); a.s_addr = htonl(addr->addresses[0]); return ipv4_is_loopback(a); #endif } } int sk_address_is_special_local(SockAddr addr) { return addr->superfamily == UNIX; } int sk_addrtype(SockAddr addr) { SockAddrStep step; int family; START_STEP(addr, step); family = SOCKADDR_FAMILY(addr, step); |
︙ | ︙ | |||
462 463 464 465 466 467 468 | * so we don't need to do anything here. :-) */ } static void sk_tcp_close(Socket s); static int sk_tcp_write(Socket s, const char *data, int len); static int sk_tcp_write_oob(Socket s, const char *data, int len); | | < | | < | > < > > | 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 | * so we don't need to do anything here. :-) */ } static void sk_tcp_close(Socket s); static int sk_tcp_write(Socket s, const char *data, int len); static int sk_tcp_write_oob(Socket s, const char *data, int len); static void sk_tcp_write_eof(Socket s); static void sk_tcp_set_frozen(Socket s, int is_frozen); static const char *sk_tcp_socket_error(Socket s); static struct socket_function_table tcp_fn_table = { sk_tcp_plug, sk_tcp_close, sk_tcp_write, sk_tcp_write_oob, sk_tcp_write_eof, sk_tcp_flush, sk_tcp_set_frozen, sk_tcp_socket_error }; static Socket sk_tcp_accept(accept_ctx_t ctx, Plug plug) { int sockfd = ctx.i; Actual_Socket ret; /* * Create Socket structure. */ ret = snew(struct Socket_tag); ret->fn = &tcp_fn_table; ret->error = NULL; ret->plug = plug; bufchain_init(&ret->output_data); ret->writable = 1; /* to start with */ ret->sending_oob = 0; ret->frozen = 1; ret->localhost_only = 0; /* unused, but best init anyway */ ret->pending_error = 0; ret->oobpending = FALSE; ret->outgoingeof = EOF_NO; ret->incomingeof = FALSE; ret->listener = 0; ret->parent = ret->child = NULL; ret->addr = NULL; ret->connected = 1; ret->s = sockfd; |
︙ | ︙ | |||
525 526 527 528 529 530 531 | static int try_connect(Actual_Socket sock) { int s; union sockaddr_union u; const union sockaddr_union *sa; int err = 0; short localport; | | | 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 | static int try_connect(Actual_Socket sock) { int s; union sockaddr_union u; const union sockaddr_union *sa; int err = 0; short localport; int salen, family; /* * 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. */ |
︙ | ︙ | |||
557 558 559 560 561 562 563 | goto ret; } cloexec(s); if (sock->oobinline) { int b = TRUE; | | > > > > > | > > > > > | > > > > > | 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 | goto ret; } cloexec(s); if (sock->oobinline) { int b = TRUE; if (setsockopt(s, SOL_SOCKET, SO_OOBINLINE, (void *) &b, sizeof(b)) < 0) { err = errno; close(s); goto ret; } } if (sock->nodelay) { int b = TRUE; if (setsockopt(s, IPPROTO_TCP, TCP_NODELAY, (void *) &b, sizeof(b)) < 0) { err = errno; close(s); goto ret; } } if (sock->keepalive) { int b = TRUE; if (setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, (void *) &b, sizeof(b)) < 0) { err = errno; close(s); goto ret; } } /* * Bind to local address. */ if (sock->privport) localport = 1023; /* count from 1023 downwards */ |
︙ | ︙ | |||
665 666 667 668 669 670 671 | break; default: assert(0 && "unknown address family"); exit(1); /* XXX: GCC doesn't understand assert() on some systems. */ } | | < < | 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 | break; default: assert(0 && "unknown address family"); exit(1); /* XXX: GCC doesn't understand assert() on some systems. */ } nonblock(s); if ((connect(s, &(sa->sa), salen)) < 0) { if ( errno != EINPROGRESS ) { err = errno; goto ret; } } else { |
︙ | ︙ | |||
715 716 717 718 719 720 721 | ret->error = NULL; ret->plug = plug; bufchain_init(&ret->output_data); ret->connected = 0; /* to start with */ ret->writable = 0; /* to start with */ ret->sending_oob = 0; ret->frozen = 0; | < > > | 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 | ret->error = NULL; ret->plug = plug; bufchain_init(&ret->output_data); ret->connected = 0; /* to start with */ ret->writable = 0; /* to start with */ ret->sending_oob = 0; ret->frozen = 0; ret->localhost_only = 0; /* unused, but best init anyway */ ret->pending_error = 0; ret->parent = ret->child = NULL; ret->oobpending = FALSE; ret->outgoingeof = EOF_NO; ret->incomingeof = FALSE; ret->listener = 0; ret->addr = addr; START_STEP(ret->addr, ret->step); ret->s = -1; ret->oobinline = oobinline; ret->nodelay = nodelay; ret->keepalive = keepalive; |
︙ | ︙ | |||
767 768 769 770 771 772 773 | ret->fn = &tcp_fn_table; ret->error = NULL; ret->plug = plug; bufchain_init(&ret->output_data); ret->writable = 0; /* to start with */ ret->sending_oob = 0; ret->frozen = 0; | < > > > | 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 | ret->fn = &tcp_fn_table; ret->error = NULL; ret->plug = plug; bufchain_init(&ret->output_data); ret->writable = 0; /* to start with */ ret->sending_oob = 0; ret->frozen = 0; ret->localhost_only = local_host_only; ret->pending_error = 0; ret->parent = ret->child = NULL; ret->oobpending = FALSE; ret->outgoingeof = EOF_NO; ret->incomingeof = FALSE; ret->listener = 1; ret->addr = NULL; ret->s = -1; /* * Translate address_family from platform-independent constants * into local reality. */ address_family = (orig_address_family == ADDRTYPE_IPV4 ? AF_INET : #ifndef NO_IPV6 |
︙ | ︙ | |||
816 817 818 819 820 821 822 | return (Socket) ret; } cloexec(s); ret->oobinline = 0; | | > > > > > | 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 | return (Socket) ret; } cloexec(s); ret->oobinline = 0; if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (const char *)&on, sizeof(on)) < 0) { ret->error = strerror(errno); close(s); return (Socket) ret; } retcode = -1; addr = NULL; addrlen = -1; /* placate optimiser */ if (srcaddr != NULL) { #ifndef NO_IPV6 hints.ai_flags = AI_NUMERICHOST; |
︙ | ︙ | |||
992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 | default: return NULL; } return buf; } /* * The function which tries to send on a socket once it's deemed * writable. */ void try_send(Actual_Socket s) { | > > > > > > > > > > > > > > > > > > > > | 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 | default: return NULL; } return buf; } /* * Deal with socket errors detected in try_send(). */ static void socket_error_callback(void *vs) { Actual_Socket s = (Actual_Socket)vs; /* * Just in case other socket work has caused this socket to vanish * or become somehow non-erroneous before this callback arrived... */ if (!find234(sktree, s, NULL) || !s->pending_error) return; /* * An error has occurred on this socket. Pass it to the plug. */ plug_closing(s->plug, strerror(s->pending_error), s->pending_error, 0); } /* * The function which tries to send on a socket once it's deemed * writable. */ void try_send(Actual_Socket s) { |
︙ | ︙ | |||
1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 | * _in_ a call from the code we'd be calling back * to, so we'd have to make half the SSH code * reentrant. Instead we flag a pending error on * the socket, to be dealt with (by calling * plug_closing()) at some suitable future moment. */ s->pending_error = err; return; } } else { if (s->sending_oob) { if (nsent < len) { memmove(s->oobdata, s->oobdata+nsent, len-nsent); s->sending_oob = len - nsent; } else { s->sending_oob = 0; } } else { bufchain_consume(&s->output_data, nsent); } } } uxsel_tell(s); } static int sk_tcp_write(Socket sock, const char *buf, int len) { Actual_Socket s = (Actual_Socket) sock; /* * Add the data to the buffer list on the socket. */ bufchain_add(&s->output_data, buf, len); /* | > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 | * _in_ a call from the code we'd be calling back * to, so we'd have to make half the SSH code * reentrant. Instead we flag a pending error on * the socket, to be dealt with (by calling * plug_closing()) at some suitable future moment. */ s->pending_error = err; /* * Immediately cease selecting on this socket, so that * we don't tight-loop repeatedly trying to do * whatever it was that went wrong. */ uxsel_tell(s); /* * Arrange to be called back from the top level to * deal with the error condition on this socket. */ queue_toplevel_callback(socket_error_callback, s); return; } } else { if (s->sending_oob) { if (nsent < len) { memmove(s->oobdata, s->oobdata+nsent, len-nsent); s->sending_oob = len - nsent; } else { s->sending_oob = 0; } } else { bufchain_consume(&s->output_data, nsent); } } } /* * If we reach here, we've finished sending everything we might * have needed to send. Send EOF, if we need to. */ if (s->outgoingeof == EOF_PENDING) { shutdown(s->s, SHUT_WR); s->outgoingeof = EOF_SENT; } /* * Also update the select status, because we don't need to select * for writing any more. */ uxsel_tell(s); } static int sk_tcp_write(Socket sock, const char *buf, int len) { Actual_Socket s = (Actual_Socket) sock; assert(s->outgoingeof == EOF_NO); /* * Add the data to the buffer list on the socket. */ bufchain_add(&s->output_data, buf, len); /* |
︙ | ︙ | |||
1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 | return bufchain_size(&s->output_data); } static int sk_tcp_write_oob(Socket sock, const char *buf, int len) { Actual_Socket s = (Actual_Socket) sock; /* * Replace the buffer list on the socket with the data. */ bufchain_clear(&s->output_data); assert(len <= sizeof(s->oobdata)); memcpy(s->oobdata, buf, len); s->sending_oob = len; | > > | 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 | return bufchain_size(&s->output_data); } static int sk_tcp_write_oob(Socket sock, const char *buf, int len) { Actual_Socket s = (Actual_Socket) sock; assert(s->outgoingeof == EOF_NO); /* * Replace the buffer list on the socket with the data. */ bufchain_clear(&s->output_data); assert(len <= sizeof(s->oobdata)); memcpy(s->oobdata, buf, len); s->sending_oob = len; |
︙ | ︙ | |||
1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 | * Update the select() status to correctly reflect whether or * not we should be selecting for write. */ uxsel_tell(s); return s->sending_oob; } static int net_select_result(int fd, int event) { int ret; char buf[20480]; /* nice big buffer for plenty of speed */ Actual_Socket s; u_long atmark; | > > > > > > > > > > > > > > > > > > > > > > > > | 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 | * Update the select() status to correctly reflect whether or * not we should be selecting for write. */ uxsel_tell(s); return s->sending_oob; } static void sk_tcp_write_eof(Socket sock) { Actual_Socket s = (Actual_Socket) sock; assert(s->outgoingeof == EOF_NO); /* * Mark the socket as pending outgoing EOF. */ s->outgoingeof = EOF_PENDING; /* * Now try sending from the start of the buffer list. */ if (s->writable) try_send(s); /* * Update the select() status to correctly reflect whether or * not we should be selecting for write. */ uxsel_tell(s); } static int net_select_result(int fd, int event) { int ret; char buf[20480]; /* nice big buffer for plenty of speed */ Actual_Socket s; u_long atmark; |
︙ | ︙ | |||
1163 1164 1165 1166 1167 1168 1169 1170 | if (s->listener) { /* * On a listening socket, the readability event means a * connection is ready to be accepted. */ union sockaddr_union su; socklen_t addrlen = sizeof(su); int t; /* socket of connection */ | > < | | < | | | | < < | 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 | if (s->listener) { /* * On a listening socket, the readability event means a * connection is ready to be accepted. */ union sockaddr_union su; socklen_t addrlen = sizeof(su); accept_ctx_t actx; int t; /* socket of connection */ memset(&su, 0, addrlen); t = accept(s->s, &su.sa, &addrlen); if (t < 0) { break; } nonblock(t); actx.i = t; if ((!s->addr || s->addr->superfamily != UNIX) && s->localhost_only && !sockaddr_is_loopback(&su.sa)) { close(t); /* someone let nonlocal through?! */ } else if (plug_accepting(s->plug, sk_tcp_accept, actx)) { close(t); /* denied or error */ } break; } /* * If we reach here, this is not a listening socket, so * readability really means readability. */ /* In the case the socket is still frozen, we don't even bother */ if (s->frozen) break; /* * We have received data on the socket. For an oobinline * socket, this might be data _before_ an urgent pointer, * in which case we send it to the back end with type==1 * (data prior to urgent). */ |
︙ | ︙ | |||
1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 | while (s->addr && sk_nextaddr(s->addr, &s->step)) { err = try_connect(s); } } if (err != 0) return plug_closing(s->plug, strerror(err), err, 0); } else if (0 == ret) { return plug_closing(s->plug, NULL, 0, 0); } else { /* * Receiving actual data on a socket means we can * stop falling back through the candidate * addresses to connect to. */ | > > | 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 | while (s->addr && sk_nextaddr(s->addr, &s->step)) { err = try_connect(s); } } if (err != 0) return plug_closing(s->plug, strerror(err), err, 0); } else if (0 == ret) { s->incomingeof = TRUE; /* stop trying to read now */ uxsel_tell(s); return plug_closing(s->plug, NULL, 0, 0); } else { /* * Receiving actual data on a socket means we can * stop falling back through the candidate * addresses to connect to. */ |
︙ | ︙ | |||
1271 1272 1273 1274 1275 1276 1277 | } break; } return 1; } | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 | } break; } return 1; } /* * Special error values are returned from sk_namelookup and sk_new * if there's a problem. These functions extract an error message, * or return NULL if there's no problem. */ const char *sk_addr_error(SockAddr addr) { |
︙ | ︙ | |||
1345 1346 1347 1348 1349 1350 1351 | static void sk_tcp_set_frozen(Socket sock, int is_frozen) { Actual_Socket s = (Actual_Socket) sock; if (s->frozen == is_frozen) return; s->frozen = is_frozen; | < < < < < > | | | | | | | | | > | 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 | static void sk_tcp_set_frozen(Socket sock, int is_frozen) { Actual_Socket s = (Actual_Socket) sock; if (s->frozen == is_frozen) return; s->frozen = is_frozen; uxsel_tell(s); } static void uxsel_tell(Actual_Socket s) { int rwx = 0; if (!s->pending_error) { if (s->listener) { rwx |= 1; /* read == accept */ } else { if (!s->connected) rwx |= 2; /* write == connect */ if (s->connected && !s->frozen && !s->incomingeof) rwx |= 1 | 4; /* read, except */ if (bufchain_size(&s->output_data)) rwx |= 2; /* write */ } } uxsel_set(s->s, rwx, net_select_result); } int net_service_lookup(char *service) { struct servent *se; |
︙ | ︙ | |||
1429 1430 1431 1432 1433 1434 1435 | #else ret->addresses = NULL; ret->naddresses = 0; #endif ret->refcount = 1; return ret; } | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 | #else ret->addresses = NULL; ret->naddresses = 0; #endif ret->refcount = 1; return ret; } SockAddr unix_sock_addr(const char *path) { SockAddr ret = snew(struct SockAddr_tag); int n; memset(ret, 0, sizeof *ret); ret->superfamily = UNIX; n = snprintf(ret->hostname, sizeof ret->hostname, "%s", path); if (n < 0) ret->error = "snprintf failed"; else if (n >= sizeof ret->hostname) ret->error = "socket pathname too long"; #ifndef NO_IPV6 ret->ais = NULL; #else ret->addresses = NULL; ret->naddresses = 0; #endif ret->refcount = 1; return ret; } Socket new_unix_listener(SockAddr listenaddr, Plug plug) { int s; union sockaddr_union u; union sockaddr_union *addr; int addrlen; Actual_Socket ret; int retcode; /* * Create Socket structure. */ ret = snew(struct Socket_tag); ret->fn = &tcp_fn_table; ret->error = NULL; ret->plug = plug; bufchain_init(&ret->output_data); ret->writable = 0; /* to start with */ ret->sending_oob = 0; ret->frozen = 0; ret->localhost_only = TRUE; ret->pending_error = 0; ret->parent = ret->child = NULL; ret->oobpending = FALSE; ret->outgoingeof = EOF_NO; ret->incomingeof = FALSE; ret->listener = 1; ret->addr = listenaddr; ret->s = -1; assert(listenaddr->superfamily == UNIX); /* * Open socket. */ s = socket(AF_UNIX, SOCK_STREAM, 0); if (s < 0) { ret->error = strerror(errno); return (Socket) ret; } cloexec(s); ret->oobinline = 0; memset(&u, '\0', sizeof(u)); u.su.sun_family = AF_UNIX; strncpy(u.su.sun_path, listenaddr->hostname, sizeof(u.su.sun_path)-1); addr = &u; addrlen = sizeof(u.su); if (unlink(u.su.sun_path) < 0 && errno != ENOENT) { close(s); ret->error = strerror(errno); return (Socket) ret; } retcode = bind(s, &addr->sa, addrlen); if (retcode < 0) { close(s); ret->error = strerror(errno); return (Socket) ret; } if (listen(s, SOMAXCONN) < 0) { close(s); ret->error = strerror(errno); return (Socket) ret; } ret->s = s; uxsel_tell(ret); add234(sktree, ret); return (Socket) ret; } |
Changes to unix/uxplink.c.
︙ | ︙ | |||
58 59 60 61 62 63 64 65 66 67 68 69 70 71 | fputc('\n', stderr); postmsg(&cf); if (logctx) { log_free(logctx); logctx = NULL; } cleanup_exit(1); } void connection_fatal(void *frontend, char *p, ...) { struct termios cf; va_list ap; premsg(&cf); fprintf(stderr, "FATAL ERROR: "); | > > > > > > > > > > > > | 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 | fputc('\n', stderr); postmsg(&cf); if (logctx) { log_free(logctx); logctx = NULL; } cleanup_exit(1); } void nonfatal(char *p, ...) { struct termios cf; va_list ap; premsg(&cf); fprintf(stderr, "ERROR: "); va_start(ap, p); vfprintf(stderr, p, ap); va_end(ap); fputc('\n', stderr); postmsg(&cf); } void connection_fatal(void *frontend, char *p, ...) { struct termios cf; va_list ap; premsg(&cf); fprintf(stderr, "FATAL ERROR: "); |
︙ | ︙ | |||
94 95 96 97 98 99 100 | exit(1); } static int local_tty = FALSE; /* do we have a local tty? */ static Backend *back; static void *backhandle; | | < < < < < < | < < | | < | < | | 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 | exit(1); } static int local_tty = FALSE; /* do we have a local tty? */ static Backend *back; static void *backhandle; static Conf *conf; /* * Default settings that are specific to pterm. */ char *platform_default_s(const char *name) { if (!strcmp(name, "TermType")) return dupstr(getenv("TERM")); if (!strcmp(name, "UserName")) return get_username(); if (!strcmp(name, "SerialLine")) return dupstr("/dev/ttyS0"); return NULL; } int platform_default_i(const char *name, int def) { return def; } FontSpec *platform_default_fontspec(const char *name) { return fontspec_new(""); } Filename *platform_default_filename(const char *name) { if (!strcmp(name, "LogFileName")) return filename_from_str("putty.log"); else return filename_from_str(""); } char *x_get_default(const char *key) { return NULL; /* this is a stub */ } int term_ldisc(Terminal *term, int mode) |
︙ | ︙ | |||
198 199 200 201 202 203 204 | /* Helper function to extract a special character from a termios. */ static char *get_ttychar(struct termios *t, int index) { cc_t c = t->c_cc[index]; #if defined(_POSIX_VDISABLE) if (c == _POSIX_VDISABLE) | | | 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 | /* Helper function to extract a special character from a termios. */ static char *get_ttychar(struct termios *t, int index) { cc_t c = t->c_cc[index]; #if defined(_POSIX_VDISABLE) if (c == _POSIX_VDISABLE) return dupstr(""); #endif return dupprintf("^<%d>", c); } char *get_ttymode(void *frontend, const char *mode) { /* |
︙ | ︙ | |||
379 380 381 382 383 384 385 386 387 388 389 390 391 | void cleanup_termios(void) { if (local_tty) tcsetattr(STDIN_FILENO, TCSANOW, &orig_termios); } bufchain stdout_data, stderr_data; int try_output(int is_stderr) { bufchain *chain = (is_stderr ? &stderr_data : &stdout_data); int fd = (is_stderr ? STDERR_FILENO : STDOUT_FILENO); void *senddata; | > | | < | < < < | | | | | | | | | | | > > > > > > > > > > > > > > | 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 | void cleanup_termios(void) { if (local_tty) tcsetattr(STDIN_FILENO, TCSANOW, &orig_termios); } bufchain stdout_data, stderr_data; enum { EOF_NO, EOF_PENDING, EOF_SENT } outgoingeof; int 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; if (bufchain_size(chain) > 0) { int prev_nonblock = nonblock(fd); 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 (!prev_nonblock) no_nonblock(fd); if (ret < 0 && errno != EAGAIN) { perror(is_stderr ? "stderr: write" : "stdout: write"); exit(1); } } if (outgoingeof == EOF_PENDING && bufchain_size(&stdout_data) == 0) { close(STDOUT_FILENO); outgoingeof = EOF_SENT; } return bufchain_size(&stdout_data) + bufchain_size(&stderr_data); } int from_backend(void *frontend_handle, int is_stderr, const char *data, int len) { if (is_stderr) { bufchain_add(&stderr_data, data, len); return try_output(TRUE); } else { assert(outgoingeof == EOF_NO); bufchain_add(&stdout_data, data, len); return try_output(FALSE); } } int from_backend_untrusted(void *frontend_handle, const char *data, int len) { /* * No "untrusted" output should get here (the way the code is * currently, it's all diverted by FLAG_STDERR). */ assert(!"Unexpected call to from_backend_untrusted()"); return 0; /* not reached */ } int from_backend_eof(void *frontend_handle) { assert(outgoingeof == EOF_NO); outgoingeof = EOF_PENDING; try_output(FALSE); return FALSE; /* do not respond to incoming EOF with outgoing */ } int get_userpass_input(prompts_t *p, unsigned char *in, int inlen) { int ret; ret = cmdline_get_passwd_input(p, in, inlen); if (ret == -1) ret = console_get_userpass_input(p, in, inlen); |
︙ | ︙ | |||
572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 | static void version(void) { printf("plink: %s\n", ver); exit(1); } int main(int argc, char **argv) { int sending; int portnumber = -1; int *fdlist; int fd; int i, fdcount, fdsize, fdstate; int connopen; int exitcode; int errors; int use_subsystem = 0; int got_host = FALSE; | > > > > > | > > > > > > | | | | | > > | | | > > > | | | | | < > > | | 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 | static void version(void) { printf("plink: %s\n", ver); exit(1); } void frontend_net_error_pending(void) {} const int share_can_be_downstream = TRUE; const int share_can_be_upstream = TRUE; int main(int argc, char **argv) { int sending; int portnumber = -1; int *fdlist; int fd; int i, fdcount, fdsize, fdstate; int connopen; int exitcode; int errors; int use_subsystem = 0; int got_host = FALSE; unsigned long now; struct winsize size; fdlist = NULL; fdcount = fdsize = 0; /* * Initialise port and protocol to sensible defaults. (These * will be overridden by more or less anything.) */ default_protocol = PROT_SSH; default_port = 22; bufchain_init(&stdout_data); bufchain_init(&stderr_data); outgoingeof = EOF_NO; flags = FLAG_STDERR | FLAG_STDERR_TTY; stderr_tty_init(); /* * Process the command line. */ conf = conf_new(); do_defaults(NULL, conf); loaded_session = FALSE; default_protocol = conf_get_int(conf, CONF_protocol); default_port = conf_get_int(conf, CONF_port); errors = 0; { /* * Override the default protocol if PLINK_PROTOCOL is set. */ char *p = getenv("PLINK_PROTOCOL"); if (p) { const Backend *b = backend_from_name(p); if (b) { default_protocol = b->protocol; default_port = b->default_port; conf_set_int(conf, CONF_protocol, default_protocol); conf_set_int(conf, CONF_port, default_port); } } } while (--argc) { char *p = *++argv; if (*p == '-') { int ret = cmdline_process_param(p, (argc > 1 ? argv[1] : NULL), 1, conf); if (ret == -2) { fprintf(stderr, "plink: option \"%s\" requires an argument\n", p); errors = 1; } else if (ret == 2) { --argc, ++argv; } else if (ret == 1) { continue; } else if (!strcmp(p, "-batch")) { console_batch_mode = 1; } else if (!strcmp(p, "-s")) { /* Save status to write to conf later. */ use_subsystem = 1; } else if (!strcmp(p, "-V") || !strcmp(p, "--version")) { version(); } else if (!strcmp(p, "--help")) { usage(); exit(0); } else if (!strcmp(p, "-pgpfp")) { pgp_fingerprints(); exit(1); } else if (!strcmp(p, "-o")) { if (argc <= 1) { fprintf(stderr, "plink: option \"-o\" requires an argument\n"); errors = 1; } else { --argc; provide_xrm_string(*++argv); } } else { fprintf(stderr, "plink: unknown option \"%s\"\n", p); errors = 1; } } else if (*p) { if (!conf_launchable(conf) || !(got_host || loaded_session)) { char *q = p; /* * If the hostname starts with "telnet:", set the * protocol to Telnet and process the string as a * Telnet URL. */ if (!strncmp(q, "telnet:", 7)) { char c; q += 7; if (q[0] == '/' && q[1] == '/') q += 2; conf_set_int(conf, CONF_protocol, PROT_TELNET); p = q; while (*p && *p != ':' && *p != '/') p++; c = *p; if (*p) *p++ = '\0'; if (c == ':') conf_set_int(conf, CONF_port, atoi(p)); else conf_set_int(conf, CONF_port, -1); conf_set_str(conf, CONF_host, q); got_host = TRUE; } else { char *r, *user, *host; /* * Before we process the [user@]host string, we * first check for the presence of a protocol * prefix (a protocol name followed by ","). */ r = strchr(p, ','); if (r) { const Backend *b; *r = '\0'; b = backend_from_name(p); if (b) { default_protocol = b->protocol; conf_set_int(conf, CONF_protocol, default_protocol); portnumber = b->default_port; } p = r + 1; } /* * A nonzero length string followed by an @ is treated |
︙ | ︙ | |||
724 725 726 727 728 729 730 | } /* * Now attempt to load a saved session with the * same name as the hostname. */ { | | | | | < | | > | < < | 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 | } /* * Now attempt to load a saved session with the * same name as the hostname. */ { Conf *conf2 = conf_new(); do_defaults(host, conf2); if (loaded_session || !conf_launchable(conf2)) { /* No settings for this host; use defaults */ /* (or session was already loaded with -load) */ conf_set_str(conf, CONF_host, host); conf_set_int(conf, CONF_port, default_port); got_host = TRUE; } else { conf_copy_into(conf, conf2); loaded_session = TRUE; } conf_free(conf2); } if (user) { /* Patch in specified username. */ conf_set_str(conf, CONF_username, user); } } } else { char *command; int cmdlen, cmdsize; cmdlen = cmdsize = 0; |
︙ | ︙ | |||
770 771 772 773 774 775 776 | command = sresize(command, cmdsize, char); } command[cmdlen++]=' '; /* always add trailing space */ if (--argc) p = *++argv; } if (cmdlen) command[--cmdlen]='\0'; /* change trailing blank to NUL */ | | | | | | | | > | > > > > > | > > | | < | | | | > | > > > > | > > > > > > > > > | > > > > > | | < < < < | < < < < < < < < < < < < < < | | | > | > > > > > > > > > > > > > > | > > | > | | | | > > | > | > | 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 | command = sresize(command, cmdsize, char); } command[cmdlen++]=' '; /* always add trailing space */ if (--argc) p = *++argv; } if (cmdlen) command[--cmdlen]='\0'; /* change trailing blank to NUL */ conf_set_str(conf, CONF_remote_cmd, command); conf_set_str(conf, CONF_remote_cmd2, ""); conf_set_int(conf, CONF_nopty, TRUE); /* command => no tty */ break; /* done with cmdline */ } } } if (errors) return 1; if (!conf_launchable(conf) || !(got_host || loaded_session)) { usage(); } /* * Muck about with the hostname in various ways. */ { char *hostbuf = dupstr(conf_get_str(conf, CONF_host)); char *host = hostbuf; char *p, *q; /* * Trim leading whitespace. */ host += strspn(host, " \t"); /* * See if host is of the form user@host, and separate out * the username if so. */ if (host[0] != '\0') { char *atsign = strrchr(host, '@'); if (atsign) { *atsign = '\0'; conf_set_str(conf, CONF_username, host); host = atsign + 1; } } /* * Trim off a colon suffix if it's there. */ host[strcspn(host, ":")] = '\0'; /* * Remove any remaining whitespace. */ p = hostbuf; q = host; while (*q) { if (*q != ' ' && *q != '\t') *p++ = *q; q++; } *p = '\0'; conf_set_str(conf, CONF_host, hostbuf); sfree(hostbuf); } /* * Perform command-line overrides on session configuration. */ cmdline_run_saved(conf); /* * Apply subsystem status. */ if (use_subsystem) conf_set_int(conf, CONF_ssh_subsys, TRUE); if (!*conf_get_str(conf, CONF_remote_cmd) && !*conf_get_str(conf, CONF_remote_cmd2) && !*conf_get_str(conf, CONF_ssh_nc_host)) flags |= FLAG_INTERACTIVE; /* * Select protocol. This is farmed out into a table in a * separate file to enable an ssh-free variant. */ back = backend_from_proto(conf_get_int(conf, CONF_protocol)); if (back == NULL) { fprintf(stderr, "Internal fault: Unsupported protocol found\n"); return 1; } /* * Select port. */ if (portnumber != -1) conf_set_int(conf, CONF_port, portnumber); /* * Block SIGPIPE, so that we'll get EPIPE individually on * particular network connections that go wrong. */ putty_signal(SIGPIPE, SIG_IGN); /* * Set up the pipe we'll use to tell us about SIGWINCH. */ if (pipe(signalpipe) < 0) { perror("pipe"); exit(1); } putty_signal(SIGWINCH, sigwinch); /* * Now that we've got the SIGWINCH handler installed, try to find * out the initial terminal size. */ if (ioctl(STDIN_FILENO, TIOCGWINSZ, &size) >= 0) { conf_set_int(conf, CONF_width, size.ws_col); conf_set_int(conf, CONF_height, size.ws_row); } 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 (conf_get_int(conf, CONF_protocol) == PROT_SSH && !conf_get_int(conf, CONF_x11_forward) && !conf_get_int(conf, CONF_agentfwd) && !conf_get_str_nthstrkey(conf, CONF_portfwd, 0)) conf_set_int(conf, CONF_ssh_simple, TRUE); /* * Start up the connection. */ logctx = log_init(NULL, conf); console_provide_logctx(logctx); { const char *error; char *realhost; /* nodelay is only useful if stdin is a terminal device */ int nodelay = conf_get_int(conf, CONF_tcp_nodelay) && isatty(0); error = back->init(NULL, &backhandle, conf, conf_get_str(conf, CONF_host), conf_get_int(conf, CONF_port), &realhost, nodelay, conf_get_int(conf, CONF_tcp_keepalives)); if (error) { fprintf(stderr, "Unable to open connection:\n%s\n", error); return 1; } back->provide_logctx(backhandle, logctx); ldisc_create(conf, NULL, back, backhandle, NULL); sfree(realhost); } connopen = 1; /* * Set up the initial console mode. We don't care if this call * fails, because we know we aren't necessarily running in a * console. */ local_tty = (tcgetattr(STDIN_FILENO, &orig_termios) == 0); atexit(cleanup_termios); ldisc_update(NULL, 1, 1); sending = FALSE; now = GETTICKCOUNT(); while (1) { fd_set rset, wset, xset; int maxfd; int rwx; int ret; unsigned long next; FD_ZERO(&rset); FD_ZERO(&wset); FD_ZERO(&xset); maxfd = 0; FD_SET_MAX(signalpipe[0], maxfd, rset); |
︙ | ︙ | |||
970 971 972 973 974 975 976 | FD_SET_MAX(fd, maxfd, rset); if (rwx & 2) FD_SET_MAX(fd, maxfd, wset); if (rwx & 4) FD_SET_MAX(fd, maxfd, xset); } | > > > > > > | > | | | | > | > > < < < < | | | | | < < < < < < | < < < < < < < < | | | < < | 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 | FD_SET_MAX(fd, maxfd, rset); if (rwx & 2) FD_SET_MAX(fd, maxfd, wset); if (rwx & 4) FD_SET_MAX(fd, maxfd, xset); } if (toplevel_callback_pending()) { struct timeval tv; tv.tv_sec = 0; tv.tv_usec = 0; ret = select(maxfd, &rset, &wset, &xset, &tv); } else if (run_timers(now, &next)) { do { unsigned long then; long ticks; struct timeval tv; then = now; now = GETTICKCOUNT(); if (now - then > next - then) ticks = 0; else ticks = next - now; tv.tv_sec = ticks / 1000; tv.tv_usec = ticks % 1000 * 1000; ret = select(maxfd, &rset, &wset, &xset, &tv); if (ret == 0) now = next; else now = GETTICKCOUNT(); } while (ret < 0 && errno == EINTR); } else { ret = select(maxfd, &rset, &wset, &xset, NULL); } if (ret < 0) { perror("select"); exit(1); } for (i = 0; i < fdcount; i++) { |
︙ | ︙ | |||
1035 1036 1037 1038 1039 1040 1041 | if (FD_ISSET(signalpipe[0], &rset)) { char c[1]; struct winsize size; if (read(signalpipe[0], c, 1) <= 0) /* ignore error */; /* ignore its value; it'll be `x' */ | | | 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 | if (FD_ISSET(signalpipe[0], &rset)) { char c[1]; struct winsize size; if (read(signalpipe[0], c, 1) <= 0) /* ignore error */; /* ignore its value; it'll be `x' */ if (ioctl(STDIN_FILENO, TIOCGWINSZ, (void *)&size) >= 0) back->size(backhandle, size.ws_col, size.ws_row); } if (FD_ISSET(STDIN_FILENO, &rset)) { char buf[4096]; int ret; |
︙ | ︙ | |||
1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 | if (FD_ISSET(STDOUT_FILENO, &wset)) { back->unthrottle(backhandle, try_output(FALSE)); } if (FD_ISSET(STDERR_FILENO, &wset)) { back->unthrottle(backhandle, try_output(TRUE)); } if ((!connopen || !back->connected(backhandle)) && bufchain_size(&stdout_data) == 0 && bufchain_size(&stderr_data) == 0) break; /* we closed the connection */ } exitcode = back->exitcode(backhandle); | > > | 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 | if (FD_ISSET(STDOUT_FILENO, &wset)) { back->unthrottle(backhandle, try_output(FALSE)); } if (FD_ISSET(STDERR_FILENO, &wset)) { back->unthrottle(backhandle, try_output(TRUE)); } run_toplevel_callbacks(); if ((!connopen || !back->connected(backhandle)) && bufchain_size(&stdout_data) == 0 && bufchain_size(&stderr_data) == 0) break; /* we closed the connection */ } exitcode = back->exitcode(backhandle); |
︙ | ︙ |
Changes to unix/uxproxy.c.
︙ | ︙ | |||
25 26 27 28 29 30 31 | char *error; Plug plug; bufchain pending_output_data; bufchain pending_input_data; | | < | 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | char *error; Plug plug; bufchain pending_output_data; bufchain pending_input_data; enum { EOF_NO, EOF_PENDING, EOF_SENT } outgoingeof; }; static int localproxy_select_result(int fd, int event); /* * Trees to look up the pipe fds in. */ |
︙ | ︙ | |||
90 91 92 93 94 95 96 97 | ps->plug = p; return ret; } static void sk_localproxy_close (Socket s) { Local_Proxy_Socket ps = (Local_Proxy_Socket) s; | | | > > | | > < | 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 | ps->plug = p; return ret; } static void sk_localproxy_close (Socket s) { Local_Proxy_Socket ps = (Local_Proxy_Socket) s; if (ps->to_cmd >= 0) { del234(localproxy_by_tofd, ps); uxsel_del(ps->to_cmd); close(ps->to_cmd); } del234(localproxy_by_fromfd, ps); uxsel_del(ps->from_cmd); close(ps->from_cmd); sfree(ps); } static int localproxy_try_send(Local_Proxy_Socket ps) { |
︙ | ︙ | |||
124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 | } else if (ret <= 0) { break; } else { bufchain_consume(&ps->pending_output_data, ret); sent += ret; } } if (bufchain_size(&ps->pending_output_data) == 0) uxsel_del(ps->to_cmd); else uxsel_set(ps->to_cmd, 2, localproxy_select_result); return sent; } static int sk_localproxy_write (Socket s, const char *data, int len) { Local_Proxy_Socket ps = (Local_Proxy_Socket) s; bufchain_add(&ps->pending_output_data, data, len); localproxy_try_send(ps); return bufchain_size(&ps->pending_output_data); } static int sk_localproxy_write_oob (Socket s, const char *data, int len) { /* * oob data is treated as inband; nasty, but nothing really * better we can do */ return sk_localproxy_write(s, data, len); } static void sk_localproxy_flush (Socket s) { /* Local_Proxy_Socket ps = (Local_Proxy_Socket) s; */ /* do nothing */ } | > > > > > > > > > > > > > > > > > > > > < < < < < < < < < < < < | 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 | } else if (ret <= 0) { break; } else { bufchain_consume(&ps->pending_output_data, ret); sent += ret; } } if (ps->outgoingeof == EOF_PENDING) { del234(localproxy_by_tofd, ps); close(ps->to_cmd); uxsel_del(ps->to_cmd); ps->to_cmd = -1; ps->outgoingeof = EOF_SENT; } if (bufchain_size(&ps->pending_output_data) == 0) uxsel_del(ps->to_cmd); else uxsel_set(ps->to_cmd, 2, localproxy_select_result); return sent; } static int sk_localproxy_write (Socket s, const char *data, int len) { Local_Proxy_Socket ps = (Local_Proxy_Socket) s; assert(ps->outgoingeof == EOF_NO); bufchain_add(&ps->pending_output_data, data, len); localproxy_try_send(ps); return bufchain_size(&ps->pending_output_data); } static int sk_localproxy_write_oob (Socket s, const char *data, int len) { /* * oob data is treated as inband; nasty, but nothing really * better we can do */ return sk_localproxy_write(s, data, len); } static void sk_localproxy_write_eof (Socket s) { Local_Proxy_Socket ps = (Local_Proxy_Socket) s; assert(ps->outgoingeof == EOF_NO); ps->outgoingeof = EOF_PENDING; localproxy_try_send(ps); } static void sk_localproxy_flush (Socket s) { /* Local_Proxy_Socket ps = (Local_Proxy_Socket) s; */ /* do nothing */ } static void sk_localproxy_set_frozen (Socket s, int is_frozen) { Local_Proxy_Socket ps = (Local_Proxy_Socket) s; if (is_frozen) uxsel_del(ps->from_cmd); else |
︙ | ︙ | |||
220 221 222 223 224 225 226 | return 1; } Socket platform_new_connection(SockAddr addr, char *hostname, int port, int privport, int oobinline, int nodelay, int keepalive, | | | | < | | > > > | | | 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 | return 1; } Socket platform_new_connection(SockAddr addr, char *hostname, int port, int privport, int oobinline, int nodelay, int keepalive, Plug plug, Conf *conf) { char *cmd; static const struct socket_function_table socket_fn_table = { sk_localproxy_plug, sk_localproxy_close, sk_localproxy_write, sk_localproxy_write_oob, sk_localproxy_write_eof, sk_localproxy_flush, sk_localproxy_set_frozen, sk_localproxy_socket_error }; Local_Proxy_Socket ret; int to_cmd_pipe[2], from_cmd_pipe[2], pid; if (conf_get_int(conf, CONF_proxy_type) != PROXY_CMD) return NULL; cmd = format_telnet_command(addr, port, conf); ret = snew(struct Socket_localproxy_tag); ret->fn = &socket_fn_table; ret->plug = plug; ret->error = NULL; ret->outgoingeof = EOF_NO; bufchain_init(&ret->pending_input_data); bufchain_init(&ret->pending_output_data); /* * Create the pipes to the proxy command, and spawn the proxy * command process. */ if (pipe(to_cmd_pipe) < 0 || pipe(from_cmd_pipe) < 0) { ret->error = dupprintf("pipe: %s", strerror(errno)); sfree(cmd); return (Socket)ret; } cloexec(to_cmd_pipe[1]); cloexec(from_cmd_pipe[0]); pid = fork(); if (pid < 0) { ret->error = dupprintf("fork: %s", strerror(errno)); sfree(cmd); return (Socket)ret; } else if (pid == 0) { close(0); close(1); dup2(to_cmd_pipe[0], 0); dup2(from_cmd_pipe[1], 1); close(to_cmd_pipe[0]); close(from_cmd_pipe[1]); noncloexec(0); noncloexec(1); execl("/bin/sh", "sh", "-c", cmd, (void *)NULL); _exit(255); } sfree(cmd); close(to_cmd_pipe[0]); |
︙ | ︙ |
Changes to unix/uxpterm.c.
︙ | ︙ | |||
8 9 10 11 12 13 14 | #include "putty.h" const char *const appname = "pterm"; const int use_event_log = 0; /* pterm doesn't need it */ const int new_session = 0, saved_sessions = 0; /* or these */ const int use_pty_argv = TRUE; | | | | | > | > > | 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 | #include "putty.h" const char *const appname = "pterm"; const int use_event_log = 0; /* pterm doesn't need it */ const int new_session = 0, saved_sessions = 0; /* or these */ const int use_pty_argv = TRUE; Backend *select_backend(Conf *conf) { return &pty_backend; } int cfgbox(Conf *conf) { /* * This is a no-op in pterm, except that we'll ensure the * protocol is set to -1 to inhibit the useless Connection * panel in the config box. */ conf_set_int(conf, CONF_protocol, -1); return 1; } void cleanup_exit(int code) { exit(code); } int process_nonoption_arg(char *arg, Conf *conf, int *allow_launch) { return 0; /* pterm doesn't have any. */ } char *make_default_wintitle(char *hostname) { return dupstr("pterm"); } int main(int argc, char **argv) { extern int pt_main(int argc, char **argv); extern void pty_pre_init(void); /* declared in pty.c */ int ret; cmdline_tooltype = TOOLTYPE_NONNETWORK; default_protocol = -1; pty_pre_init(); ret = pt_main(argc, argv); cleanup_exit(ret); return ret; /* not reached, but placates optimisers */ } |
Changes to unix/uxpty.c.
︙ | ︙ | |||
72 73 74 75 76 77 78 | /* * The pty_signal_pipe, along with the SIGCHLD handler, must be * process-global rather than session-specific. */ static int pty_signal_pipe[2] = { -1, -1 }; /* obviously bogus initial val */ struct pty_tag { | | | 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 | /* * The pty_signal_pipe, along with the SIGCHLD handler, must be * process-global rather than session-specific. */ static int pty_signal_pipe[2] = { -1, -1 }; /* obviously bogus initial val */ struct pty_tag { Conf *conf; int master_fd, slave_fd; void *frontend; char name[FILENAME_MAX]; pid_t child_pid; int term_width, term_height; int child_dead, finished; int exit_code; |
︙ | ︙ | |||
163 164 165 166 167 168 169 | * Likewise, since utmp is only used via pty_pre_init, it too must * be single-instance, so we can declare utmp-related variables * here. */ static Pty single_pty = NULL; #ifndef OMIT_UTMP | | | | 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 | * Likewise, since utmp is only used via pty_pre_init, it too must * be single-instance, so we can declare utmp-related variables * here. */ static Pty single_pty = NULL; #ifndef OMIT_UTMP static pid_t pty_utmp_helper_pid = -1; static int pty_utmp_helper_pipe = -1; static int pty_stamped_utmp; static struct utmpx utmp_entry; #endif /* * pty_argv is a grievous hack to allow a proper argv to be passed * through from the Unix command line. Again, it doesn't really |
︙ | ︙ | |||
266 267 268 269 270 271 272 | } #ifndef OMIT_UTMP static void fatal_sig_handler(int signum) { putty_signal(signum, SIG_DFL); cleanup_utmp(); | < | 266 267 268 269 270 271 272 273 274 275 276 277 278 279 | } #ifndef OMIT_UTMP static void fatal_sig_handler(int signum) { putty_signal(signum, SIG_DFL); cleanup_utmp(); raise(signum); } #endif static int pty_open_slave(Pty pty) { if (pty->slave_fd < 0) { |
︙ | ︙ | |||
331 332 333 334 335 336 337 | got_one: /* We need to chown/chmod the /dev/ttyXX device. */ gp = getgrnam("tty"); chown(pty->name, getuid(), gp ? gp->gr_gid : -1); chmod(pty->name, 0600); #else | > > > > > > > > > > > > > > > | > < < < < < | < < < | 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 | got_one: /* We need to chown/chmod the /dev/ttyXX device. */ gp = getgrnam("tty"); chown(pty->name, getuid(), gp ? gp->gr_gid : -1); chmod(pty->name, 0600); #else const int flags = O_RDWR #ifdef O_NOCTTY | O_NOCTTY #endif ; #ifdef HAVE_POSIX_OPENPT pty->master_fd = posix_openpt(flags); if (pty->master_fd < 0) { perror("posix_openpt"); exit(1); } #else pty->master_fd = open("/dev/ptmx", flags); if (pty->master_fd < 0) { perror("/dev/ptmx: open"); exit(1); } #endif if (grantpt(pty->master_fd) < 0) { perror("grantpt"); exit(1); } if (unlockpt(pty->master_fd) < 0) { perror("unlockpt"); exit(1); } cloexec(pty->master_fd); pty->name[FILENAME_MAX-1] = '\0'; strncpy(pty->name, ptsname(pty->master_fd), FILENAME_MAX-1); #endif nonblock(pty->master_fd); if (!ptys_by_fd) ptys_by_fd = newtree234(pty_compare_by_fd); add234(ptys_by_fd, pty); } /* |
︙ | ︙ | |||
392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 | #ifndef OMIT_UTMP pid_t pid; int pipefd[2]; #endif pty = single_pty = snew(struct pty_tag); bufchain_init(&pty->output_data); /* set the child signal handler straight away; it needs to be set * before we ever fork. */ putty_signal(SIGCHLD, sigchld_handler); pty->master_fd = pty->slave_fd = -1; #ifndef OMIT_UTMP pty_stamped_utmp = FALSE; #endif if (geteuid() != getuid() || getegid() != getgid()) { pty_open_master(pty); | > < | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | > | > > > | > > > | > > > | > > > | 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 | #ifndef OMIT_UTMP pid_t pid; int pipefd[2]; #endif pty = single_pty = snew(struct pty_tag); pty->conf = NULL; bufchain_init(&pty->output_data); /* set the child signal handler straight away; it needs to be set * before we ever fork. */ putty_signal(SIGCHLD, sigchld_handler); pty->master_fd = pty->slave_fd = -1; #ifndef OMIT_UTMP pty_stamped_utmp = FALSE; #endif if (geteuid() != getuid() || getegid() != getgid()) { pty_open_master(pty); #ifndef OMIT_UTMP /* * Fork off the utmp helper. */ if (pipe(pipefd) < 0) { perror("pterm: pipe"); exit(1); } cloexec(pipefd[0]); cloexec(pipefd[1]); pid = fork(); if (pid < 0) { perror("pterm: fork"); exit(1); } else if (pid == 0) { char display[128], buffer[128]; int dlen, ret; close(pipefd[1]); /* * Now sit here until we receive a display name from the * other end of the pipe, and then stamp utmp. Unstamp utmp * again, and exit, when the pipe closes. */ dlen = 0; while (1) { ret = read(pipefd[0], buffer, lenof(buffer)); if (ret <= 0) { cleanup_utmp(); _exit(0); } else if (!pty_stamped_utmp) { if (dlen < lenof(display)) memcpy(display+dlen, buffer, min(ret, lenof(display)-dlen)); if (buffer[ret-1] == '\0') { /* * Now we have a display name. NUL-terminate * it, and stamp utmp. */ display[lenof(display)-1] = '\0'; /* * Trap as many fatal signals as we can in the * hope of having the best possible chance to * clean up utmp before termination. We are * unfortunately unprotected against SIGKILL, * but that's life. */ putty_signal(SIGHUP, fatal_sig_handler); putty_signal(SIGINT, fatal_sig_handler); putty_signal(SIGQUIT, fatal_sig_handler); putty_signal(SIGILL, fatal_sig_handler); putty_signal(SIGABRT, fatal_sig_handler); putty_signal(SIGFPE, fatal_sig_handler); putty_signal(SIGPIPE, fatal_sig_handler); putty_signal(SIGALRM, fatal_sig_handler); putty_signal(SIGTERM, fatal_sig_handler); putty_signal(SIGSEGV, fatal_sig_handler); putty_signal(SIGUSR1, fatal_sig_handler); putty_signal(SIGUSR2, fatal_sig_handler); #ifdef SIGBUS putty_signal(SIGBUS, fatal_sig_handler); #endif #ifdef SIGPOLL putty_signal(SIGPOLL, fatal_sig_handler); #endif #ifdef SIGPROF putty_signal(SIGPROF, fatal_sig_handler); #endif #ifdef SIGSYS putty_signal(SIGSYS, fatal_sig_handler); #endif #ifdef SIGTRAP putty_signal(SIGTRAP, fatal_sig_handler); #endif #ifdef SIGVTALRM putty_signal(SIGVTALRM, fatal_sig_handler); #endif #ifdef SIGXCPU putty_signal(SIGXCPU, fatal_sig_handler); #endif #ifdef SIGXFSZ putty_signal(SIGXFSZ, fatal_sig_handler); #endif #ifdef SIGIO putty_signal(SIGIO, fatal_sig_handler); #endif setup_utmp(pty->name, display); } } } } else { close(pipefd[0]); pty_utmp_helper_pid = pid; pty_utmp_helper_pipe = pipefd[1]; } #endif } /* Drop privs. */ { #ifndef HAVE_NO_SETRESUID int gid = getgid(), uid = getuid(); int setresgid(gid_t, gid_t, gid_t); int setresuid(uid_t, uid_t, uid_t); if (setresgid(gid, gid, gid) < 0) { perror("setresgid"); exit(1); } if (setresuid(uid, uid, uid) < 0) { perror("setresuid"); exit(1); } #else if (setgid(getgid()) < 0) { perror("setgid"); exit(1); } if (setuid(getuid()) < 0) { perror("setuid"); exit(1); } #endif } } int pty_real_select_result(Pty pty, int event, int status) { char buf[4096]; |
︙ | ︙ | |||
584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 | * Attempt to send data down the pty. */ pty_try_write(pty); } } if (finished && !pty->finished) { uxsel_del(pty->master_fd); pty_close(pty); pty->master_fd = -1; pty->finished = TRUE; /* * This is a slight layering-violation sort of hack: only * if we're not closing on exit (COE is set to Never, or to * Only On Clean and it wasn't a clean exit) do we output a * `terminated' message. */ | > > > | | > | 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 | * Attempt to send data down the pty. */ pty_try_write(pty); } } if (finished && !pty->finished) { int close_on_exit; uxsel_del(pty->master_fd); pty_close(pty); pty->master_fd = -1; pty->finished = TRUE; /* * This is a slight layering-violation sort of hack: only * if we're not closing on exit (COE is set to Never, or to * Only On Clean and it wasn't a clean exit) do we output a * `terminated' message. */ close_on_exit = conf_get_int(pty->conf, CONF_close_on_exit); if (close_on_exit == FORCE_OFF || (close_on_exit == AUTO && pty->exit_code != 0)) { char message[512]; message[0] = '\0'; if (WIFEXITED(pty->exit_code)) sprintf(message, "\r\n[pterm: process terminated with exit" " code %d]\r\n", WEXITSTATUS(pty->exit_code)); else if (WIFSIGNALED(pty->exit_code)) #ifdef HAVE_NO_STRSIGNAL sprintf(message, "\r\n[pterm: process terminated on signal" " %d]\r\n", WTERMSIG(pty->exit_code)); |
︙ | ︙ | |||
677 678 679 680 681 682 683 | * Called to set up the pty. * * Returns an error message, or NULL on success. * * Also places the canonical host name into `realhost'. It must be * freed by the caller. */ | | > | | | > | > | | | | | | | | | | | | | | | > | 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 | * Called to set up the pty. * * Returns an error message, or NULL on success. * * Also places the canonical host name into `realhost'. It must be * freed by the caller. */ static const char *pty_init(void *frontend, void **backend_handle, Conf *conf, char *host, int port, char **realhost, int nodelay, int keepalive) { int slavefd; pid_t pid, pgrp; #ifndef NOT_X_WINDOWS /* for Mac OS X native compilation */ long windowid; #endif Pty pty; if (single_pty) { pty = single_pty; assert(pty->conf == NULL); } else { pty = snew(struct pty_tag); pty->master_fd = pty->slave_fd = -1; #ifndef OMIT_UTMP pty_stamped_utmp = FALSE; #endif } pty->frontend = frontend; *backend_handle = NULL; /* we can't sensibly use this, sadly */ pty->conf = conf_copy(conf); pty->term_width = conf_get_int(conf, CONF_width); pty->term_height = conf_get_int(conf, CONF_height); if (pty->master_fd < 0) pty_open_master(pty); /* * Set the backspace character to be whichever of ^H and ^? is * specified by bksp_is_delete. */ { struct termios attrs; tcgetattr(pty->master_fd, &attrs); attrs.c_cc[VERASE] = conf_get_int(conf, CONF_bksp_is_delete) ? '\177' : '\010'; tcsetattr(pty->master_fd, TCSANOW, &attrs); } #ifndef OMIT_UTMP /* * Stamp utmp (that is, tell the utmp helper process to do so), * or not. */ if (pty_utmp_helper_pipe >= 0) { /* if it's < 0, we can't anyway */ if (!conf_get_int(conf, CONF_stamp_utmp)) { close(pty_utmp_helper_pipe); /* just let the child process die */ pty_utmp_helper_pipe = -1; } else { char *location = get_x_display(pty->frontend); int len = strlen(location)+1, pos = 0; /* +1 to include NUL */ while (pos < len) { int ret = write(pty_utmp_helper_pipe, location+pos, len - pos); if (ret < 0) { perror("pterm: writing to utmp helper process"); close(pty_utmp_helper_pipe); /* arrgh, just give up */ pty_utmp_helper_pipe = -1; break; } pos += ret; } } } #endif #ifndef NOT_X_WINDOWS /* for Mac OS X native compilation */ windowid = get_windowid(pty->frontend); #endif |
︙ | ︙ | |||
768 769 770 771 772 773 774 | slavefd = pty_open_slave(pty); if (slavefd < 0) { perror("slave pty: open"); _exit(1); } close(pty->master_fd); | | > | > > > | > | | | < < < < | < < | | | 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 | slavefd = pty_open_slave(pty); if (slavefd < 0) { perror("slave pty: open"); _exit(1); } close(pty->master_fd); noncloexec(slavefd); dup2(slavefd, 0); dup2(slavefd, 1); dup2(slavefd, 2); close(slavefd); setsid(); #ifdef TIOCSCTTY ioctl(0, TIOCSCTTY, 1); #endif pgrp = getpid(); tcsetpgrp(0, pgrp); setpgid(pgrp, pgrp); { int ptyfd = open(pty->name, O_WRONLY, 0); if (ptyfd >= 0) close(ptyfd); } setpgid(pgrp, pgrp); { char *term_env_var = dupprintf("TERM=%s", conf_get_str(conf, CONF_termtype)); putenv(term_env_var); /* We mustn't free term_env_var, as putenv links it into the * environment in place. */ } #ifndef NOT_X_WINDOWS /* for Mac OS X native compilation */ { char *windowid_env_var = dupprintf("WINDOWID=%ld", windowid); putenv(windowid_env_var); /* We mustn't free windowid_env_var, as putenv links it into the * environment in place. */ } #endif { char *key, *val; for (val = conf_get_str_strs(conf, CONF_environmt, NULL, &key); val != NULL; val = conf_get_str_strs(conf, CONF_environmt, key, &key)) { char *varval = dupcat(key, "=", val, NULL); putenv(varval); /* * We must not free varval, since putenv links it * into the environment _in place_. Weird, but * there we go. Memory usage will be rationalised * as soon as we exec anyway. */ |
︙ | ︙ | |||
832 833 834 835 836 837 838 | * blocked during pt_main() startup. Reverse all this for our * child process. */ putty_signal(SIGINT, SIG_DFL); putty_signal(SIGQUIT, SIG_DFL); putty_signal(SIGPIPE, SIG_DFL); block_signal(SIGCHLD, 0); | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 | * blocked during pt_main() startup. Reverse all this for our * child process. */ putty_signal(SIGINT, SIG_DFL); putty_signal(SIGQUIT, SIG_DFL); putty_signal(SIGPIPE, SIG_DFL); block_signal(SIGCHLD, 0); if (pty_argv) { /* * Exec the exact argument list we were given. */ execvp(pty_argv[0], pty_argv); /* * If that fails, and if we had exactly one argument, pass * that argument to $SHELL -c. * * This arranges that we can _either_ follow 'pterm -e' * with a list of argv elements to be fed directly to * exec, _or_ with a single argument containing a command * to be parsed by a shell (but, in cases of doubt, the * former is more reliable). * * A quick survey of other terminal emulators' -e options * (as of Debian squeeze) suggests that: * * - xterm supports both modes, more or less like this * - gnome-terminal will only accept a one-string shell command * - Eterm, kterm and rxvt will only accept a list of * argv elements (as did older versions of pterm). * * It therefore seems important to support both usage * modes in order to be a drop-in replacement for either * xterm or gnome-terminal, and hence for anyone's * plausible uses of the Debian-style alias * 'x-terminal-emulator'... */ if (pty_argv[1] == NULL) { char *shell = getenv("SHELL"); if (shell) execl(shell, shell, "-c", pty_argv[0], (void *)NULL); } } else { char *shell = getenv("SHELL"); char *shellname; if (conf_get_int(conf, CONF_login_shell)) { char *p = strrchr(shell, '/'); shellname = snewn(2+strlen(shell), char); p = p ? p+1 : shell; sprintf(shellname, "-%s", p); } else shellname = shell; execl(getenv("SHELL"), shellname, (void *)NULL); |
︙ | ︙ | |||
875 876 877 878 879 880 881 | cloexec(pty_signal_pipe[0]); cloexec(pty_signal_pipe[1]); } pty_uxsel_setup(pty); *backend_handle = pty; | | | | > > > > > > > > > | > | 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 | cloexec(pty_signal_pipe[0]); cloexec(pty_signal_pipe[1]); } pty_uxsel_setup(pty); *backend_handle = pty; *realhost = dupstr(""); return NULL; } static void pty_reconfig(void *handle, Conf *conf) { Pty pty = (Pty)handle; /* * We don't have much need to reconfigure this backend, but * unfortunately we do need to pick up the setting of Close On * Exit so we know whether to give a `terminated' message. */ conf_copy_into(pty->conf, conf); } /* * Stub routine (never called in pterm). */ static void pty_free(void *handle) { Pty pty = (Pty)handle; /* Either of these may fail `not found'. That's fine with us. */ del234(ptys_by_pid, pty); del234(ptys_by_fd, pty); conf_free(pty->conf); pty->conf = NULL; if (pty == single_pty) { /* * Leave this structure around in case we need to Restart * Session. */ } else { sfree(pty); } } static void pty_try_write(Pty pty) { void *data; int len, ret; |
︙ | ︙ |
Changes to unix/uxputty.c.
︙ | ︙ | |||
27 28 29 30 31 32 33 | * Clean up. */ sk_cleanup(); random_save_seed(); exit(code); } | | | | | | | | | | | < | < | 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 | * Clean up. */ sk_cleanup(); random_save_seed(); exit(code); } Backend *select_backend(Conf *conf) { Backend *back = backend_from_proto(conf_get_int(conf, CONF_protocol)); assert(back != NULL); return back; } int cfgbox(Conf *conf) { char *title = dupcat(appname, " Configuration", NULL); int ret = do_config_box(title, conf, 0, 0); sfree(title); return ret; } static int got_host = 0; const int use_event_log = 1, new_session = 1, saved_sessions = 1; int process_nonoption_arg(char *arg, Conf *conf, int *allow_launch) { char *p, *q = arg; if (got_host) { /* * If we already have a host name, treat this argument as a * port number. NB we have to treat this as a saved -P * argument, so that it will be deferred until it's a good * moment to run it. */ int ret = cmdline_process_param("-P", arg, 1, conf); assert(ret == 2); } else if (!strncmp(q, "telnet:", 7)) { /* * If the hostname starts with "telnet:", * set the protocol to Telnet and process * the string as a Telnet URL. */ char c; q += 7; if (q[0] == '/' && q[1] == '/') q += 2; conf_set_int(conf, CONF_protocol, PROT_TELNET); p = q; while (*p && *p != ':' && *p != '/') p++; c = *p; if (*p) *p++ = '\0'; if (c == ':') conf_set_int(conf, CONF_port, atoi(p)); else conf_set_int(conf, CONF_port, -1); conf_set_str(conf, CONF_host, q); got_host = 1; } else { /* * Otherwise, treat this argument as a host name. */ p = arg; while (*p && !isspace((unsigned char)*p)) p++; if (*p) *p++ = '\0'; conf_set_str(conf, CONF_host, q); got_host = 1; } if (got_host) *allow_launch = TRUE; return 1; } |
︙ | ︙ | |||
120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 | /* Try to take account of --display and what have you. */ if (!(display = gdk_get_display())) /* fall back to traditional method */ display = getenv("DISPLAY"); return dupstr(display); } int main(int argc, char **argv) { extern int pt_main(int argc, char **argv); sk_init(); flags = FLAG_VERBOSE | FLAG_INTERACTIVE; default_protocol = be_default_protocol; /* Find the appropriate default port. */ { Backend *b = backend_from_proto(default_protocol); default_port = 0; /* illegal */ if (b) default_port = b->default_port; } | > > > > > | > > | 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 | /* Try to take account of --display and what have you. */ if (!(display = gdk_get_display())) /* fall back to traditional method */ display = getenv("DISPLAY"); return dupstr(display); } const int share_can_be_downstream = TRUE; const int share_can_be_upstream = TRUE; int main(int argc, char **argv) { extern int pt_main(int argc, char **argv); int ret; sk_init(); flags = FLAG_VERBOSE | FLAG_INTERACTIVE; default_protocol = be_default_protocol; /* Find the appropriate default port. */ { Backend *b = backend_from_proto(default_protocol); default_port = 0; /* illegal */ if (b) default_port = b->default_port; } ret = pt_main(argc, argv); cleanup_exit(ret); return ret; /* not reached, but placates optimisers */ } |
Changes to unix/uxser.c.
︙ | ︙ | |||
56 57 58 59 60 61 62 | static tree234 *serial_by_fd = NULL; static int serial_select_result(int fd, int event); static void serial_uxsel_setup(Serial serial); static void serial_try_write(Serial serial); | | | > | | 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 | static tree234 *serial_by_fd = NULL; static int serial_select_result(int fd, int event); static void serial_uxsel_setup(Serial serial); static void serial_try_write(Serial serial); static const char *serial_configure(Serial serial, Conf *conf) { struct termios options; int bflag, bval, speed, flow, parity; const char *str; char *msg; if (serial->fd < 0) return "Unable to reconfigure already-closed serial connection"; tcgetattr(serial->fd, &options); /* * Find the appropriate baud rate flag. */ speed = conf_get_int(conf, CONF_serspeed); #define SETBAUD(x) (bflag = B ## x, bval = x) #define CHECKBAUD(x) do { if (speed >= x) SETBAUD(x); } while (0) SETBAUD(50); #ifdef B75 CHECKBAUD(75); #endif #ifdef B110 CHECKBAUD(110); #endif |
︙ | ︙ | |||
179 180 181 182 183 184 185 | cfsetispeed(&options, bflag); cfsetospeed(&options, bflag); msg = dupprintf("Configuring baud rate %d", bval); logevent(serial->frontend, msg); sfree(msg); options.c_cflag &= ~CSIZE; | | | > | > | | > | | | 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 | cfsetispeed(&options, bflag); cfsetospeed(&options, bflag); msg = dupprintf("Configuring baud rate %d", bval); logevent(serial->frontend, msg); sfree(msg); options.c_cflag &= ~CSIZE; switch (conf_get_int(conf, CONF_serdatabits)) { case 5: options.c_cflag |= CS5; break; case 6: options.c_cflag |= CS6; break; case 7: options.c_cflag |= CS7; break; case 8: options.c_cflag |= CS8; break; default: return "Invalid number of data bits (need 5, 6, 7 or 8)"; } msg = dupprintf("Configuring %d data bits", conf_get_int(conf, CONF_serdatabits)); logevent(serial->frontend, msg); sfree(msg); if (conf_get_int(conf, CONF_serstopbits) >= 4) { options.c_cflag |= CSTOPB; } else { options.c_cflag &= ~CSTOPB; } msg = dupprintf("Configuring %d stop bits", (options.c_cflag & CSTOPB ? 2 : 1)); logevent(serial->frontend, msg); sfree(msg); options.c_iflag &= ~(IXON|IXOFF); #ifdef CRTSCTS options.c_cflag &= ~CRTSCTS; #endif #ifdef CNEW_RTSCTS options.c_cflag &= ~CNEW_RTSCTS; #endif flow = conf_get_int(conf, CONF_serflow); if (flow == SER_FLOW_XONXOFF) { options.c_iflag |= IXON | IXOFF; str = "XON/XOFF"; } else if (flow == SER_FLOW_RTSCTS) { #ifdef CRTSCTS options.c_cflag |= CRTSCTS; #endif #ifdef CNEW_RTSCTS options.c_cflag |= CNEW_RTSCTS; #endif str = "RTS/CTS"; } else str = "no"; msg = dupprintf("Configuring %s flow control", str); logevent(serial->frontend, msg); sfree(msg); /* Parity */ parity = conf_get_int(conf, CONF_serparity); if (parity == SER_PAR_ODD) { options.c_cflag |= PARENB; options.c_cflag |= PARODD; str = "odd"; } else if (parity == SER_PAR_EVEN) { options.c_cflag |= PARENB; options.c_cflag &= ~PARODD; str = "even"; } else { options.c_cflag &= ~PARENB; str = "no"; } |
︙ | ︙ | |||
280 281 282 283 284 285 286 | * * Returns an error message, or NULL on success. * * Also places the canonical host name into `realhost'. It must be * freed by the caller. */ static const char *serial_init(void *frontend_handle, void **backend_handle, | | > > | > | | | | 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 | * * Returns an error message, or NULL on success. * * Also places the canonical host name into `realhost'. It must be * freed by the caller. */ static const char *serial_init(void *frontend_handle, void **backend_handle, Conf *conf, char *host, int port, char **realhost, int nodelay, int keepalive) { Serial serial; const char *err; char *line; serial = snew(struct serial_backend_data); *backend_handle = serial; serial->frontend = frontend_handle; serial->finished = FALSE; serial->inbufsize = 0; bufchain_init(&serial->output_data); line = conf_get_str(conf, CONF_serline); { char *msg = dupprintf("Opening serial device %s", line); logevent(serial->frontend, msg); sfree(msg); } serial->fd = open(line, O_RDWR | O_NOCTTY | O_NDELAY | O_NONBLOCK); if (serial->fd < 0) return "Unable to open serial port"; cloexec(serial->fd); err = serial_configure(serial, conf); if (err) return err; *realhost = dupstr(line); if (!serial_by_fd) serial_by_fd = newtree234(serial_compare_by_fd); add234(serial_by_fd, serial); serial_uxsel_setup(serial); |
︙ | ︙ | |||
345 346 347 348 349 350 351 | serial_close(serial); bufchain_clear(&serial->output_data); sfree(serial); } | | | | 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 | serial_close(serial); bufchain_clear(&serial->output_data); sfree(serial); } static void serial_reconfig(void *handle, Conf *conf) { Serial serial = (Serial) handle; /* * FIXME: what should we do if this returns an error? */ serial_configure(serial, conf); } static int serial_select_result(int fd, int event) { Serial serial; char buf[4096]; int ret; |
︙ | ︙ |
Changes to unix/uxsftp.c.
︙ | ︙ | |||
30 31 32 33 34 35 36 | void uxsel_input_remove(int id) { } char *x_get_default(const char *key) { return NULL; /* this is a stub */ } | | | < < | | < | < | | 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 | void uxsel_input_remove(int id) { } char *x_get_default(const char *key) { return NULL; /* this is a stub */ } void platform_get_x11_auth(struct X11Display *display, Conf *conf) { /* Do nothing, therefore no auth. */ } const int platform_uses_x11_unix_by_default = TRUE; /* * Default settings that are specific to PSFTP. */ char *platform_default_s(const char *name) { return NULL; } int platform_default_i(const char *name, int def) { return def; } FontSpec *platform_default_fontspec(const char *name) { return fontspec_new(""); } Filename *platform_default_filename(const char *name) { if (!strcmp(name, "LogFileName")) return filename_from_str("putty.log"); else return filename_from_str(""); } char *get_ttymode(void *frontend, const char *mode) { return NULL; } int get_userpass_input(prompts_t *p, unsigned char *in, int inlen) { int ret; |
︙ | ︙ | |||
121 122 123 124 125 126 127 | } struct RFile { int fd; }; RFile *open_existing_file(char *name, uint64 *size, | | > | > > > | 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 | } struct RFile { int fd; }; RFile *open_existing_file(char *name, uint64 *size, unsigned long *mtime, unsigned long *atime, long *perms) { int fd; RFile *ret; fd = open(name, O_RDONLY); if (fd < 0) return NULL; ret = snew(RFile); ret->fd = fd; if (size || mtime || atime || perms) { struct stat statbuf; if (fstat(fd, &statbuf) < 0) { fprintf(stderr, "%s: stat: %s\n", name, strerror(errno)); memset(&statbuf, 0, sizeof(statbuf)); } if (size) *size = uint64_make((statbuf.st_size >> 16) >> 16, statbuf.st_size); if (mtime) *mtime = statbuf.st_mtime; if (atime) *atime = statbuf.st_atime; if (perms) *perms = statbuf.st_mode; } return ret; } int read_from_file(RFile *f, void *buffer, int length) { |
︙ | ︙ | |||
170 171 172 173 174 175 176 | } struct WFile { int fd; char *name; }; | | | > | 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 | } struct WFile { int fd; char *name; }; WFile *open_new_file(char *name, long perms) { int fd; WFile *ret; fd = open(name, O_CREAT | O_TRUNC | O_WRONLY, (mode_t)(perms ? perms : 0666)); if (fd < 0) return NULL; ret = snew(WFile); ret->fd = fd; ret->name = dupstr(name); |
︙ | ︙ | |||
438 439 440 441 442 443 444 | * optionally stdin. */ static int ssh_sftp_do_select(int include_stdin, int no_fds_ok) { fd_set rset, wset, xset; int i, fdcount, fdsize, *fdlist; int fd, fdstate, rwx, ret, maxfd; | | > | 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 | * optionally stdin. */ static int ssh_sftp_do_select(int include_stdin, int no_fds_ok) { fd_set rset, wset, xset; int i, fdcount, fdsize, *fdlist; int fd, fdstate, rwx, ret, maxfd; unsigned long now = GETTICKCOUNT(); unsigned long next; fdlist = NULL; fdcount = fdsize = 0; do { /* Count the currently active fds. */ |
︙ | ︙ | |||
483 484 485 486 487 488 489 | if (rwx & 4) FD_SET_MAX(fd, maxfd, xset); } if (include_stdin) FD_SET_MAX(0, maxfd, rset); | > > > > > > > > | > | | | | > | > | < < < < | | | | | < < < < < < < | < < < < < < < | | | < < | 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 | if (rwx & 4) FD_SET_MAX(fd, maxfd, xset); } if (include_stdin) FD_SET_MAX(0, maxfd, rset); if (toplevel_callback_pending()) { struct timeval tv; tv.tv_sec = 0; tv.tv_usec = 0; ret = select(maxfd, &rset, &wset, &xset, &tv); if (ret == 0) run_toplevel_callbacks(); } else if (run_timers(now, &next)) { do { unsigned long then; long ticks; struct timeval tv; then = now; now = GETTICKCOUNT(); if (now - then > next - then) ticks = 0; else ticks = next - now; tv.tv_sec = ticks / 1000; tv.tv_usec = ticks % 1000 * 1000; ret = select(maxfd, &rset, &wset, &xset, &tv); if (ret == 0) now = next; else now = GETTICKCOUNT(); } while (ret < 0 && errno == EINTR); } else { ret = select(maxfd, &rset, &wset, &xset, NULL); } } while (ret == 0); if (ret < 0) { perror("select"); exit(1); } |
︙ | ︙ | |||
545 546 547 548 549 550 551 552 553 554 555 556 557 558 | if (FD_ISSET(fd, &rset)) select_result(fd, 1); if (FD_ISSET(fd, &wset)) select_result(fd, 2); } sfree(fdlist); return FD_ISSET(0, &rset) ? 1 : 0; } /* * Wait for some network data and process it. */ | > > | 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 | if (FD_ISSET(fd, &rset)) select_result(fd, 1); if (FD_ISSET(fd, &wset)) select_result(fd, 2); } sfree(fdlist); run_toplevel_callbacks(); return FD_ISSET(0, &rset) ? 1 : 0; } /* * Wait for some network data and process it. */ |
︙ | ︙ | |||
575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 | buf = NULL; buflen = bufsize = 0; while (1) { ret = ssh_sftp_do_select(TRUE, no_fds_ok); if (ret < 0) { printf("connection died\n"); return NULL; /* woop woop */ } if (ret > 0) { if (buflen >= bufsize) { bufsize = buflen + 512; buf = sresize(buf, bufsize, char); } ret = read(0, buf+buflen, 1); if (ret < 0) { perror("read"); return NULL; } if (ret == 0) { /* eof on stdin; no error, but no answer either */ return NULL; } if (buf[buflen++] == '\n') { /* we have a full line */ return buf; } } } } /* * Main program: do platform-specific initialisation and then call * psftp_main(). */ int main(int argc, char *argv[]) { uxsel_init(); return psftp_main(argc, argv); } | > > > > > | 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 | buf = NULL; buflen = bufsize = 0; while (1) { ret = ssh_sftp_do_select(TRUE, no_fds_ok); if (ret < 0) { printf("connection died\n"); sfree(buf); return NULL; /* woop woop */ } if (ret > 0) { if (buflen >= bufsize) { bufsize = buflen + 512; buf = sresize(buf, bufsize, char); } ret = read(0, buf+buflen, 1); if (ret < 0) { perror("read"); sfree(buf); return NULL; } if (ret == 0) { /* eof on stdin; no error, but no answer either */ sfree(buf); return NULL; } if (buf[buflen++] == '\n') { /* we have a full line */ return buf; } } } } void frontend_net_error_pending(void) {} /* * Main program: do platform-specific initialisation and then call * psftp_main(). */ int main(int argc, char *argv[]) { uxsel_init(); return psftp_main(argc, argv); } |
Added unix/uxshare.c.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 | /* * Unix implementation of SSH connection-sharing IPC setup. */ #include <stdio.h> #include <assert.h> #include <errno.h> #include <unistd.h> #include <fcntl.h> #include <sys/stat.h> #include <sys/types.h> #include <sys/file.h> #define DEFINE_PLUG_METHOD_MACROS #include "tree234.h" #include "putty.h" #include "network.h" #include "proxy.h" #include "ssh.h" #define CONNSHARE_SOCKETDIR_PREFIX "/tmp/putty-connshare" /* * Functions provided by uxnet.c to help connection sharing. */ SockAddr unix_sock_addr(const char *path); Socket new_unix_listener(SockAddr listenaddr, Plug plug); static char *make_dirname(const char *name, char **parent_out) { char *username, *dirname, *parent; username = get_username(); parent = dupprintf("%s.%s", CONNSHARE_SOCKETDIR_PREFIX, username); sfree(username); assert(*parent == '/'); dirname = dupprintf("%s/%s", parent, name); if (parent_out) *parent_out = parent; else sfree(parent); return dirname; } static char *make_dir_and_check_ours(const char *dirname) { struct stat st; /* * Create the directory. We might have created it before, so * EEXIST is an OK error; but anything else is doom. */ if (mkdir(dirname, 0700) < 0 && errno != EEXIST) return dupprintf("%s: mkdir: %s", dirname, strerror(errno)); /* * Now check that that directory is _owned by us_ and not writable * by anybody else. This protects us against somebody else * previously having created the directory in a way that's * writable to us, and thus manipulating us into creating the * actual socket in a directory they can see so that they can * connect to it and use our authenticated SSH sessions. */ if (stat(dirname, &st) < 0) return dupprintf("%s: stat: %s", dirname, strerror(errno)); if (st.st_uid != getuid()) return dupprintf("%s: directory owned by uid %d, not by us", dirname, st.st_uid); if ((st.st_mode & 077) != 0) return dupprintf("%s: directory has overgenerous permissions %03o" " (expected 700)", dirname, st.st_mode & 0777); return NULL; } int platform_ssh_share(const char *pi_name, Conf *conf, Plug downplug, Plug upplug, Socket *sock, char **logtext, char **ds_err, char **us_err, int can_upstream, int can_downstream) { char *name, *parentdirname, *dirname, *lockname, *sockname, *err; int lockfd; Socket retsock; /* * Transform the platform-independent version of the connection * identifier into something valid for a Unix socket, by escaping * slashes (and, while we're here, any control characters). */ { const char *p; char *q; name = snewn(1+3*strlen(pi_name), char); for (p = pi_name, q = name; *p; p++) { if (*p == '/' || *p == '%' || (unsigned char)*p < 0x20 || *p == 0x7f) { q += sprintf(q, "%%%02x", (unsigned char)*p); } else { *q++ = *p; } } *q = '\0'; } /* * First, make sure our subdirectory exists. We must create two * levels of directory - the one for this particular connection, * and the containing one for our username. */ dirname = make_dirname(name, &parentdirname); if ((err = make_dir_and_check_ours(parentdirname)) != NULL) { *logtext = err; sfree(dirname); sfree(parentdirname); sfree(name); return SHARE_NONE; } sfree(parentdirname); if ((err = make_dir_and_check_ours(dirname)) != NULL) { *logtext = err; sfree(dirname); sfree(name); return SHARE_NONE; } /* * Acquire a lock on a file in that directory. */ lockname = dupcat(dirname, "/lock", (char *)NULL); lockfd = open(lockname, O_CREAT | O_RDWR | O_TRUNC, 0600); if (lockfd < 0) { *logtext = dupprintf("%s: open: %s", lockname, strerror(errno)); sfree(dirname); sfree(lockname); sfree(name); return SHARE_NONE; } if (flock(lockfd, LOCK_EX) < 0) { *logtext = dupprintf("%s: flock(LOCK_EX): %s", lockname, strerror(errno)); sfree(dirname); sfree(lockname); close(lockfd); sfree(name); return SHARE_NONE; } sockname = dupprintf("%s/socket", dirname); *logtext = NULL; if (can_downstream) { retsock = new_connection(unix_sock_addr(sockname), "", 0, 0, 1, 0, 0, downplug, conf); if (sk_socket_error(retsock) == NULL) { sfree(*logtext); *logtext = sockname; *sock = retsock; sfree(dirname); sfree(lockname); close(lockfd); sfree(name); return SHARE_DOWNSTREAM; } sfree(*ds_err); *ds_err = dupprintf("%s: %s", sockname, sk_socket_error(retsock)); sk_close(retsock); } if (can_upstream) { retsock = new_unix_listener(unix_sock_addr(sockname), upplug); if (sk_socket_error(retsock) == NULL) { sfree(*logtext); *logtext = sockname; *sock = retsock; sfree(dirname); sfree(lockname); close(lockfd); sfree(name); return SHARE_UPSTREAM; } sfree(*us_err); *us_err = dupprintf("%s: %s", sockname, sk_socket_error(retsock)); sk_close(retsock); } /* One of the above clauses ought to have happened. */ assert(*logtext || *ds_err || *us_err); sfree(dirname); sfree(lockname); sfree(sockname); close(lockfd); sfree(name); return SHARE_NONE; } void platform_ssh_share_cleanup(const char *name) { char *dirname, *filename; dirname = make_dirname(name, NULL); filename = dupcat(dirname, "/socket", (char *)NULL); remove(filename); sfree(filename); filename = dupcat(dirname, "/lock", (char *)NULL); remove(filename); sfree(filename); rmdir(dirname); /* * We deliberately _don't_ clean up the parent directory * /tmp/putty-connshare.<username>, because if we leave it around * then it reduces the ability for other users to be a nuisance by * putting their own directory in the way of it. */ sfree(dirname); } |
Changes to unix/uxstore.c.
︙ | ︙ | |||
162 163 164 165 166 167 168 | char *filename; FILE *fp; *errmsg = NULL; /* * Start by making sure the .putty directory and its sessions | | < < < | | > | | > > | > > | > > > > | | | 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 | char *filename; FILE *fp; *errmsg = NULL; /* * Start by making sure the .putty directory and its sessions * subdir actually exist. */ filename = make_filename(INDEX_DIR, NULL); if (mkdir(filename, 0700) < 0 && errno != EEXIST) { *errmsg = dupprintf("Unable to save session: mkdir(\"%s\") " "returned '%s'", filename, strerror(errno)); sfree(filename); return NULL; } sfree(filename); filename = make_filename(INDEX_SESSIONDIR, NULL); if (mkdir(filename, 0700) < 0 && errno != EEXIST) { *errmsg = dupprintf("Unable to save session: mkdir(\"%s\") " "returned '%s'", filename, strerror(errno)); sfree(filename); return NULL; } sfree(filename); filename = make_filename(INDEX_SESSION, sessionname); fp = fopen(filename, "w"); if (!fp) { *errmsg = dupprintf("Unable to save session: open(\"%s\") " "returned '%s'", filename, strerror(errno)); sfree(filename); return NULL; /* can't open */ } sfree(filename); return fp; } |
︙ | ︙ | |||
295 296 297 298 299 300 301 | ret = newtree234(keycmp); while ( (line = fgetline(fp)) ) { char *value = strchr(line, '='); struct skeyval *kv; | | > > | | < < | < | 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 | ret = newtree234(keycmp); while ( (line = fgetline(fp)) ) { char *value = strchr(line, '='); struct skeyval *kv; if (!value) { sfree(line); continue; } *value++ = '\0'; value[strcspn(value, "\r\n")] = '\0'; /* trim trailing NL */ kv = snew(struct skeyval); kv->key = dupstr(line); kv->value = dupstr(value); add234(ret, kv); sfree(line); } fclose(fp); return ret; } char *read_setting_s(void *handle, const char *key) { tree234 *tree = (tree234 *)handle; const char *val; struct skeyval tmp, *kv; tmp.key = key; if (tree != NULL && (kv = find234(tree, &tmp, NULL)) != NULL) { val = kv->value; assert(val != NULL); } else val = get_setting(key); if (!val) return NULL; else return dupstr(val); } int read_setting_i(void *handle, const char *key, int defvalue) { tree234 *tree = (tree234 *)handle; const char *val; struct skeyval tmp, *kv; |
︙ | ︙ | |||
356 357 358 359 360 361 362 | if (!val) return defvalue; else return atoi(val); } | | > > | > > | < | > | > | | | > | | | > > > > > > | | | | | 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 | if (!val) return defvalue; else return atoi(val); } FontSpec *read_setting_fontspec(void *handle, const char *name) { /* * In GTK1-only PuTTY, we used to store font names simply as a * valid X font description string (logical or alias), under a * bare key such as "Font". * * In GTK2 PuTTY, we have a prefix system where "client:" * indicates a Pango font and "server:" an X one; existing * configuration needs to be reinterpreted as having the * "server:" prefix, so we change the storage key from the * provided name string (e.g. "Font") to a suffixed one * ("FontName"). */ char *suffname = dupcat(name, "Name", NULL); char *tmp; if ((tmp = read_setting_s(handle, suffname)) != NULL) { FontSpec *fs = fontspec_new(tmp); sfree(suffname); sfree(tmp); return fs; /* got new-style name */ } sfree(suffname); /* Fall back to old-style name. */ tmp = read_setting_s(handle, name); if (tmp && *tmp) { char *tmp2 = dupcat("server:", tmp, NULL); FontSpec *fs = fontspec_new(tmp2); sfree(tmp2); sfree(tmp); return fs; } else { sfree(tmp); return NULL; } } Filename *read_setting_filename(void *handle, const char *name) { char *tmp = read_setting_s(handle, name); if (tmp) { Filename *ret = filename_from_str(tmp); sfree(tmp); return ret; } else return NULL; } void write_setting_fontspec(void *handle, const char *name, FontSpec *fs) { /* * read_setting_fontspec had to handle two cases, but when * writing our settings back out we simply always generate the * new-style name. */ char *suffname = dupcat(name, "Name", NULL); write_setting_s(handle, suffname, fs->name); sfree(suffname); } void write_setting_filename(void *handle, const char *name, Filename *result) { write_setting_s(handle, name, result->path); } void close_settings_r(void *handle) { tree234 *tree = (tree234 *)handle; struct skeyval *kv; |
︙ | ︙ | |||
576 577 578 579 580 581 582 | const char *keytype, const char *key) { FILE *rfp, *wfp; char *newtext, *line; int headerlen; char *filename, *tmpfilename; | < < < | | > > > > > > > > > > | | > > > > | > > > > > | 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 | const char *keytype, const char *key) { FILE *rfp, *wfp; char *newtext, *line; int headerlen; char *filename, *tmpfilename; /* * Open both the old file and a new file. */ tmpfilename = make_filename(INDEX_HOSTKEYS_TMP, NULL); wfp = fopen(tmpfilename, "w"); if (!wfp && errno == ENOENT) { char *dir; dir = make_filename(INDEX_DIR, NULL); if (mkdir(dir, 0700) < 0) { char *msg = dupprintf("Unable to store host key: mkdir(\"%s\") " "returned '%s'", dir, strerror(errno)); nonfatal(msg); sfree(dir); sfree(tmpfilename); return; } sfree(dir); wfp = fopen(tmpfilename, "w"); } if (!wfp) { char *msg = dupprintf("Unable to store host key: open(\"%s\") " "returned '%s'", tmpfilename, strerror(errno)); nonfatal(msg); sfree(tmpfilename); return; } filename = make_filename(INDEX_HOSTKEYS, NULL); rfp = fopen(filename, "r"); newtext = dupprintf("%s@%d:%s %s\n", keytype, port, hostname, key); headerlen = 1 + strcspn(newtext, " "); /* count the space too */ /* * Copy all lines from the old file to the new one that _don't_ * involve the same host key identifier as the one we're adding. */ if (rfp) { while ( (line = fgetline(rfp)) ) { if (strncmp(line, newtext, headerlen)) fputs(line, wfp); sfree(line); } fclose(rfp); } /* * Now add the new line at the end. */ fputs(newtext, wfp); fclose(wfp); if (rename(tmpfilename, filename) < 0) { char *msg = dupprintf("Unable to store host key: rename(\"%s\",\"%s\")" " returned '%s'", tmpfilename, filename, strerror(errno)); nonfatal(msg); } sfree(tmpfilename); sfree(filename); sfree(newtext); } void read_random_seed(noise_consumer_t consumer) |
︙ | ︙ | |||
656 657 658 659 660 661 662 663 664 665 | /* * Don't truncate the random seed file if it already exists; if * something goes wrong half way through writing it, it would * be better to leave the old data there than to leave it empty. */ fd = open(fname, O_CREAT | O_WRONLY, 0600); if (fd < 0) { char *dir; dir = make_filename(INDEX_DIR, NULL); | > > > > > > > > | > > > > > > > > > > > > > > > > | > > > > > > | 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 | /* * Don't truncate the random seed file if it already exists; if * something goes wrong half way through writing it, it would * be better to leave the old data there than to leave it empty. */ fd = open(fname, O_CREAT | O_WRONLY, 0600); if (fd < 0) { if (errno != ENOENT) { char *msg = dupprintf("Unable to write random seed: open(\"%s\") " "returned '%s'", fname, strerror(errno)); nonfatal(msg); sfree(msg); sfree(fname); return; } char *dir; dir = make_filename(INDEX_DIR, NULL); if (mkdir(dir, 0700) < 0) { char *msg = dupprintf("Unable to write random seed: mkdir(\"%s\") " "returned '%s'", dir, strerror(errno)); nonfatal(msg); sfree(msg); sfree(fname); sfree(dir); return; } sfree(dir); fd = open(fname, O_CREAT | O_WRONLY, 0600); if (fd < 0) { char *msg = dupprintf("Unable to write random seed: open(\"%s\") " "returned '%s'", fname, strerror(errno)); nonfatal(msg); sfree(msg); sfree(fname); return; } } while (len > 0) { int ret = write(fd, data, len); if (ret < 0) { char *msg = dupprintf("Unable to write random seed: write " "returned '%s'", strerror(errno)); nonfatal(msg); sfree(msg); break; } len -= ret; data = (char *)data + len; } close(fd); sfree(fname); } void cleanup_all(void) { } |
Changes to unix/uxucs.c.
︙ | ︙ | |||
17 18 19 20 21 22 23 | */ int is_dbcs_leadbyte(int codepage, char byte) { return 0; /* we don't do DBCS */ } | | < < < | < < < | 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 | */ int is_dbcs_leadbyte(int codepage, char byte) { return 0; /* we don't do DBCS */ } int mb_to_wc(int codepage, int flags, const char *mbstr, int mblen, wchar_t *wcstr, int wclen) { if (codepage == DEFAULT_CODEPAGE) { int n = 0; mbstate_t state; memset(&state, 0, sizeof state); while (mblen > 0) { size_t i = mbrtowc(wcstr+n, mbstr, (size_t)mblen, &state); if (i == (size_t)-1 || i == (size_t)-2) break; n++; mbstr += i; mblen -= i; } return n; } else if (codepage == CS_NONE) { int n = 0; while (mblen > 0) { wcstr[n] = 0xD800 | (mbstr[0] & 0xFF); n++; mbstr++; mblen--; } return n; } else return charset_to_unicode(&mbstr, &mblen, wcstr, wclen, codepage, NULL, NULL, 0); } int wc_to_mb(int codepage, int flags, const wchar_t *wcstr, int wclen, char *mbstr, int mblen, char *defchr, int *defused, struct unicode_data *ucsdata) { /* FIXME: we should remove the defused param completely... */ if (defused) *defused = 0; if (codepage == DEFAULT_CODEPAGE) { char output[MB_LEN_MAX]; mbstate_t state; int n = 0; memset(&state, 0, sizeof state); while (wclen > 0) { int i = wcrtomb(output, wcstr[0], &state); if (i == (size_t)-1 || i > n - mblen) break; memcpy(mbstr+n, output, i); n += i; wcstr++; wclen--; } return n; } else if (codepage == CS_NONE) { int n = 0; while (wclen > 0 && n < mblen) { if (*wcstr >= 0xD800 && *wcstr < 0xD900) mbstr[n++] = (*wcstr & 0xFF); else if (defchr) |
︙ | ︙ | |||
135 136 137 138 139 140 141 | if (strstr(s, "UTF-8")) ucsdata->line_codepage = CS_UTF8; } } /* * Failing that, line_codepage should be decoded from the | | | 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 | if (strstr(s, "UTF-8")) ucsdata->line_codepage = CS_UTF8; } } /* * Failing that, line_codepage should be decoded from the * specification in conf. */ if (ucsdata->line_codepage == CS_NONE) ucsdata->line_codepage = decode_codepage(linecharset); /* * If line_codepage is _still_ CS_NONE, we assume we're using * the font's own encoding. This has been passed in to us, so |
︙ | ︙ | |||
158 159 160 161 162 163 164 | ret = 1; /* * Set up unitab_line, by translating each individual character * in the line codepage into Unicode. */ for (i = 0; i < 256; i++) { | | > | 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 | ret = 1; /* * Set up unitab_line, by translating each individual character * in the line codepage into Unicode. */ for (i = 0; i < 256; i++) { char c[1]; const char *p; wchar_t wc[1]; int len; c[0] = i; p = c; len = 1; if (ucsdata->line_codepage == CS_NONE) ucsdata->unitab_line[i] = 0xD800 | i; |
︙ | ︙ | |||
212 213 214 215 216 217 218 | } /* * Set up unitab_scoacs. The SCO Alternate Character Set is * simply CP437. */ for (i = 0; i < 256; i++) { | | > | 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 | } /* * Set up unitab_scoacs. The SCO Alternate Character Set is * simply CP437. */ for (i = 0; i < 256; i++) { char c[1]; const char *p; wchar_t wc[1]; int len; c[0] = i; p = c; len = 1; if (1 == charset_to_unicode(&p, &len, wc, 1, CS_CP437, NULL, L"", 0)) ucsdata->unitab_scoacs[i] = wc[0]; |
︙ | ︙ | |||
253 254 255 256 257 258 259 | return "Use font encoding"; return charset_to_localenc(codepage); } const char *cp_enumerate(int index) { int charset; | < < | | > > > > | | | 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 | return "Use font encoding"; return charset_to_localenc(codepage); } const char *cp_enumerate(int index) { int charset; charset = charset_localenc_nth(index); if (charset == CS_NONE) { /* "Use font encoding" comes after all the named charsets */ if (charset_localenc_nth(index-1) != CS_NONE) return "Use font encoding"; return NULL; } return charset_to_localenc(charset); } int decode_codepage(char *cp_name) { if (!cp_name || !*cp_name) return CS_UTF8; return charset_from_localenc(cp_name); } |
Changes to version.c.
1 2 3 4 5 6 7 8 9 10 11 12 13 | /* * PuTTY version numbering */ #define STR1(x) #x #define STR(x) STR1(x) #if defined SNAPSHOT #if defined SVN_REV #define SNAPSHOT_TEXT STR(SNAPSHOT) ":r" STR(SVN_REV) #else #define SNAPSHOT_TEXT STR(SNAPSHOT) | > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | /* * PuTTY version numbering */ #define STR1(x) #x #define STR(x) STR1(x) #ifdef INCLUDE_EMPTY_H /* * Horrible hack to force version.o to be rebuilt unconditionally in * the automake world: empty.h is an empty header file, created by the * makefile and forcibly updated every time make is run. Including it * here causes automake to track it as a dependency, which will cause * version.o to be rebuilt too. * * The space between # and include causes mkfiles.pl's dependency * scanner (for all other makefile types) to ignore this include, * which is correct because only the automake makefile passes * -DINCLUDE_EMPTY_H to enable it. */ # include "empty.h" #endif #if defined SNAPSHOT #if defined SVN_REV #define SNAPSHOT_TEXT STR(SNAPSHOT) ":r" STR(SVN_REV) #else #define SNAPSHOT_TEXT STR(SNAPSHOT) |
︙ | ︙ |
Changes to wcwidth.c.
︙ | ︙ | |||
46 47 48 49 50 51 52 | * up a proper standard for the behavior of UTF-8 character terminals * will require a careful analysis not only of each Unicode character, * but also of each presentation form, something the author of these * routines has avoided to do so far. * * http://www.unicode.org/unicode/reports/tr11/ * | | | | | | 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 | * up a proper standard for the behavior of UTF-8 character terminals * will require a careful analysis not only of each Unicode character, * but also of each presentation form, something the author of these * routines has avoided to do so far. * * http://www.unicode.org/unicode/reports/tr11/ * * Markus Kuhn -- 2007-05-26 (Unicode 5.0) * * Permission to use, copy, modify, and distribute this software * for any purpose and without fee is hereby granted. The author * disclaims all warranties with regard to this software. * * Latest version: http://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c */ #include <wchar.h> #include "putty.h" /* for prototypes */ struct interval { unsigned int first; unsigned int last; }; /* auxiliary function for binary search in interval table */ static int bisearch(unsigned int ucs, const struct interval *table, int max) { int min = 0; int mid; if (ucs < table[0].first || ucs > table[max].last) return 0; while (max >= min) { mid = (min + max) / 2; |
︙ | ︙ | |||
117 118 119 120 121 122 123 | * ISO 8859-1 and WGL4 characters, Unicode control characters, * etc.) have a column width of 1. * * This implementation assumes that wchar_t characters are encoded * in ISO 10646. */ | | | < | | | | | > | | | | | | | | | | | | | | | | | | | | > > > | > | | | > > | > | 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 | * ISO 8859-1 and WGL4 characters, Unicode control characters, * etc.) have a column width of 1. * * This implementation assumes that wchar_t characters are encoded * in ISO 10646. */ int mk_wcwidth(unsigned int ucs) { /* sorted list of non-overlapping intervals of non-spacing characters */ /* generated by "uniset +cat=Me +cat=Mn +cat=Cf -00AD +1160-11FF +200B c" */ static const struct interval combining[] = { { 0x0300, 0x036F }, { 0x0483, 0x0486 }, { 0x0488, 0x0489 }, { 0x0591, 0x05BD }, { 0x05BF, 0x05BF }, { 0x05C1, 0x05C2 }, { 0x05C4, 0x05C5 }, { 0x05C7, 0x05C7 }, { 0x0600, 0x0603 }, { 0x0610, 0x0615 }, { 0x064B, 0x065E }, { 0x0670, 0x0670 }, { 0x06D6, 0x06E4 }, { 0x06E7, 0x06E8 }, { 0x06EA, 0x06ED }, { 0x070F, 0x070F }, { 0x0711, 0x0711 }, { 0x0730, 0x074A }, { 0x07A6, 0x07B0 }, { 0x07EB, 0x07F3 }, { 0x0901, 0x0902 }, { 0x093C, 0x093C }, { 0x0941, 0x0948 }, { 0x094D, 0x094D }, { 0x0951, 0x0954 }, { 0x0962, 0x0963 }, { 0x0981, 0x0981 }, { 0x09BC, 0x09BC }, { 0x09C1, 0x09C4 }, { 0x09CD, 0x09CD }, { 0x09E2, 0x09E3 }, { 0x0A01, 0x0A02 }, { 0x0A3C, 0x0A3C }, { 0x0A41, 0x0A42 }, { 0x0A47, 0x0A48 }, { 0x0A4B, 0x0A4D }, { 0x0A70, 0x0A71 }, { 0x0A81, 0x0A82 }, { 0x0ABC, 0x0ABC }, { 0x0AC1, 0x0AC5 }, { 0x0AC7, 0x0AC8 }, { 0x0ACD, 0x0ACD }, { 0x0AE2, 0x0AE3 }, { 0x0B01, 0x0B01 }, { 0x0B3C, 0x0B3C }, { 0x0B3F, 0x0B3F }, { 0x0B41, 0x0B43 }, { 0x0B4D, 0x0B4D }, { 0x0B56, 0x0B56 }, { 0x0B82, 0x0B82 }, { 0x0BC0, 0x0BC0 }, { 0x0BCD, 0x0BCD }, { 0x0C3E, 0x0C40 }, { 0x0C46, 0x0C48 }, { 0x0C4A, 0x0C4D }, { 0x0C55, 0x0C56 }, { 0x0CBC, 0x0CBC }, { 0x0CBF, 0x0CBF }, { 0x0CC6, 0x0CC6 }, { 0x0CCC, 0x0CCD }, { 0x0CE2, 0x0CE3 }, { 0x0D41, 0x0D43 }, { 0x0D4D, 0x0D4D }, { 0x0DCA, 0x0DCA }, { 0x0DD2, 0x0DD4 }, { 0x0DD6, 0x0DD6 }, { 0x0E31, 0x0E31 }, { 0x0E34, 0x0E3A }, { 0x0E47, 0x0E4E }, { 0x0EB1, 0x0EB1 }, { 0x0EB4, 0x0EB9 }, { 0x0EBB, 0x0EBC }, { 0x0EC8, 0x0ECD }, { 0x0F18, 0x0F19 }, { 0x0F35, 0x0F35 }, { 0x0F37, 0x0F37 }, { 0x0F39, 0x0F39 }, { 0x0F71, 0x0F7E }, { 0x0F80, 0x0F84 }, { 0x0F86, 0x0F87 }, { 0x0F90, 0x0F97 }, { 0x0F99, 0x0FBC }, { 0x0FC6, 0x0FC6 }, { 0x102D, 0x1030 }, { 0x1032, 0x1032 }, { 0x1036, 0x1037 }, { 0x1039, 0x1039 }, { 0x1058, 0x1059 }, { 0x1160, 0x11FF }, { 0x135F, 0x135F }, { 0x1712, 0x1714 }, { 0x1732, 0x1734 }, { 0x1752, 0x1753 }, { 0x1772, 0x1773 }, { 0x17B4, 0x17B5 }, { 0x17B7, 0x17BD }, { 0x17C6, 0x17C6 }, { 0x17C9, 0x17D3 }, { 0x17DD, 0x17DD }, { 0x180B, 0x180D }, { 0x18A9, 0x18A9 }, { 0x1920, 0x1922 }, { 0x1927, 0x1928 }, { 0x1932, 0x1932 }, { 0x1939, 0x193B }, { 0x1A17, 0x1A18 }, { 0x1B00, 0x1B03 }, { 0x1B34, 0x1B34 }, { 0x1B36, 0x1B3A }, { 0x1B3C, 0x1B3C }, { 0x1B42, 0x1B42 }, { 0x1B6B, 0x1B73 }, { 0x1DC0, 0x1DCA }, { 0x1DFE, 0x1DFF }, { 0x200B, 0x200F }, { 0x202A, 0x202E }, { 0x2060, 0x2063 }, { 0x206A, 0x206F }, { 0x20D0, 0x20EF }, { 0x302A, 0x302F }, { 0x3099, 0x309A }, { 0xA806, 0xA806 }, { 0xA80B, 0xA80B }, { 0xA825, 0xA826 }, { 0xFB1E, 0xFB1E }, { 0xFE00, 0xFE0F }, { 0xFE20, 0xFE23 }, { 0xFEFF, 0xFEFF }, { 0xFFF9, 0xFFFB }, { 0x10A01, 0x10A03 }, { 0x10A05, 0x10A06 }, { 0x10A0C, 0x10A0F }, { 0x10A38, 0x10A3A }, { 0x10A3F, 0x10A3F }, { 0x1D167, 0x1D169 }, { 0x1D173, 0x1D182 }, { 0x1D185, 0x1D18B }, { 0x1D1AA, 0x1D1AD }, { 0x1D242, 0x1D244 }, { 0xE0001, 0xE0001 }, { 0xE0020, 0xE007F }, { 0xE0100, 0xE01EF } }; /* test for 8-bit control characters */ if (ucs == 0) return 0; if (ucs < 32 || (ucs >= 0x7f && ucs < 0xa0)) return -1; |
︙ | ︙ | |||
186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 | (ucs >= 0x1100 && (ucs <= 0x115f || /* Hangul Jamo init. consonants */ ucs == 0x2329 || ucs == 0x232a || (ucs >= 0x2e80 && ucs <= 0xa4cf && ucs != 0x303f) || /* CJK ... Yi */ (ucs >= 0xac00 && ucs <= 0xd7a3) || /* Hangul Syllables */ (ucs >= 0xf900 && ucs <= 0xfaff) || /* CJK Compatibility Ideographs */ (ucs >= 0xfe30 && ucs <= 0xfe6f) || /* CJK Compatibility Forms */ (ucs >= 0xff00 && ucs <= 0xff60) || /* Fullwidth Forms */ (ucs >= 0xffe0 && ucs <= 0xffe6) || (ucs >= 0x20000 && ucs <= 0x2fffd) || (ucs >= 0x30000 && ucs <= 0x3fffd))); } | > | | | | 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 | (ucs >= 0x1100 && (ucs <= 0x115f || /* Hangul Jamo init. consonants */ ucs == 0x2329 || ucs == 0x232a || (ucs >= 0x2e80 && ucs <= 0xa4cf && ucs != 0x303f) || /* CJK ... Yi */ (ucs >= 0xac00 && ucs <= 0xd7a3) || /* Hangul Syllables */ (ucs >= 0xf900 && ucs <= 0xfaff) || /* CJK Compatibility Ideographs */ (ucs >= 0xfe10 && ucs <= 0xfe19) || /* Vertical forms */ (ucs >= 0xfe30 && ucs <= 0xfe6f) || /* CJK Compatibility Forms */ (ucs >= 0xff00 && ucs <= 0xff60) || /* Fullwidth Forms */ (ucs >= 0xffe0 && ucs <= 0xffe6) || (ucs >= 0x20000 && ucs <= 0x2fffd) || (ucs >= 0x30000 && ucs <= 0x3fffd))); } int mk_wcswidth(const unsigned int *pwcs, size_t n) { int w, width = 0; for (;*pwcs && n-- > 0; pwcs++) if ((w = mk_wcwidth(*pwcs)) < 0) return -1; else width += w; return width; } /* * The following functions are the same as mk_wcwidth() and * mk_wcswidth(), except that spacing characters in the East Asian * Ambiguous (A) category as defined in Unicode Technical Report #11 * have a column width of 2. This variant might be useful for users of * CJK legacy encodings who want to migrate to UCS without changing * the traditional terminal character-width behaviour. It is not * otherwise recommended for general use. */ int mk_wcwidth_cjk(unsigned int ucs) { /* sorted list of non-overlapping intervals of East Asian Ambiguous * characters, generated by "uniset +WIDTH-A -cat=Me -cat=Mn -cat=Cf c" */ static const struct interval ambiguous[] = { { 0x00A1, 0x00A1 }, { 0x00A4, 0x00A4 }, { 0x00A7, 0x00A8 }, { 0x00AA, 0x00AA }, { 0x00AE, 0x00AE }, { 0x00B0, 0x00B4 }, { 0x00B6, 0x00BA }, { 0x00BC, 0x00BF }, { 0x00C6, 0x00C6 }, |
︙ | ︙ | |||
285 286 287 288 289 290 291 | sizeof(ambiguous) / sizeof(struct interval) - 1)) return 2; return mk_wcwidth(ucs); } | | | 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 | sizeof(ambiguous) / sizeof(struct interval) - 1)) return 2; return mk_wcwidth(ucs); } int mk_wcswidth_cjk(const unsigned int *pwcs, size_t n) { int w, width = 0; for (;*pwcs && n-- > 0; pwcs++) if ((w = mk_wcwidth_cjk(*pwcs)) < 0) return -1; else width += w; return width; } |
Changes to wildcard.c.
︙ | ︙ | |||
322 323 324 325 326 327 328 | return 0; /* it's a wildcard! */ } else { if (output) *output++ = *wildcard; wildcard++; } } | > | | 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 | return 0; /* it's a wildcard! */ } else { if (output) *output++ = *wildcard; wildcard++; } } if (output) *output = '\0'; return 1; /* it's clean */ } #ifdef TESTMODE struct test { const char *wildcard; |
︙ | ︙ |
Changes to windows/pageant.rc.
︙ | ︙ | |||
24 25 26 27 28 29 30 | 211 DIALOG DISCARDABLE 0, 0, 330, 200 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "Pageant Key List" FONT 8, "MS Shell Dlg" BEGIN LISTBOX 100, 10, 10, 310, 155, | | < < | | | | 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 | 211 DIALOG DISCARDABLE 0, 0, 330, 200 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "Pageant Key List" FONT 8, "MS Shell Dlg" BEGIN LISTBOX 100, 10, 10, 310, 155, LBS_EXTENDEDSEL | LBS_HASSTRINGS | LBS_USETABSTOPS | WS_VSCROLL | WS_TABSTOP PUSHBUTTON "&Add Key", 101, 75, 162, 60, 14 PUSHBUTTON "&Remove Key", 102, 195, 162, 60, 14 PUSHBUTTON "&Help", 103, 10, 182, 50, 14 DEFPUSHBUTTON "&Close", IDOK, 270, 182, 50, 14 END /* Accelerators used: cl */ 213 DIALOG DISCARDABLE 140, 40, 136, 70 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "About Pageant" FONT 8, "MS Shell Dlg" BEGIN DEFPUSHBUTTON "&Close", IDOK, 82, 52, 48, 14 PUSHBUTTON "View &Licence", 101, 6, 52, 70, 14 CTEXT "Pageant", 102, 10, 6, 120, 8 CTEXT "", 100, 10, 16, 120, 16 CTEXT "\251 1997-2013 Simon Tatham. All rights reserved.", 103, 10, 34, 120, 16 END /* No accelerators used */ 214 DIALOG DISCARDABLE 50, 50, 226, 263 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "PuTTY Licence" FONT 8, "MS Shell Dlg" BEGIN DEFPUSHBUTTON "OK", IDOK, 98, 243, 44, 14 LTEXT "Copyright \251 1997-2013 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 LTEXT "Barry, Justin Bradford, Ben Harris, Malcolm Smith, Ahmad Khalifa,", 1003, 10, 42, 206, 8 LTEXT "Markus Kuhn, Colin Watson, and CORE SDI S.A.", 1004, 10, 50, 206, 8 LTEXT "Permission is hereby granted, free of charge, to any person", 1005, 10, 66, 206, 8 LTEXT "obtaining a copy of this software and associated documentation", 1006, 10, 74, 206, 8 LTEXT "files (the ""Software""), to deal in the Software without restriction,", 1007, 10, 82, 206, 8 LTEXT "including without limitation the rights to use, copy, modify, merge,", 1008, 10, 90, 206, 8 LTEXT "publish, distribute, sublicense, and/or sell copies of the Software,", 1009, 10, 98, 206, 8 LTEXT "and to permit persons to whom the Software is furnished to do so,", 1010, 10, 106, 206, 8 |
︙ | ︙ | |||
91 92 93 94 95 96 97 | END #include "version.rc2" #ifndef NO_MANIFESTS 1 RT_MANIFEST "pageant.mft" #endif /* NO_MANIFESTS */ | < < < < < < < < < < < < | 89 90 91 92 93 94 95 | END #include "version.rc2" #ifndef NO_MANIFESTS 1 RT_MANIFEST "pageant.mft" #endif /* NO_MANIFESTS */ |
Changes to windows/putty.iss.
1 | ; -*- no -*- | | | | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | ; -*- no -*- ; $Id: putty.iss 9998 2013-08-06 17:09:07Z simon $ ; ; -- Inno Setup installer script for PuTTY and its related tools. ; Last tested with Inno Setup 5.0.8. ; ; TODO for future releases: ; ; - It might be nice to have an option to add PSCP, Plink and PSFTP to ; the PATH. See wish `installer-addpath'. ; ; - Maybe a "custom" installation might be useful? Hassle with ; UninstallDisplayIcon, though. [Setup] AppName=PuTTY AppVerName=PuTTY version 0.63 VersionInfoTextVersion=Release 0.63 AppVersion=0.63 VersionInfoVersion=0.63.0.0 AppPublisher=Simon Tatham AppPublisherURL=http://www.chiark.greenend.org.uk/~sgtatham/putty/ AppReadmeFile={app}\README.txt DefaultDirName={pf}\PuTTY DefaultGroupName=PuTTY SetupIconFile=puttyins.ico UninstallDisplayIcon={app}\putty.exe |
︙ | ︙ |
Changes to windows/puttygen.rc.
︙ | ︙ | |||
34 35 36 37 38 39 40 | CAPTION "About PuTTYgen" FONT 8, "MS Shell Dlg" BEGIN DEFPUSHBUTTON "&Close", IDOK, 82, 52, 48, 14 PUSHBUTTON "View &Licence", 101, 6, 52, 70, 14 CTEXT "PuTTYgen", 102, 10, 6, 120, 8 CTEXT "", 100, 10, 16, 120, 16 | | | | 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 | CAPTION "About PuTTYgen" FONT 8, "MS Shell Dlg" BEGIN DEFPUSHBUTTON "&Close", IDOK, 82, 52, 48, 14 PUSHBUTTON "View &Licence", 101, 6, 52, 70, 14 CTEXT "PuTTYgen", 102, 10, 6, 120, 8 CTEXT "", 100, 10, 16, 120, 16 CTEXT "\251 1997-2013 Simon Tatham. All rights reserved.", 103, 10, 34, 120, 16 END /* No accelerators used */ 214 DIALOG DISCARDABLE 50, 50, 226, 263 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "PuTTY Licence" FONT 8, "MS Shell Dlg" BEGIN DEFPUSHBUTTON "OK", IDOK, 98, 243, 44, 14 LTEXT "Copyright \251 1997-2013 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 LTEXT "Barry, Justin Bradford, Ben Harris, Malcolm Smith, Ahmad Khalifa,", 1003, 10, 42, 206, 8 LTEXT "Markus Kuhn, Colin Watson, and CORE SDI S.A.", 1004, 10, 50, 206, 8 LTEXT "Permission is hereby granted, free of charge, to any person", 1005, 10, 66, 206, 8 |
︙ | ︙ |
Changes to windows/version.rc2.
︙ | ︙ | |||
35 36 37 38 39 40 41 | */ #define STR1(x) #x #define STR(x) STR1(x) /* We keep this around even for snapshots, for monotonicity of version * numbering. It needs to be kept up to date. NB _comma_-separated. */ | | | 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | */ #define STR1(x) #x #define STR(x) STR1(x) /* 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,63 #if defined SNAPSHOT /* Make SVN_REV mandatory for snapshots, to avoid issuing binary * version numbers that look like full releases. */ #ifndef SVN_REV #error SVN_REV not defined/nonzero for snapshot build |
︙ | ︙ | |||
109 110 111 112 113 114 115 | /* (On Win98SE and Win2K, we can see most of this on the Version tab * in the file properties in Explorer.) */ BLOCK "StringFileInfo" BEGIN /* "lang-charset" LLLLCCCC = (UK English, Unicode) */ BLOCK "080904B0" BEGIN | | | | | 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 | /* (On Win98SE and Win2K, we can see most of this on the Version tab * in the file properties in Explorer.) */ BLOCK "StringFileInfo" BEGIN /* "lang-charset" LLLLCCCC = (UK English, Unicode) */ BLOCK "080904B0" BEGIN VALUE "CompanyName", "Simon Tatham" /* required :/ */ VALUE "ProductName", "PuTTY suite" VALUE "FileDescription", APPDESC VALUE "InternalName", APPNAME VALUE "OriginalFilename", APPNAME VALUE "FileVersion", VERSION_TEXT VALUE "ProductVersion", VERSION_TEXT VALUE "LegalCopyright", "Copyright \251 1997-2013 Simon Tatham." #if (!defined SNAPSHOT) && (!defined RELEASE) /* Only if VS_FF_PRIVATEBUILD. */ VALUE "PrivateBuild", VERSION_TEXT /* NBI */ #endif END END BLOCK "VarFileInfo" |
︙ | ︙ |
Changes to windows/win_res.rc2.
︙ | ︙ | |||
14 15 16 17 18 19 20 | IDI_MAINICON ICON "putty.ico" IDI_CFGICON ICON "puttycfg.ico" /* Accelerators used: clw */ IDD_ABOUTBOX DIALOG DISCARDABLE 140, 40, 214, 70 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU | | | | | | | | | 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 | IDI_MAINICON ICON "putty.ico" IDI_CFGICON ICON "puttycfg.ico" /* Accelerators used: clw */ IDD_ABOUTBOX DIALOG DISCARDABLE 140, 40, 214, 70 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "About PuTTY" FONT 8, "MS Shell Dlg" BEGIN DEFPUSHBUTTON "&Close", IDOK, 160, 52, 48, 14 PUSHBUTTON "View &Licence", IDA_LICENCE, 6, 52, 70, 14 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-2013 Simon Tatham. All rights reserved.", IDA_TEXT2, 10, 34, 194, 16 END /* Accelerators used: aco */ IDD_MAINBOX DIALOG DISCARDABLE 0, 0, 300, 252 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "PuTTY Configuration" FONT 8, "MS Shell Dlg" CLASS "PuTTYConfigBox" BEGIN END /* Accelerators used: co */ IDD_LOGBOX DIALOG DISCARDABLE 100, 20, 300, 119 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "PuTTY Event Log" FONT 8, "MS Shell Dlg" BEGIN DEFPUSHBUTTON "&Close", IDOK, 135, 102, 44, 14 PUSHBUTTON "C&opy", IDN_COPY, 81, 102, 44, 14 LISTBOX IDN_LIST, 3, 3, 294, 95, LBS_HASSTRINGS | LBS_USETABSTOPS | WS_VSCROLL | LBS_EXTENDEDSEL END /* No accelerators used */ IDD_LICENCEBOX DIALOG DISCARDABLE 50, 50, 226, 263 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "PuTTY Licence" FONT 8, "MS Shell Dlg" BEGIN DEFPUSHBUTTON "OK", IDOK, 98, 243, 44, 14 LTEXT "Copyright \251 1997-2013 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 LTEXT "Barry, Justin Bradford, Ben Harris, Malcolm Smith, Ahmad Khalifa,", 1003, 10, 42, 206, 8 LTEXT "Markus Kuhn, Colin Watson, and CORE SDI S.A.", 1004, 10, 50, 206, 8 LTEXT "Permission is hereby granted, free of charge, to any person", 1005, 10, 66, 206, 8 LTEXT "obtaining a copy of this software and associated documentation", 1006, 10, 74, 206, 8 LTEXT "files (the ""Software""), to deal in the Software without restriction,", 1007, 10, 82, 206, 8 LTEXT "including without limitation the rights to use, copy, modify, merge,", 1008, 10, 90, 206, 8 LTEXT "publish, distribute, sublicense, and/or sell copies of the Software,", 1009, 10, 98, 206, 8 LTEXT "and to permit persons to whom the Software is furnished to do so,", 1010, 10, 106, 206, 8 |
︙ | ︙ |
Changes to windows/wincfg.c.
︙ | ︙ | |||
66 67 68 69 70 71 72 | * Full-screen mode is a Windows peculiarity; hence * scrollbar_in_fullscreen is as well. */ s = ctrl_getset(b, "Window", "scrollback", "Control the scrollback in the window"); ctrl_checkbox(s, "Display scrollbar in full screen mode", 'i', HELPCTX(window_scrollback), | | | | | 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 | * Full-screen mode is a Windows peculiarity; hence * scrollbar_in_fullscreen is as well. */ s = ctrl_getset(b, "Window", "scrollback", "Control the scrollback in the window"); ctrl_checkbox(s, "Display scrollbar in full screen mode", 'i', HELPCTX(window_scrollback), conf_checkbox_handler, I(CONF_scrollbar_in_fullscreen)); /* * Really this wants to go just after `Display scrollbar'. See * if we can find that control, and do some shuffling. */ { int i; for (i = 0; i < s->ncontrols; i++) { c = s->ctrls[i]; if (c->generic.type == CTRL_CHECKBOX && c->generic.context.i == CONF_scrollbar) { /* * Control i is the scrollbar checkbox. * Control s->ncontrols-1 is the scrollbar-in-FS one. */ if (i < s->ncontrols-2) { c = s->ctrls[s->ncontrols-1]; memmove(s->ctrls+i+2, s->ctrls+i+1, |
︙ | ︙ | |||
101 102 103 104 105 106 107 | * Windows has the AltGr key, which has various Windows- * specific options. */ s = ctrl_getset(b, "Terminal/Keyboard", "features", "Enable extra keyboard features:"); ctrl_checkbox(s, "AltGr acts as Compose key", 't', HELPCTX(keyboard_compose), | | | | 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 | * Windows has the AltGr key, which has various Windows- * specific options. */ s = ctrl_getset(b, "Terminal/Keyboard", "features", "Enable extra keyboard features:"); ctrl_checkbox(s, "AltGr acts as Compose key", 't', HELPCTX(keyboard_compose), conf_checkbox_handler, I(CONF_compose_key)); ctrl_checkbox(s, "Control-Alt is different from AltGr", 'd', HELPCTX(keyboard_ctrlalt), conf_checkbox_handler, I(CONF_ctrlaltkeys)); /* * Windows allows an arbitrary .WAV to be played as a bell, and * also the use of the PC speaker. For this we must search the * existing controlset for the radio-button set controlling the * `beep' option, and add extra buttons to it. * |
︙ | ︙ | |||
129 130 131 132 133 134 135 | */ s = ctrl_getset(b, "Terminal/Bell", "style", "Set the style of bell"); { int i; for (i = 0; i < s->ncontrols; i++) { c = s->ctrls[i]; if (c->generic.type == CTRL_RADIO && | | | | 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 | */ s = ctrl_getset(b, "Terminal/Bell", "style", "Set the style of bell"); { int i; for (i = 0; i < s->ncontrols; i++) { c = s->ctrls[i]; if (c->generic.type == CTRL_RADIO && c->generic.context.i == CONF_beep) { assert(c->generic.handler == conf_radiobutton_handler); c->radio.nbuttons += 2; c->radio.buttons = sresize(c->radio.buttons, c->radio.nbuttons, char *); c->radio.buttons[c->radio.nbuttons-1] = dupstr("Play a custom sound file"); c->radio.buttons[c->radio.nbuttons-2] = dupstr("Beep using the PC speaker"); |
︙ | ︙ | |||
155 156 157 158 159 160 161 | break; } } } ctrl_filesel(s, "Custom sound file to play as a bell:", NO_SHORTCUT, FILTER_WAVE_FILES, FALSE, "Select bell sound file", HELPCTX(bell_style), | | | | | | | | | | 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 | break; } } } ctrl_filesel(s, "Custom sound file to play as a bell:", NO_SHORTCUT, FILTER_WAVE_FILES, FALSE, "Select bell sound file", HELPCTX(bell_style), conf_filesel_handler, I(CONF_bell_wavefile)); /* * While we've got this box open, taskbar flashing on a bell is * also Windows-specific. */ ctrl_radiobuttons(s, "Taskbar/caption indication on bell:", 'i', 3, HELPCTX(bell_taskbar), conf_radiobutton_handler, I(CONF_beep_ind), "Disabled", I(B_IND_DISABLED), "Flashing", I(B_IND_FLASH), "Steady", I(B_IND_STEADY), NULL); /* * The sunken-edge border is a Windows GUI feature. */ s = ctrl_getset(b, "Window/Appearance", "border", "Adjust the window border"); ctrl_checkbox(s, "Sunken-edge border (slightly thicker)", 's', HELPCTX(appearance_border), conf_checkbox_handler, I(CONF_sunken_edge)); /* * Configurable font quality settings for Windows. */ s = ctrl_getset(b, "Window/Appearance", "font", "Font settings"); ctrl_checkbox(s, "Allow selection of variable-pitch fonts", NO_SHORTCUT, HELPCTX(appearance_font), variable_pitch_handler, I(0)); ctrl_radiobuttons(s, "Font quality:", 'q', 2, HELPCTX(appearance_font), conf_radiobutton_handler, I(CONF_font_quality), "Antialiased", I(FQ_ANTIALIASED), "Non-Antialiased", I(FQ_NONANTIALIASED), "ClearType", I(FQ_CLEARTYPE), "Default", I(FQ_DEFAULT), NULL); /* * Cyrillic Lock is a horrid misfeature even on Windows, and * the least we can do is ensure it never makes it to any other * platform (at least unless someone fixes it!). */ s = ctrl_getset(b, "Window/Translation", "tweaks", NULL); ctrl_checkbox(s, "Caps Lock acts as Cyrillic switch", 's', HELPCTX(translation_cyrillic), conf_checkbox_handler, I(CONF_xlat_capslockcyr)); /* * On Windows we can use but not enumerate translation tables * from the operating system. Briefly document this. */ s = ctrl_getset(b, "Window/Translation", "trans", "Character set translation on received data"); |
︙ | ︙ | |||
228 229 230 231 232 233 234 | s = ctrl_getset(b, "Window/Translation", "linedraw", str); sfree(str); { int i; for (i = 0; i < s->ncontrols; i++) { c = s->ctrls[i]; if (c->generic.type == CTRL_RADIO && | | | | 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 | s = ctrl_getset(b, "Window/Translation", "linedraw", str); sfree(str); { int i; for (i = 0; i < s->ncontrols; i++) { c = s->ctrls[i]; if (c->generic.type == CTRL_RADIO && c->generic.context.i == CONF_vtmode) { assert(c->generic.handler == conf_radiobutton_handler); c->radio.nbuttons += 3; c->radio.buttons = sresize(c->radio.buttons, c->radio.nbuttons, char *); c->radio.buttons[c->radio.nbuttons-3] = dupstr("Font has XWindows encoding"); c->radio.buttons[c->radio.nbuttons-2] = dupstr("Use font in both ANSI and OEM modes"); |
︙ | ︙ | |||
268 269 270 271 272 273 274 | /* * RTF paste is Windows-specific. */ s = ctrl_getset(b, "Window/Selection", "format", "Formatting of pasted characters"); ctrl_checkbox(s, "Paste to clipboard in RTF as well as plain text", 'f', HELPCTX(selection_rtf), | | | | | | | | | | | | | | | | | < | | 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 | /* * RTF paste is Windows-specific. */ s = ctrl_getset(b, "Window/Selection", "format", "Formatting of pasted characters"); ctrl_checkbox(s, "Paste to clipboard in RTF as well as plain text", 'f', HELPCTX(selection_rtf), conf_checkbox_handler, I(CONF_rtf_paste)); /* * Windows often has no middle button, so we supply a selection * mode in which the more critical Paste action is available on * the right button instead. */ s = ctrl_getset(b, "Window/Selection", "mouse", "Control use of mouse"); ctrl_radiobuttons(s, "Action of mouse buttons:", 'm', 1, HELPCTX(selection_buttons), conf_radiobutton_handler, I(CONF_mouse_is_xterm), "Windows (Middle extends, Right brings up menu)", I(2), "Compromise (Middle extends, Right pastes)", I(0), "xterm (Right extends, Middle pastes)", I(1), NULL); /* * This really ought to go at the _top_ of its box, not the * bottom, so we'll just do some shuffling now we've set it * up... */ c = s->ctrls[s->ncontrols-1]; /* this should be the new control */ memmove(s->ctrls+1, s->ctrls, (s->ncontrols-1)*sizeof(union control *)); s->ctrls[0] = c; /* * Logical palettes don't even make sense anywhere except Windows. */ s = ctrl_getset(b, "Window/Colours", "general", "General options for colour usage"); ctrl_checkbox(s, "Attempt to use logical palettes", 'l', HELPCTX(colours_logpal), conf_checkbox_handler, I(CONF_try_palette)); ctrl_checkbox(s, "Use system colours", 's', HELPCTX(colours_system), conf_checkbox_handler, I(CONF_system_colour)); /* * Resize-by-changing-font is a Windows insanity. */ s = ctrl_getset(b, "Window", "size", "Set the size of the window"); ctrl_radiobuttons(s, "When window is resized:", 'z', 1, HELPCTX(window_resize), conf_radiobutton_handler, I(CONF_resize_action), "Change the number of rows and columns", I(RESIZE_TERM), "Change the size of the font", I(RESIZE_FONT), "Change font size only when maximised", I(RESIZE_EITHER), "Forbid resizing completely", I(RESIZE_DISABLED), NULL); /* * Most of the Window/Behaviour stuff is there to mimic Windows * conventions which PuTTY can optionally disregard. Hence, * most of these options are Windows-specific. */ s = ctrl_getset(b, "Window/Behaviour", "main", NULL); ctrl_checkbox(s, "Window closes on ALT-F4", '4', HELPCTX(behaviour_altf4), conf_checkbox_handler, I(CONF_alt_f4)); ctrl_checkbox(s, "System menu appears on ALT-Space", 'y', HELPCTX(behaviour_altspace), conf_checkbox_handler, I(CONF_alt_space)); ctrl_checkbox(s, "System menu appears on ALT alone", 'l', HELPCTX(behaviour_altonly), conf_checkbox_handler, I(CONF_alt_only)); ctrl_checkbox(s, "Ensure window is always on top", 'e', HELPCTX(behaviour_alwaysontop), conf_checkbox_handler, I(CONF_alwaysontop)); ctrl_checkbox(s, "Full screen on Alt-Enter", 'f', HELPCTX(behaviour_altenter), conf_checkbox_handler, I(CONF_fullscreenonaltenter)); /* * Windows supports a local-command proxy. This also means we * must adjust the text on the `Telnet command' control. */ if (!midsession) { int i; s = ctrl_getset(b, "Connection/Proxy", "basics", NULL); for (i = 0; i < s->ncontrols; i++) { c = s->ctrls[i]; if (c->generic.type == CTRL_RADIO && c->generic.context.i == CONF_proxy_type) { assert(c->generic.handler == conf_radiobutton_handler); c->radio.nbuttons++; c->radio.buttons = sresize(c->radio.buttons, c->radio.nbuttons, char *); c->radio.buttons[c->radio.nbuttons-1] = dupstr("Local"); c->radio.buttondata = sresize(c->radio.buttondata, c->radio.nbuttons, intorptr); c->radio.buttondata[c->radio.nbuttons-1] = I(PROXY_CMD); break; } } for (i = 0; i < s->ncontrols; i++) { c = s->ctrls[i]; if (c->generic.type == CTRL_EDITBOX && c->generic.context.i == CONF_proxy_telnet_command) { assert(c->generic.handler == conf_editbox_handler); sfree(c->generic.label); c->generic.label = dupstr("Telnet command, or local" " proxy command"); break; } } } |
︙ | ︙ | |||
395 396 397 398 399 400 401 | * means to override it. */ if (!midsession && backend_from_proto(PROT_SSH)) { s = ctrl_getset(b, "Connection/SSH/X11", "x11", "X11 forwarding"); ctrl_filesel(s, "X authority file for local display", 't', NULL, FALSE, "Select X authority file", HELPCTX(ssh_tunnels_xauthority), | | | 394 395 396 397 398 399 400 401 402 403 | * means to override it. */ if (!midsession && backend_from_proto(PROT_SSH)) { s = ctrl_getset(b, "Connection/SSH/X11", "x11", "X11 forwarding"); ctrl_filesel(s, "X authority file for local display", 't', NULL, FALSE, "Select X authority file", HELPCTX(ssh_tunnels_xauthority), conf_filesel_handler, I(CONF_xauthfile)); } } |
Changes to windows/wincons.c.
︙ | ︙ | |||
37 38 39 40 41 42 43 | { } void notify_remote_exit(void *frontend) { } | | | 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | { } void notify_remote_exit(void *frontend) { } void timer_change_notify(unsigned long next) { } int verify_ssh_host_key(void *frontend, char *host, int port, char *keytype, char *keystr, char *fingerprint, void (*callback)(void *ctx, int result), void *ctx) { |
︙ | ︙ | |||
197 198 199 200 201 202 203 | } } /* * Ask whether to wipe a session log file before writing to it. * Returns 2 for wipe, 1 for append, 0 for cancel (don't log). */ | | | 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 | } } /* * Ask whether to wipe a session log file before writing to it. * Returns 2 for wipe, 1 for append, 0 for cancel (don't log). */ int askappend(void *frontend, Filename *filename, void (*callback)(void *ctx, int result), void *ctx) { HANDLE hin; DWORD savemode, i; static const char msgtemplate[] = "The session log file \"%.*s\" already exists.\n" |
︙ | ︙ | |||
219 220 221 222 223 224 225 | static const char msgtemplate_batch[] = "The session log file \"%.*s\" already exists.\n" "Logging will not be enabled.\n"; char line[32]; if (console_batch_mode) { | | | | 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 | static const char msgtemplate_batch[] = "The session log file \"%.*s\" already exists.\n" "Logging will not be enabled.\n"; char line[32]; if (console_batch_mode) { fprintf(stderr, msgtemplate_batch, FILENAME_MAX, filename->path); fflush(stderr); return 0; } fprintf(stderr, msgtemplate, FILENAME_MAX, filename->path); fflush(stderr); hin = GetStdHandle(STD_INPUT_HANDLE); GetConsoleMode(hin, &savemode); SetConsoleMode(hin, (savemode | ENABLE_ECHO_INPUT | ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT)); ReadFile(hin, line, sizeof(line) - 1, &i, NULL); |
︙ | ︙ | |||
311 312 313 314 315 316 317 | /* * Zero all the results, in case we abort half-way through. */ { int i; for (i = 0; i < (int)p->n_prompts; i++) | | | 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 | /* * Zero all the results, in case we abort half-way through. */ { int i; for (i = 0; i < (int)p->n_prompts; i++) prompt_set_result(p->prompts[i], ""); } /* * The prompts_t might contain a message to be displayed but no * actual prompt. More usually, though, it will contain * questions that the user needs to answer, in which case we * need to ensure that we're able to get the answers. |
︙ | ︙ | |||
361 362 363 364 365 366 367 | console_data_untrusted(hout, p->instruction, l); if (p->instruction[l-1] != '\n') console_data_untrusted(hout, "\n", 1); } for (curr_prompt = 0; curr_prompt < p->n_prompts; curr_prompt++) { | | > < > > > > | > > > | > > > | > | > | | > > > | < > > > > > > < | 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 | console_data_untrusted(hout, p->instruction, l); if (p->instruction[l-1] != '\n') console_data_untrusted(hout, "\n", 1); } for (curr_prompt = 0; curr_prompt < p->n_prompts; curr_prompt++) { DWORD savemode, newmode; int len; prompt_t *pr = p->prompts[curr_prompt]; GetConsoleMode(hin, &savemode); newmode = savemode | ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT; if (!pr->echo) newmode &= ~ENABLE_ECHO_INPUT; else newmode |= ENABLE_ECHO_INPUT; SetConsoleMode(hin, newmode); console_data_untrusted(hout, pr->prompt, strlen(pr->prompt)); len = 0; while (1) { DWORD ret = 0; BOOL r; prompt_ensure_result_size(pr, len * 5 / 4 + 512); r = ReadFile(hin, pr->result + len, pr->resultsize - len - 1, &ret, NULL); if (!r || ret == 0) { len = -1; break; } len += ret; if (pr->result[len - 1] == '\n') { len--; if (pr->result[len - 1] == '\r') len--; break; } } SetConsoleMode(hin, savemode); if (!pr->echo) { DWORD dummy; WriteFile(hout, "\r\n", 2, &dummy, NULL); } if (len < 0) { return 0; /* failure due to read error */ } pr->result[len] = '\0'; } return 1; /* success */ } void frontend_keypress(void *handle) { /* * This is nothing but a stub, in console code. */ return; } |
Changes to windows/winctrls.c.
︙ | ︙ | |||
444 445 446 447 448 449 450 451 452 453 454 455 456 457 | } SelectObject(hdc, oldfont); ReleaseDC(cp->hwnd, hdc); if (lines) *lines = nlines; return ret; } /* * A single standalone static text control. */ void statictext(struct ctlpos *cp, char *text, int lines, int id) | > > | 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 | } SelectObject(hdc, oldfont); ReleaseDC(cp->hwnd, hdc); if (lines) *lines = nlines; sfree(pwidths); return ret; } /* * A single standalone static text control. */ void statictext(struct ctlpos *cp, char *text, int lines, int id) |
︙ | ︙ | |||
1635 1636 1637 1638 1639 1640 1641 1642 | case CTRL_FONTSELECT: num_ids = 3; escaped = shortcut_escape(ctrl->fontselect.label, ctrl->fontselect.shortcut); shortcuts[nshortcuts++] = ctrl->fontselect.shortcut; statictext(&pos, escaped, 1, base_id); staticbtn(&pos, "", base_id+1, "Change...", base_id+2); sfree(escaped); | > < | 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 | case CTRL_FONTSELECT: num_ids = 3; escaped = shortcut_escape(ctrl->fontselect.label, ctrl->fontselect.shortcut); shortcuts[nshortcuts++] = ctrl->fontselect.shortcut; statictext(&pos, escaped, 1, base_id); staticbtn(&pos, "", base_id+1, "Change...", base_id+2); data = fontspec_new("", 0, 0, 0); sfree(escaped); break; default: assert(!"Can't happen"); num_ids = 0; /* placate gcc */ break; } |
︙ | ︙ | |||
1661 1662 1663 1664 1665 1666 1667 | c->num_ids = num_ids; c->data = data; memcpy(c->shortcuts, shortcuts, sizeof(shortcuts)); winctrl_add(wc, c); winctrl_add_shortcuts(dp, c); if (actual_base_id == base_id) base_id += num_ids; | > > | | 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 | c->num_ids = num_ids; c->data = data; memcpy(c->shortcuts, shortcuts, sizeof(shortcuts)); winctrl_add(wc, c); winctrl_add_shortcuts(dp, c); if (actual_base_id == base_id) base_id += num_ids; } else { sfree(data); } if (colstart >= 0) { /* * Update the ypos in all columns crossed by this * control. */ int i; |
︙ | ︙ | |||
1930 1931 1932 1933 1934 1935 1936 | if (id == 2 && (msg == WM_COMMAND && (HIWORD(wParam) == BN_CLICKED || HIWORD(wParam) == BN_DOUBLECLICKED))) { CHOOSEFONT cf; LOGFONT lf; HDC hdc; | | | | | | | < < < | < | > > | 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 | if (id == 2 && (msg == WM_COMMAND && (HIWORD(wParam) == BN_CLICKED || HIWORD(wParam) == BN_DOUBLECLICKED))) { CHOOSEFONT cf; LOGFONT lf; HDC hdc; FontSpec *fs = (FontSpec *)c->data; hdc = GetDC(0); lf.lfHeight = -MulDiv(fs->height, GetDeviceCaps(hdc, LOGPIXELSY), 72); ReleaseDC(0, hdc); lf.lfWidth = lf.lfEscapement = lf.lfOrientation = 0; lf.lfItalic = lf.lfUnderline = lf.lfStrikeOut = 0; lf.lfWeight = (fs->isbold ? FW_BOLD : 0); lf.lfCharSet = fs->charset; lf.lfOutPrecision = OUT_DEFAULT_PRECIS; lf.lfClipPrecision = CLIP_DEFAULT_PRECIS; lf.lfQuality = DEFAULT_QUALITY; lf.lfPitchAndFamily = FIXED_PITCH | FF_DONTCARE; strncpy(lf.lfFaceName, fs->name, sizeof(lf.lfFaceName) - 1); lf.lfFaceName[sizeof(lf.lfFaceName) - 1] = '\0'; cf.lStructSize = sizeof(cf); cf.hwndOwner = dp->hwnd; cf.lpLogFont = &lf; cf.Flags = (dp->fixed_pitch_fonts ? CF_FIXEDPITCHONLY : 0) | CF_FORCEFONTEXIST | CF_INITTOLOGFONTSTRUCT | CF_SCREENFONTS; if (ChooseFont(&cf)) { fs = fontspec_new(lf.lfFaceName, (lf.lfWeight == FW_BOLD), cf.iPointSize / 10, lf.lfCharSet); dlg_fontsel_set(ctrl, dp, fs); fontspec_free(fs); ctrl->generic.handler(ctrl, dp, dp->data, EVENT_VALCHANGE); } } break; } /* |
︙ | ︙ | |||
2096 2097 2098 2099 2100 2101 2102 | { struct dlgparam *dp = (struct dlgparam *)dlg; struct winctrl *c = dlg_findbyctrl(dp, ctrl); assert(c && c->ctrl->generic.type == CTRL_EDITBOX); SetDlgItemText(dp->hwnd, c->base_id+1, text); } | | | < | 2098 2099 2100 2101 2102 2103 2104 2105 2106 2107 2108 2109 2110 2111 2112 2113 2114 2115 2116 2117 | { struct dlgparam *dp = (struct dlgparam *)dlg; struct winctrl *c = dlg_findbyctrl(dp, ctrl); assert(c && c->ctrl->generic.type == CTRL_EDITBOX); SetDlgItemText(dp->hwnd, c->base_id+1, text); } char *dlg_editbox_get(union control *ctrl, void *dlg) { struct dlgparam *dp = (struct dlgparam *)dlg; struct winctrl *c = dlg_findbyctrl(dp, ctrl); assert(c && c->ctrl->generic.type == CTRL_EDITBOX); return GetDlgItemText_alloc(dp->hwnd, c->base_id+1); } /* The `listbox' functions can also apply to combo boxes. */ void dlg_listbox_clear(union control *ctrl, void *dlg) { struct dlgparam *dp = (struct dlgparam *)dlg; struct winctrl *c = dlg_findbyctrl(dp, ctrl); |
︙ | ︙ | |||
2282 2283 2284 2285 2286 2287 2288 | } if (escaped) { SetDlgItemText(dp->hwnd, id, escaped); sfree(escaped); } } | | | | > > | < > > > | | > | | | | | | | | | 2283 2284 2285 2286 2287 2288 2289 2290 2291 2292 2293 2294 2295 2296 2297 2298 2299 2300 2301 2302 2303 2304 2305 2306 2307 2308 2309 2310 2311 2312 2313 2314 2315 2316 2317 2318 2319 2320 2321 2322 2323 2324 2325 2326 2327 2328 2329 2330 2331 2332 2333 2334 2335 2336 2337 2338 2339 2340 2341 2342 2343 2344 2345 2346 | } if (escaped) { SetDlgItemText(dp->hwnd, id, escaped); sfree(escaped); } } void dlg_filesel_set(union control *ctrl, void *dlg, Filename *fn) { struct dlgparam *dp = (struct dlgparam *)dlg; struct winctrl *c = dlg_findbyctrl(dp, ctrl); assert(c && c->ctrl->generic.type == CTRL_FILESELECT); SetDlgItemText(dp->hwnd, c->base_id+1, fn->path); } Filename *dlg_filesel_get(union control *ctrl, void *dlg) { struct dlgparam *dp = (struct dlgparam *)dlg; struct winctrl *c = dlg_findbyctrl(dp, ctrl); char *tmp; Filename *ret; assert(c && c->ctrl->generic.type == CTRL_FILESELECT); tmp = GetDlgItemText_alloc(dp->hwnd, c->base_id+1); ret = filename_from_str(tmp); sfree(tmp); return ret; } void dlg_fontsel_set(union control *ctrl, void *dlg, FontSpec *fs) { char *buf, *boldstr; struct dlgparam *dp = (struct dlgparam *)dlg; struct winctrl *c = dlg_findbyctrl(dp, ctrl); assert(c && c->ctrl->generic.type == CTRL_FONTSELECT); fontspec_free((FontSpec *)c->data); c->data = fontspec_copy(fs); boldstr = (fs->isbold ? "bold, " : ""); if (fs->height == 0) buf = dupprintf("Font: %s, %sdefault height", fs->name, boldstr); else buf = dupprintf("Font: %s, %s%d-%s", fs->name, boldstr, (fs->height < 0 ? -fs->height : fs->height), (fs->height < 0 ? "pixel" : "point")); SetDlgItemText(dp->hwnd, c->base_id+1, buf); sfree(buf); dlg_auto_set_fixed_pitch_flag(dp); } FontSpec *dlg_fontsel_get(union control *ctrl, void *dlg) { struct dlgparam *dp = (struct dlgparam *)dlg; struct winctrl *c = dlg_findbyctrl(dp, ctrl); assert(c && c->ctrl->generic.type == CTRL_FONTSELECT); return fontspec_copy((FontSpec *)c->data); } /* * Bracketing a large set of updates in these two functions will * cause the front end (if possible) to delay updating the screen * until it's all complete, thus avoiding flicker. */ |
︙ | ︙ | |||
2360 2361 2362 2363 2364 2365 2366 2367 2368 2369 2370 2371 2372 2373 | void dlg_set_focus(union control *ctrl, void *dlg) { struct dlgparam *dp = (struct dlgparam *)dlg; struct winctrl *c = dlg_findbyctrl(dp, ctrl); int id; HWND ctl; switch (ctrl->generic.type) { case CTRL_EDITBOX: id = c->base_id + 1; break; case CTRL_RADIO: for (id = c->base_id + ctrl->radio.nbuttons; id > 1; id--) if (IsDlgButtonChecked(dp->hwnd, id)) break; /* | > > | 2366 2367 2368 2369 2370 2371 2372 2373 2374 2375 2376 2377 2378 2379 2380 2381 | void dlg_set_focus(union control *ctrl, void *dlg) { struct dlgparam *dp = (struct dlgparam *)dlg; struct winctrl *c = dlg_findbyctrl(dp, ctrl); int id; HWND ctl; if (!c) return; switch (ctrl->generic.type) { case CTRL_EDITBOX: id = c->base_id + 1; break; case CTRL_RADIO: for (id = c->base_id + ctrl->radio.nbuttons; id > 1; id--) if (IsDlgButtonChecked(dp->hwnd, id)) break; /* |
︙ | ︙ | |||
2467 2468 2469 2470 2471 2472 2473 | } else return 0; } void dlg_auto_set_fixed_pitch_flag(void *dlg) { struct dlgparam *dp = (struct dlgparam *)dlg; | | > > | | > > > | | | | | | | | 2475 2476 2477 2478 2479 2480 2481 2482 2483 2484 2485 2486 2487 2488 2489 2490 2491 2492 2493 2494 2495 2496 2497 2498 2499 2500 2501 2502 2503 2504 2505 2506 2507 2508 2509 2510 2511 2512 2513 2514 2515 2516 2517 2518 2519 2520 2521 2522 2523 2524 | } else return 0; } void dlg_auto_set_fixed_pitch_flag(void *dlg) { struct dlgparam *dp = (struct dlgparam *)dlg; Conf *conf = (Conf *)dp->data; FontSpec *fs; int quality; HFONT hfont; HDC hdc; TEXTMETRIC tm; int is_var; /* * Attempt to load the current font, and see if it's * variable-pitch. If so, start off the fixed-pitch flag for the * dialog box as false. * * We assume here that any client of the dlg_* mechanism which is * using font selectors at all is also using a normal 'Conf *' * as dp->data. */ quality = conf_get_int(conf, CONF_font_quality); fs = conf_get_fontspec(conf, CONF_font); hfont = CreateFont(0, 0, 0, 0, FW_DONTCARE, FALSE, FALSE, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, FONT_QUALITY(quality), FIXED_PITCH | FF_DONTCARE, fs->name); hdc = GetDC(NULL); if (hdc && SelectObject(hdc, hfont) && GetTextMetrics(hdc, &tm)) { /* Note that the TMPF_FIXED_PITCH bit is defined upside down :-( */ is_var = (tm.tmPitchAndFamily & TMPF_FIXED_PITCH); } else { is_var = FALSE; /* assume it's basically normal */ } if (hdc) ReleaseDC(NULL, hdc); if (hfont) DeleteObject(hfont); if (is_var) dp->fixed_pitch_fonts = FALSE; } int dlg_get_fixed_pitch_flag(void *dlg) { |
︙ | ︙ |
Changes to windows/windefs.c.
1 2 3 4 5 6 7 8 | /* * windefs.c: default settings that are specific to Windows. */ #include "putty.h" #include <commctrl.h> | | < | | < < < | < < | | < | < | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | /* * windefs.c: default settings that are specific to Windows. */ #include "putty.h" #include <commctrl.h> FontSpec *platform_default_fontspec(const char *name) { if (!strcmp(name, "Font")) return fontspec_new("Courier New", 0, 10, ANSI_CHARSET); else return fontspec_new("", 0, 0, 0); } Filename *platform_default_filename(const char *name) { if (!strcmp(name, "LogFileName")) return filename_from_str("putty.log"); else return filename_from_str(""); } char *platform_default_s(const char *name) { if (!strcmp(name, "SerialLine")) return dupstr("COM1"); return NULL; |
︙ | ︙ |
Changes to windows/windlg.c.
︙ | ︙ | |||
40 41 42 43 44 45 46 | */ static struct winctrls ctrls_base, ctrls_panel; static struct dlgparam dp; static char **events = NULL; static int nevents = 0, negsize = 0; | | | 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 | */ static struct winctrls ctrls_base, ctrls_panel; static struct dlgparam dp; static char **events = NULL; static int nevents = 0, negsize = 0; extern Conf *conf; /* defined in window.c */ #define PRINTER_DISABLED_STRING "None (printing disabled)" void force_normal(HWND hwnd) { static int recurse = 0; |
︙ | ︙ | |||
213 214 215 216 217 218 219 | EnableWindow(hwnd, 1); SetActiveWindow(hwnd); return 0; case IDA_WEB: /* Load web browser */ ShellExecute(hwnd, "open", | | | 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 | EnableWindow(hwnd, 1); SetActiveWindow(hwnd); return 0; case IDA_WEB: /* Load web browser */ ShellExecute(hwnd, "open", "http://www.chiark.greenend.org.uk/~sgtatham/putty/", 0, 0, SW_SHOWDEFAULT); return 0; } return 0; case WM_CLOSE: EndDialog(hwnd, TRUE); return 0; |
︙ | ︙ | |||
644 645 646 647 648 649 650 | dp_init(&dp); winctrl_init(&ctrls_base); winctrl_init(&ctrls_panel); dp_add_tree(&dp, &ctrls_base); dp_add_tree(&dp, &ctrls_panel); dp.wintitle = dupprintf("%s Configuration", appname); dp.errtitle = dupprintf("%s Error", appname); | | | | | > | | < | > | > | 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 | dp_init(&dp); winctrl_init(&ctrls_base); winctrl_init(&ctrls_panel); dp_add_tree(&dp, &ctrls_base); dp_add_tree(&dp, &ctrls_panel); dp.wintitle = dupprintf("%s Configuration", appname); dp.errtitle = dupprintf("%s Error", appname); dp.data = conf; dlg_auto_set_fixed_pitch_flag(&dp); dp.shortcuts['g'] = TRUE; /* the treeview: `Cate&gory' */ ret = SaneDialogBox(hinst, MAKEINTRESOURCE(IDD_MAINBOX), NULL, GenericMainDlgProc); ctrl_free_box(ctrlbox); winctrl_cleanup(&ctrls_panel); winctrl_cleanup(&ctrls_base); dp_cleanup(&dp); return ret; } int do_reconfig(HWND hwnd, int protcfginfo) { Conf *backup_conf; int ret, protocol; backup_conf = conf_copy(conf); ctrlbox = ctrl_new_box(); protocol = conf_get_int(conf, CONF_protocol); setup_config_box(ctrlbox, TRUE, protocol, protcfginfo); win_setup_config_box(ctrlbox, &dp.hwnd, has_help(), TRUE, protocol); dp_init(&dp); winctrl_init(&ctrls_base); winctrl_init(&ctrls_panel); dp_add_tree(&dp, &ctrls_base); dp_add_tree(&dp, &ctrls_panel); dp.wintitle = dupprintf("%s Reconfiguration", appname); dp.errtitle = dupprintf("%s Error", appname); dp.data = conf; dlg_auto_set_fixed_pitch_flag(&dp); dp.shortcuts['g'] = TRUE; /* the treeview: `Cate&gory' */ ret = SaneDialogBox(hinst, MAKEINTRESOURCE(IDD_MAINBOX), NULL, GenericMainDlgProc); ctrl_free_box(ctrlbox); winctrl_cleanup(&ctrls_base); winctrl_cleanup(&ctrls_panel); dp_cleanup(&dp); if (!ret) conf_copy_into(conf, backup_conf); conf_free(backup_conf); return ret; } void logevent(void *frontend, const char *string) { char timebuf[40]; |
︙ | ︙ | |||
852 853 854 855 856 857 858 | return 0; } /* * Ask whether to wipe a session log file before writing to it. * Returns 2 for wipe, 1 for append, 0 for cancel (don't log). */ | | | | 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 | return 0; } /* * Ask whether to wipe a session log file before writing to it. * Returns 2 for wipe, 1 for append, 0 for cancel (don't log). */ int askappend(void *frontend, Filename *filename, void (*callback)(void *ctx, int result), void *ctx) { static const char msgtemplate[] = "The session log file \"%.*s\" already exists.\n" "You can overwrite it with a new session log,\n" "append your session log to the end of it,\n" "or disable session logging for this session.\n" "Hit Yes to wipe the file, No to append to it,\n" "or Cancel to disable logging."; char *message; char *mbtitle; int mbret; message = dupprintf(msgtemplate, FILENAME_MAX, filename->path); mbtitle = dupprintf("%s Log to File", appname); mbret = MessageBox(NULL, message, mbtitle, MB_ICONQUESTION | MB_YESNOCANCEL | MB_DEFBUTTON3); socket_reselect_all(); |
︙ | ︙ |
Changes to windows/window.c.
︙ | ︙ | |||
76 77 78 79 80 81 82 | #define WHEEL_DELTA 120 #endif static Mouse_Button translate_button(Mouse_Button button); static LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, unsigned char *output); | | | < < < < | > > > > > > | > > > > | 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 | #define WHEEL_DELTA 120 #endif static Mouse_Button translate_button(Mouse_Button button); static LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, unsigned char *output); static void conftopalette(void); static void systopalette(void); static void init_palette(void); static void init_fonts(int, int); static void another_font(int); static void deinit_fonts(void); static void set_input_locale(HKL); static void update_savedsess_menu(void); static void init_winfuncs(void); static int is_full_screen(void); static void make_full_screen(void); static void clear_full_screen(void); static void flip_full_screen(void); static int process_clipdata(HGLOBAL clipdata, int unicode); /* Window layout information */ static void reset_window(int); static int extra_width, extra_height; static int font_width, font_height, font_dualwidth, font_varpitch; static int offset_width, offset_height; static int was_zoomed = 0; static int prev_rows, prev_cols; static void flash_window(int mode); static void sys_cursor_update(void); static int get_fullscreen_rect(RECT * ss); static int caret_x = -1, caret_y = -1; static int kbd_codepage; static void *ldisc; static Backend *back; static void *backhandle; static struct unicode_data ucsdata; static int session_closed; static int reconfiguring = FALSE; static const struct telnet_special *specials = NULL; static HMENU specials_menu = NULL; static int n_specials = 0; static wchar_t *clipboard_contents; static size_t clipboard_length; #define TIMING_TIMER_ID 1234 static long timing_next_time; static struct { HMENU menu; } popup_menus[2]; enum { SYSMENU, CTXMENU }; static HMENU savedsess_menu; struct wm_netevent_params { /* Used to pass data to wm_netevent_callback */ WPARAM wParam; LPARAM lParam; }; Conf *conf; /* exported to windlg.c */ static void conf_cache_data(void); int cursor_type; int vtmode; static struct sesslist sesslist; /* for saved-session menu */ struct agent_callback { void (*callback)(void *, void *, int); void *callback_ctx; void *data; |
︙ | ︙ | |||
160 161 162 163 164 165 166 | #define FONT_NARROW 0x10 #define FONT_OEM 0x20 #define FONT_OEMBOLD 0x21 #define FONT_OEMUND 0x22 #define FONT_OEMBOLDUND 0x23 | | | | > | 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 | #define FONT_NARROW 0x10 #define FONT_OEM 0x20 #define FONT_OEMBOLD 0x21 #define FONT_OEMUND 0x22 #define FONT_OEMBOLDUND 0x23 #define FONT_MAXNO 0x40 #define FONT_SHIFT 5 static HFONT fonts[FONT_MAXNO]; static LOGFONT lfont; static int fontflag[FONT_MAXNO]; static enum { BOLD_NONE, BOLD_SHADOW, BOLD_FONT } bold_font_mode; static int bold_colours; static enum { UND_LINE, UND_FONT } und_mode; static int descent; #define NCFGCOLOURS 22 #define NEXTCOLOURS 240 |
︙ | ︙ | |||
198 199 200 201 202 203 204 205 206 207 208 209 210 211 | static char *window_name, *icon_name; static int compose_state = 0; static UINT wm_mousewheel = WM_MOUSEWHEEL; /* Dummy routine, only required in plink. */ void ldisc_update(void *frontend, int echo, int edit) { } char *get_ttymode(void *frontend, const char *mode) { | > > > > > > > > > | 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 | static char *window_name, *icon_name; static int compose_state = 0; static UINT wm_mousewheel = WM_MOUSEWHEEL; #define IS_HIGH_VARSEL(wch1, wch2) \ ((wch1) == 0xDB40 && ((wch2) >= 0xDD00 && (wch2) <= 0xDDEF)) #define IS_LOW_VARSEL(wch) \ (((wch) >= 0x180B && (wch) <= 0x180D) || /* MONGOLIAN FREE VARIATION SELECTOR */ \ ((wch) >= 0xFE00 && (wch) <= 0xFE0F)) /* VARIATION SELECTOR 1-16 */ const int share_can_be_downstream = TRUE; const int share_can_be_upstream = TRUE; /* Dummy routine, only required in plink. */ void ldisc_update(void *frontend, int echo, int edit) { } char *get_ttymode(void *frontend, const char *mode) { |
︙ | ︙ | |||
219 220 221 222 223 224 225 | char *realhost; int i; /* * Select protocol. This is farmed out into a table in a * separate file to enable an ssh-free variant. */ | | | > > | > | | | | < | < | | 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 | char *realhost; int i; /* * Select protocol. This is farmed out into a table in a * separate file to enable an ssh-free variant. */ back = backend_from_proto(conf_get_int(conf, CONF_protocol)); if (back == NULL) { char *str = dupprintf("%s Internal Error", appname); MessageBox(NULL, "Unsupported protocol number found", str, MB_OK | MB_ICONEXCLAMATION); sfree(str); cleanup_exit(1); } error = back->init(NULL, &backhandle, conf, conf_get_str(conf, CONF_host), conf_get_int(conf, CONF_port), &realhost, conf_get_int(conf, CONF_tcp_nodelay), conf_get_int(conf, CONF_tcp_keepalives)); back->provide_logctx(backhandle, logctx); if (error) { char *str = dupprintf("%s Error", appname); sprintf(msg, "Unable to open connection to\n" "%.800s\n" "%s", conf_dest(conf), error); MessageBox(NULL, msg, str, MB_ICONERROR | MB_OK); sfree(str); exit(0); } window_name = icon_name = NULL; title = conf_get_str(conf, CONF_wintitle); if (!*title) { sprintf(msg, "%s - %s", realhost, appname); title = msg; } sfree(realhost); set_title(NULL, title); set_icon(NULL, title); /* * Connect the terminal to the backend for resize purposes. */ term_provide_resize_fn(term, back->size, backhandle); /* * Set up a line discipline. */ ldisc = ldisc_create(conf, term, back, backhandle, NULL); /* * Destroy the Restart Session menu item. (This will return * failure if it's already absent, as it will be the very first * time we call this function. We ignore that, because as long * as the menu item ends up not being there, we don't care * whether it was us who removed it or not!) */ for (i = 0; i < lenof(popup_menus); i++) { DeleteMenu(popup_menus[i].menu, IDM_RESTART, MF_BYCOMMAND); } session_closed = FALSE; } static void close_session(void *ignored_context) { char morestuff[100]; int i; session_closed = TRUE; sprintf(morestuff, "%.70s (inactive)", appname); set_icon(NULL, morestuff); |
︙ | ︙ | |||
307 308 309 310 311 312 313 | * delete first to ensure we never end up with more than one. */ for (i = 0; i < lenof(popup_menus); i++) { DeleteMenu(popup_menus[i].menu, IDM_RESTART, MF_BYCOMMAND); InsertMenu(popup_menus[i].menu, IDM_DUPSESS, MF_BYCOMMAND | MF_ENABLED, IDM_RESTART, "&Restart Session"); } | < < < < < < < < < | 324 325 326 327 328 329 330 331 332 333 334 335 336 337 | * delete first to ensure we never end up with more than one. */ for (i = 0; i < lenof(popup_menus); i++) { DeleteMenu(popup_menus[i].menu, IDM_RESTART, MF_BYCOMMAND); InsertMenu(popup_menus[i].menu, IDM_DUPSESS, MF_BYCOMMAND | MF_ENABLED, IDM_RESTART, "&Restart Session"); } } int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) { WNDCLASS wndclass; MSG msg; HRESULT hr; |
︙ | ︙ | |||
358 359 360 361 362 363 364 | if (osVersion.dwMajorVersion < 4 || (osVersion.dwMajorVersion == 4 && osVersion.dwPlatformId != VER_PLATFORM_WIN32_NT)) wm_mousewheel = RegisterWindowMessage("MSWHEEL_ROLLMSG"); init_help(); | > | > | 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 | if (osVersion.dwMajorVersion < 4 || (osVersion.dwMajorVersion == 4 && osVersion.dwPlatformId != VER_PLATFORM_WIN32_NT)) wm_mousewheel = RegisterWindowMessage("MSWHEEL_ROLLMSG"); init_help(); init_winfuncs(); conf = conf_new(); /* * Initialize COM. */ hr = CoInitialize(NULL); if (hr != S_OK && hr != S_FALSE) { char *str = dupprintf("%s Fatal Error", appname); |
︙ | ︙ | |||
391 392 393 394 395 396 397 | /* Find the appropriate default port. */ { Backend *b = backend_from_proto(default_protocol); default_port = 0; /* illegal */ if (b) default_port = b->default_port; } | | | | 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 | /* Find the appropriate default port. */ { Backend *b = backend_from_proto(default_protocol); default_port = 0; /* illegal */ if (b) default_port = b->default_port; } conf_set_int(conf, CONF_logtype, LGTYP_NONE); do_defaults(NULL, conf); p = cmdline; /* * Process a couple of command-line options which are more * easily dealt with before the line is broken up into words. * These are the old-fashioned but convenient @sessionname and |
︙ | ︙ | |||
418 419 420 421 422 423 424 | * very convenient means of automated saved-session * launching, via IDM_SAVEDSESS or Windows 7 jump lists. */ int i = strlen(p); while (i > 1 && isspace(p[i - 1])) i--; p[i] = '\0'; | | | | | | > | | < > | 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 | * very convenient means of automated saved-session * launching, via IDM_SAVEDSESS or Windows 7 jump lists. */ int i = strlen(p); while (i > 1 && isspace(p[i - 1])) i--; p[i] = '\0'; do_defaults(p + 1, conf); if (!conf_launchable(conf) && !do_config()) { cleanup_exit(0); } allow_launch = TRUE; /* allow it to be launched directly */ } else if (*p == '&') { /* * An initial & means we've been given a command line * containing the hex value of a HANDLE for a file * mapping object, which we must then interpret as a * serialised Conf. */ HANDLE filemap; void *cp; unsigned cpsize; if (sscanf(p + 1, "%p:%u", &filemap, &cpsize) == 2 && (cp = MapViewOfFile(filemap, FILE_MAP_READ, 0, 0, cpsize)) != NULL) { conf_deserialise(conf, cp, cpsize); UnmapViewOfFile(cp); CloseHandle(filemap); } else if (!do_config()) { cleanup_exit(0); } allow_launch = TRUE; } else { |
︙ | ︙ | |||
457 458 459 460 461 462 463 | split_into_argv(cmdline, &argc, &argv, NULL); for (i = 0; i < argc; i++) { char *p = argv[i]; int ret; ret = cmdline_process_param(p, i+1<argc?argv[i+1]:NULL, | | | 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 | split_into_argv(cmdline, &argc, &argv, NULL); for (i = 0; i < argc; i++) { char *p = argv[i]; int ret; ret = cmdline_process_param(p, i+1<argc?argv[i+1]:NULL, 1, conf); if (ret == -2) { cmdline_error("option \"%s\" requires an argument", p); } else if (ret == 2) { i++; /* skip next argument */ } else if (ret == 1) { continue; /* nothing further needs doing */ } else if (!strcmp(p, "-cleanup") || |
︙ | ︙ | |||
518 519 520 521 522 523 524 | /* * If we already have a host name, treat * this argument as a port number. NB we * have to treat this as a saved -P * argument, so that it will be deferred * until it's a good moment to run it. */ | | | | | | < | < | | | | | > | > > > > > | > > | | < | | | < < | | | | | | | | | | | | | | | | | | | | | < > | | | | | | | | > | > > | | | | | | | > > > > > > | | | | < < < < < > | | 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 | /* * If we already have a host name, treat * this argument as a port number. NB we * have to treat this as a saved -P * argument, so that it will be deferred * until it's a good moment to run it. */ int ret = cmdline_process_param("-P", p, 1, conf); assert(ret == 2); } else if (!strncmp(q, "telnet:", 7)) { /* * If the hostname starts with "telnet:", * set the protocol to Telnet and process * the string as a Telnet URL. */ char c; q += 7; if (q[0] == '/' && q[1] == '/') q += 2; conf_set_int(conf, CONF_protocol, PROT_TELNET); p = q; while (*p && *p != ':' && *p != '/') p++; c = *p; if (*p) *p++ = '\0'; if (c == ':') conf_set_int(conf, CONF_port, atoi(p)); else conf_set_int(conf, CONF_port, -1); conf_set_str(conf, CONF_host, q); got_host = 1; } else { /* * Otherwise, treat this argument as a host * name. */ while (*p && !isspace(*p)) p++; if (*p) *p++ = '\0'; conf_set_str(conf, CONF_host, q); got_host = 1; } } else { cmdline_error("unknown option \"%s\"", p); } } } cmdline_run_saved(conf); if (loaded_session || got_host) allow_launch = TRUE; if ((!allow_launch || !conf_launchable(conf)) && !do_config()) { cleanup_exit(0); } /* * Muck about with the hostname in various ways. */ { char *hostbuf = dupstr(conf_get_str(conf, CONF_host)); char *host = hostbuf; char *p, *q; /* * Trim leading whitespace. */ host += strspn(host, " \t"); /* * See if host is of the form user@host, and separate * out the username if so. */ if (host[0] != '\0') { char *atsign = strrchr(host, '@'); if (atsign) { *atsign = '\0'; conf_set_str(conf, CONF_username, host); host = atsign + 1; } } /* * Trim a colon suffix off the hostname if it's there. In * order to protect IPv6 address literals against this * treatment, we do not do this if there's _more_ than one * colon. */ { char *c = strchr(host, ':'); if (c) { char *d = strchr(c+1, ':'); if (!d) *c = '\0'; } } /* * Remove any remaining whitespace. */ p = hostbuf; q = host; while (*q) { if (*q != ' ' && *q != '\t') *p++ = *q; q++; } *p = '\0'; conf_set_str(conf, CONF_host, hostbuf); sfree(hostbuf); } } if (!prev) { wndclass.style = 0; wndclass.lpfnWndProc = WndProc; wndclass.cbClsExtra = 0; wndclass.cbWndExtra = 0; wndclass.hInstance = inst; wndclass.hIcon = LoadIcon(inst, MAKEINTRESOURCE(IDI_MAINICON)); wndclass.hCursor = LoadCursor(NULL, IDC_IBEAM); wndclass.hbrBackground = NULL; wndclass.lpszMenuName = NULL; wndclass.lpszClassName = appname; RegisterClass(&wndclass); } memset(&ucsdata, 0, sizeof(ucsdata)); conf_cache_data(); conftopalette(); /* * Guess some defaults for the window size. This all gets * updated later, so we don't really care too much. However, we * do want the font width/height guesses to correspond to a * large font rather than a small one... */ font_width = 10; font_height = 20; extra_width = 25; extra_height = 28; guess_width = extra_width + font_width * conf_get_int(conf, CONF_width); guess_height = extra_height + font_height*conf_get_int(conf, CONF_height); { RECT r; get_fullscreen_rect(&r); if (guess_width > r.right - r.left) guess_width = r.right - r.left; if (guess_height > r.bottom - r.top) guess_height = r.bottom - r.top; } { int winmode = WS_OVERLAPPEDWINDOW | WS_VSCROLL; int exwinmode = 0; if (!conf_get_int(conf, CONF_scrollbar)) winmode &= ~(WS_VSCROLL); if (conf_get_int(conf, CONF_resize_action) == RESIZE_DISABLED) winmode &= ~(WS_THICKFRAME | WS_MAXIMIZEBOX); if (conf_get_int(conf, CONF_alwaysontop)) exwinmode |= WS_EX_TOPMOST; if (conf_get_int(conf, CONF_sunken_edge)) exwinmode |= WS_EX_CLIENTEDGE; hwnd = CreateWindowEx(exwinmode, appname, appname, winmode, CW_USEDEFAULT, CW_USEDEFAULT, guess_width, guess_height, NULL, NULL, inst, NULL); } /* * Initialise the fonts, simultaneously correcting the guesses * for font_{width,height}. */ init_fonts(0,0); /* * Initialise the terminal. (We have to do this _after_ * creating the window, since the terminal is the first thing * which will call schedule_timer(), which will in turn call * timer_change_notify() which will expect hwnd to exist.) */ term = term_init(conf, &ucsdata, NULL); logctx = log_init(NULL, conf); term_provide_logctx(term, logctx); term_size(term, conf_get_int(conf, CONF_height), conf_get_int(conf, CONF_width), conf_get_int(conf, CONF_savelines)); /* * Correct the guesses for extra_{width,height}. */ { RECT cr, wr; GetWindowRect(hwnd, &wr); GetClientRect(hwnd, &cr); offset_width = offset_height = conf_get_int(conf, CONF_window_border); extra_width = wr.right - wr.left - cr.right + cr.left + offset_width*2; extra_height = wr.bottom - wr.top - cr.bottom + cr.top +offset_height*2; } /* * Resize the window, now we know what size we _really_ want it * to be. |
︙ | ︙ | |||
789 790 791 792 793 794 795 | "Sa&ved Sessions"); AppendMenu(m, MF_ENABLED, IDM_RECONF, "Chan&ge Settings..."); AppendMenu(m, MF_SEPARATOR, 0, 0); AppendMenu(m, MF_ENABLED, IDM_COPYALL, "C&opy All to Clipboard"); AppendMenu(m, MF_ENABLED, IDM_CLRSB, "C&lear Scrollback"); AppendMenu(m, MF_ENABLED, IDM_RESET, "Rese&t Terminal"); AppendMenu(m, MF_SEPARATOR, 0, 0); | > | | | 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 | "Sa&ved Sessions"); AppendMenu(m, MF_ENABLED, IDM_RECONF, "Chan&ge Settings..."); AppendMenu(m, MF_SEPARATOR, 0, 0); AppendMenu(m, MF_ENABLED, IDM_COPYALL, "C&opy All to Clipboard"); AppendMenu(m, MF_ENABLED, IDM_CLRSB, "C&lear Scrollback"); AppendMenu(m, MF_ENABLED, IDM_RESET, "Rese&t Terminal"); AppendMenu(m, MF_SEPARATOR, 0, 0); AppendMenu(m, (conf_get_int(conf, CONF_resize_action) == RESIZE_DISABLED) ? MF_GRAYED : MF_ENABLED, IDM_FULLSCREEN, "&Full Screen"); AppendMenu(m, MF_SEPARATOR, 0, 0); if (has_help()) AppendMenu(m, MF_ENABLED, IDM_HELP, "&Help"); str = dupprintf("&About %s", appname); AppendMenu(m, MF_ENABLED, IDM_ABOUT, str); sfree(str); } |
︙ | ︙ | |||
826 827 828 829 830 831 832 833 834 835 | term_set_focus(term, GetForegroundWindow() == hwnd); UpdateWindow(hwnd); while (1) { HANDLE *handles; int nhandles, n; handles = handle_get_events(&nhandles); | > > > > > > > > > > > > > > > > > > > > > > > > > > > | | < < | < < < < < < < < < < | < < < < | 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 | term_set_focus(term, GetForegroundWindow() == hwnd); UpdateWindow(hwnd); while (1) { HANDLE *handles; int nhandles, n; DWORD timeout; if (toplevel_callback_pending() || PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) { /* * If we have anything we'd like to do immediately, set * the timeout for MsgWaitForMultipleObjects to zero so * that we'll only do a quick check of our handles and * then get on with whatever that was. * * One such option is a pending toplevel callback. The * other is a non-empty Windows message queue, which you'd * think we could leave to MsgWaitForMultipleObjects to * check for us along with all the handles, but in fact we * can't because once PeekMessage in one iteration of this * loop has removed a message from the queue, the whole * queue is considered uninteresting by the next * invocation of MWFMO. So we check ourselves whether the * message queue is non-empty, and if so, set this timeout * to zero to ensure MWFMO doesn't block. */ timeout = 0; } else { timeout = INFINITE; /* The messages seem unreliable; especially if we're being tricky */ term_set_focus(term, GetForegroundWindow() == hwnd); } handles = handle_get_events(&nhandles); n = MsgWaitForMultipleObjects(nhandles, handles, FALSE, timeout, QS_ALLINPUT); if ((unsigned)(n - WAIT_OBJECT_0) < (unsigned)nhandles) { handle_got_event(handles[n - WAIT_OBJECT_0]); sfree(handles); } else sfree(handles); if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { if (msg.message == WM_QUIT) goto finished; /* two-level break */ if (!(IsWindow(logbox) && IsDialogMessage(logbox, &msg))) DispatchMessage(&msg); } run_toplevel_callbacks(); } finished: cleanup_exit(msg.wParam); /* this doesn't return... */ return msg.wParam; /* ... but optimiser doesn't know */ } |
︙ | ︙ | |||
884 885 886 887 888 889 890 | */ deinit_fonts(); sfree(logpal); if (pal) DeleteObject(pal); sk_cleanup(); | | | 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 | */ deinit_fonts(); sfree(logpal); if (pal) DeleteObject(pal); sk_cleanup(); if (conf_get_int(conf, CONF_protocol) == PROT_SSH) { random_save_seed(); #ifdef MSCRYPTOAPI crypto_wrapup(); #endif } shutdown_help(); |
︙ | ︙ | |||
1062 1063 1064 1065 1066 1067 1068 | } /* * set or clear the "raw mouse message" mode */ void set_raw_mouse_mode(void *frontend, int activate) { | | | | | 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 | } /* * set or clear the "raw mouse message" mode */ void set_raw_mouse_mode(void *frontend, int activate) { activate = activate && !conf_get_int(conf, CONF_no_mouse_rep); send_raw_mouse = activate; update_mouse_pointer(); } /* * Print a message box and close the connection. */ void connection_fatal(void *frontend, char *fmt, ...) { va_list ap; char *stuff, morestuff[100]; va_start(ap, fmt); stuff = dupvprintf(fmt, ap); va_end(ap); sprintf(morestuff, "%.70s Fatal Error", appname); MessageBox(hwnd, stuff, morestuff, MB_ICONERROR | MB_OK); sfree(stuff); if (conf_get_int(conf, CONF_close_on_exit) == FORCE_ON) PostQuitMessage(1); else { queue_toplevel_callback(close_session, NULL); } } /* * Report an error at the command-line parsing stage. */ void cmdline_error(char *fmt, ...) |
︙ | ︙ | |||
1109 1110 1111 1112 1113 1114 1115 | sfree(stuff); exit(1); } /* * Actually do the job requested by a WM_NETEVENT */ | | < < | < < | < | < < < | | | | | | 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 | sfree(stuff); exit(1); } /* * Actually do the job requested by a WM_NETEVENT */ static void wm_netevent_callback(void *vctx) { struct wm_netevent_params *params = (struct wm_netevent_params *)vctx; select_result(params->wParam, params->lParam); sfree(vctx); } /* * Copy the colour palette from the configuration data into defpal. * This is non-trivial because the colour indices are different. */ static void conftopalette(void) { int i; static const int ww[] = { 256, 257, 258, 259, 260, 261, 0, 8, 1, 9, 2, 10, 3, 11, 4, 12, 5, 13, 6, 14, 7, 15 }; for (i = 0; i < 22; i++) { int w = ww[i]; defpal[w].rgbtRed = conf_get_int_int(conf, CONF_colours, i*3+0); defpal[w].rgbtGreen = conf_get_int_int(conf, CONF_colours, i*3+1); defpal[w].rgbtBlue = conf_get_int_int(conf, CONF_colours, i*3+2); } for (i = 0; i < NEXTCOLOURS; i++) { if (i < 216) { int r = i / 36, g = (i / 6) % 6, b = i % 6; defpal[i+16].rgbtRed = r ? r * 40 + 55 : 0; defpal[i+16].rgbtGreen = g ? g * 40 + 55 : 0; defpal[i+16].rgbtBlue = b ? b * 40 + 55 : 0; } else { int shade = i - 216; shade = shade * 10 + 8; defpal[i+16].rgbtRed = defpal[i+16].rgbtGreen = defpal[i+16].rgbtBlue = shade; } } /* Override with system colours if appropriate */ if (conf_get_int(conf, CONF_system_colour)) systopalette(); } /* * Override bit of defpal with colours from the system. * (NB that this takes a copy the system colours at the time this is called, * so subsequent colour scheme changes don't take effect. To fix that we'd |
︙ | ︙ | |||
1198 1199 1200 1201 1202 1203 1204 | * Set up the colour palette. */ static void init_palette(void) { int i; HDC hdc = GetDC(hwnd); if (hdc) { | > | | 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 | * Set up the colour palette. */ static void init_palette(void) { int i; HDC hdc = GetDC(hwnd); if (hdc) { if (conf_get_int(conf, CONF_try_palette) && GetDeviceCaps(hdc, RASTERCAPS) & RC_PALETTE) { /* * This is a genuine case where we must use smalloc * because the snew macros can't cope. */ logpal = smalloc(sizeof(*logpal) - sizeof(logpal->palPalEntry) + NALLCOLOURS * sizeof(PALETTEENTRY)); |
︙ | ︙ | |||
1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 | * ordinary one (manual underlining by means of line drawing can * be done in a pinch). */ static void init_fonts(int pick_width, int pick_height) { TEXTMETRIC tm; CPINFO cpinfo; int fontsize[3]; int i; HDC hdc; int fw_dontcare, fw_bold; for (i = 0; i < FONT_MAXNO; i++) fonts[i] = NULL; | > > > | > > | | > | | | | 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 | * ordinary one (manual underlining by means of line drawing can * be done in a pinch). */ static void init_fonts(int pick_width, int pick_height) { TEXTMETRIC tm; CPINFO cpinfo; FontSpec *font; int fontsize[3]; int i; int quality; HDC hdc; int fw_dontcare, fw_bold; for (i = 0; i < FONT_MAXNO; i++) fonts[i] = NULL; bold_font_mode = conf_get_int(conf, CONF_bold_style) & 1 ? BOLD_FONT : BOLD_NONE; bold_colours = conf_get_int(conf, CONF_bold_style) & 2 ? TRUE : FALSE; und_mode = UND_FONT; font = conf_get_fontspec(conf, CONF_font); if (font->isbold) { fw_dontcare = FW_BOLD; fw_bold = FW_HEAVY; } else { fw_dontcare = FW_DONTCARE; fw_bold = FW_BOLD; } hdc = GetDC(hwnd); if (pick_height) font_height = pick_height; else { font_height = font->height; if (font_height > 0) { font_height = -MulDiv(font_height, GetDeviceCaps(hdc, LOGPIXELSY), 72); } } font_width = pick_width; quality = conf_get_int(conf, CONF_font_quality); #define f(i,c,w,u) \ fonts[i] = CreateFont (font_height, font_width, 0, 0, w, FALSE, u, FALSE, \ c, OUT_DEFAULT_PRECIS, \ CLIP_DEFAULT_PRECIS, FONT_QUALITY(quality), \ FIXED_PITCH | FF_DONTCARE, font->name) f(FONT_NORMAL, font->charset, fw_dontcare, FALSE); SelectObject(hdc, fonts[FONT_NORMAL]); GetTextMetrics(hdc, &tm); GetObject(fonts[FONT_NORMAL], sizeof(LOGFONT), &lfont); /* Note that the TMPF_FIXED_PITCH bit is defined upside down :-( */ |
︙ | ︙ | |||
1462 1463 1464 1465 1466 1467 1468 | else ucsdata.font_codepage = -1; GetCPInfo(ucsdata.font_codepage, &cpinfo); ucsdata.dbcs_screenfont = (cpinfo.MaxCharSize > 1); } | | | 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 | else ucsdata.font_codepage = -1; GetCPInfo(ucsdata.font_codepage, &cpinfo); ucsdata.dbcs_screenfont = (cpinfo.MaxCharSize > 1); } f(FONT_UNDERLINE, font->charset, fw_dontcare, TRUE); /* * Some fonts, e.g. 9-pt Courier, draw their underlines * outside their character cell. We successfully prevent * screen corruption by clipping the text output, but then * we lose the underline completely. Here we try to work * out whether this is such a font, and if it is, we set a |
︙ | ︙ | |||
1512 1513 1514 1515 1516 1517 1518 | if (!gotit) { und_mode = UND_LINE; DeleteObject(fonts[FONT_UNDERLINE]); fonts[FONT_UNDERLINE] = 0; } } | | | | 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 | if (!gotit) { und_mode = UND_LINE; DeleteObject(fonts[FONT_UNDERLINE]); fonts[FONT_UNDERLINE] = 0; } } if (bold_font_mode == BOLD_FONT) { f(FONT_BOLD, font->charset, fw_bold, FALSE); } #undef f descent = tm.tmAscent + 1; if (descent >= font_height) descent = font_height - 1; |
︙ | ︙ | |||
1539 1540 1541 1542 1543 1544 1545 | if (fontsize[FONT_UNDERLINE] != fontsize[FONT_NORMAL]) { und_mode = UND_LINE; DeleteObject(fonts[FONT_UNDERLINE]); fonts[FONT_UNDERLINE] = 0; } | | | | | > > > | | | > > | | 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 | if (fontsize[FONT_UNDERLINE] != fontsize[FONT_NORMAL]) { und_mode = UND_LINE; DeleteObject(fonts[FONT_UNDERLINE]); fonts[FONT_UNDERLINE] = 0; } if (bold_font_mode == BOLD_FONT && fontsize[FONT_BOLD] != fontsize[FONT_NORMAL]) { bold_font_mode = BOLD_SHADOW; DeleteObject(fonts[FONT_BOLD]); fonts[FONT_BOLD] = 0; } fontflag[0] = fontflag[1] = fontflag[2] = 1; init_ucs(conf, &ucsdata); } static void another_font(int fontno) { int basefont; int fw_dontcare, fw_bold, quality; int c, u, w, x; char *s; FontSpec *font; if (fontno < 0 || fontno >= FONT_MAXNO || fontflag[fontno]) return; basefont = (fontno & ~(FONT_BOLDUND)); if (basefont != fontno && !fontflag[basefont]) another_font(basefont); font = conf_get_fontspec(conf, CONF_font); if (font->isbold) { fw_dontcare = FW_BOLD; fw_bold = FW_HEAVY; } else { fw_dontcare = FW_DONTCARE; fw_bold = FW_BOLD; } c = font->charset; w = fw_dontcare; u = FALSE; s = font->name; x = font_width; if (fontno & FONT_WIDE) x *= 2; if (fontno & FONT_NARROW) x = (x+1)/2; if (fontno & FONT_OEM) c = OEM_CHARSET; if (fontno & FONT_BOLD) w = fw_bold; if (fontno & FONT_UNDERLINE) u = TRUE; quality = conf_get_int(conf, CONF_font_quality); fonts[fontno] = CreateFont(font_height * (1 + !!(fontno & FONT_HIGH)), x, 0, 0, w, FALSE, u, FALSE, c, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, FONT_QUALITY(quality), DEFAULT_PITCH | FF_DONTCARE, s); fontflag[fontno] = 1; } static void deinit_fonts(void) { |
︙ | ︙ | |||
1615 1616 1617 1618 1619 1620 1621 | void request_resize(void *frontend, int w, int h) { int width, height; /* If the window is maximized supress resizing attempts */ if (IsZoomed(hwnd)) { | | | | 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 | void request_resize(void *frontend, int w, int h) { int width, height; /* If the window is maximized supress resizing attempts */ if (IsZoomed(hwnd)) { if (conf_get_int(conf, CONF_resize_action) == RESIZE_TERM) return; } if (conf_get_int(conf, CONF_resize_action) == RESIZE_DISABLED) return; if (h == term->rows && w == term->cols) return; /* Sanity checks ... */ { static int first_time = 1; static RECT ss; |
︙ | ︙ | |||
1650 1651 1652 1653 1654 1655 1656 | if (w < 15) w = 15; if (h < 1) h = 1; } } | | > | | > > > | > | 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 | if (w < 15) w = 15; if (h < 1) h = 1; } } term_size(term, h, w, conf_get_int(conf, CONF_savelines)); if (conf_get_int(conf, CONF_resize_action) != RESIZE_FONT && !IsZoomed(hwnd)) { width = extra_width + font_width * w; height = extra_height + font_height * h; SetWindowPos(hwnd, NULL, 0, 0, width, height, SWP_NOACTIVATE | SWP_NOCOPYBITS | SWP_NOMOVE | SWP_NOZORDER); } else reset_window(0); InvalidateRect(hwnd, NULL, TRUE); } static void reset_window(int reinit) { /* * This function decides how to resize or redraw when the * user changes something. * * This function doesn't like to change the terminal size but if the * font size is locked that may be it's only soluion. */ int win_width, win_height, resize_action, window_border; RECT cr, wr; #ifdef RDB_DEBUG_PATCH debug((27, "reset_window()")); #endif /* Current window sizes ... */ GetWindowRect(hwnd, &wr); GetClientRect(hwnd, &cr); win_width = cr.right - cr.left; win_height = cr.bottom - cr.top; resize_action = conf_get_int(conf, CONF_resize_action); window_border = conf_get_int(conf, CONF_window_border); if (resize_action == RESIZE_DISABLED) reinit = 2; /* Are we being forced to reload the fonts ? */ if (reinit>1) { #ifdef RDB_DEBUG_PATCH debug((27, "reset_window() -- Forced deinit")); #endif deinit_fonts(); |
︙ | ︙ | |||
1722 1723 1724 1725 1726 1727 1728 | /* We're fullscreen, this means we must not change the size of * the window so it's the font size or the terminal itself. */ extra_width = wr.right - wr.left - cr.right + cr.left; extra_height = wr.bottom - wr.top - cr.bottom + cr.top; | | | | | | | | | 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 | /* We're fullscreen, this means we must not change the size of * the window so it's the font size or the terminal itself. */ extra_width = wr.right - wr.left - cr.right + cr.left; extra_height = wr.bottom - wr.top - cr.bottom + cr.top; if (resize_action != RESIZE_TERM) { if (font_width != win_width/term->cols || font_height != win_height/term->rows) { deinit_fonts(); init_fonts(win_width/term->cols, win_height/term->rows); offset_width = (win_width-font_width*term->cols)/2; offset_height = (win_height-font_height*term->rows)/2; InvalidateRect(hwnd, NULL, TRUE); #ifdef RDB_DEBUG_PATCH debug((25, "reset_window() -> Z font resize to (%d, %d)", font_width, font_height)); #endif } } else { if (font_width * term->cols != win_width || font_height * term->rows != win_height) { /* Our only choice at this point is to change the * size of the terminal; Oh well. */ term_size(term, win_height/font_height, win_width/font_width, conf_get_int(conf, CONF_savelines)); offset_width = (win_width-font_width*term->cols)/2; offset_height = (win_height-font_height*term->rows)/2; InvalidateRect(hwnd, NULL, TRUE); #ifdef RDB_DEBUG_PATCH debug((27, "reset_window() -> Zoomed term_size")); #endif } } return; } /* Hmm, a force re-init means we should ignore the current window * so we resize to the default font size. */ if (reinit>0) { #ifdef RDB_DEBUG_PATCH debug((27, "reset_window() -> Forced re-init")); #endif offset_width = offset_height = window_border; extra_width = wr.right - wr.left - cr.right + cr.left + offset_width*2; extra_height = wr.bottom - wr.top - cr.bottom + cr.top +offset_height*2; if (win_width != font_width*term->cols + offset_width*2 || win_height != font_height*term->rows + offset_height*2) { /* If this is too large windows will resize it to the maximum |
︙ | ︙ | |||
1787 1788 1789 1790 1791 1792 1793 | return; } /* Okay the user doesn't want us to change the font so we try the * window. But that may be too big for the screen which forces us * to change the terminal. */ | | | | | | > | 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 | return; } /* Okay the user doesn't want us to change the font so we try the * window. But that may be too big for the screen which forces us * to change the terminal. */ if ((resize_action == RESIZE_TERM && reinit<=0) || (resize_action == RESIZE_EITHER && reinit<0) || reinit>0) { offset_width = offset_height = window_border; extra_width = wr.right - wr.left - cr.right + cr.left + offset_width*2; extra_height = wr.bottom - wr.top - cr.bottom + cr.top +offset_height*2; if (win_width != font_width*term->cols + offset_width*2 || win_height != font_height*term->rows + offset_height*2) { static RECT ss; int width, height; get_fullscreen_rect(&ss); width = (ss.right - ss.left - extra_width) / font_width; height = (ss.bottom - ss.top - extra_height) / font_height; /* Grrr too big */ if ( term->rows > height || term->cols > width ) { if (resize_action == RESIZE_EITHER) { /* Make the font the biggest we can */ if (term->cols > width) font_width = (ss.right - ss.left - extra_width) / term->cols; if (term->rows > height) font_height = (ss.bottom - ss.top - extra_height) / term->rows; deinit_fonts(); init_fonts(font_width, font_height); width = (ss.right - ss.left - extra_width) / font_width; height = (ss.bottom - ss.top - extra_height) / font_height; } else { if ( height > term->rows ) height = term->rows; if ( width > term->cols ) width = term->cols; term_size(term, height, width, conf_get_int(conf, CONF_savelines)); #ifdef RDB_DEBUG_PATCH debug((27, "reset_window() -> term resize to (%d,%d)", height, width)); #endif } } |
︙ | ︙ | |||
1849 1850 1851 1852 1853 1854 1855 | #endif } return; } /* We're allowed to or must change the font but do we want to ? */ | | | | | | 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 | #endif } return; } /* We're allowed to or must change the font but do we want to ? */ if (font_width != (win_width-window_border*2)/term->cols || font_height != (win_height-window_border*2)/term->rows) { deinit_fonts(); init_fonts((win_width-window_border*2)/term->cols, (win_height-window_border*2)/term->rows); offset_width = (win_width-font_width*term->cols)/2; offset_height = (win_height-font_height*term->rows)/2; extra_width = wr.right - wr.left - cr.right + cr.left +offset_width*2; extra_height = wr.bottom - wr.top - cr.bottom + cr.top+offset_height*2; InvalidateRect(hwnd, NULL, TRUE); |
︙ | ︙ | |||
1883 1884 1885 1886 1887 1888 1889 | kbd_codepage = atoi(lbuf); } static void click(Mouse_Button b, int x, int y, int shift, int ctrl, int alt) { int thistime = GetMessageTime(); | | > | 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 | kbd_codepage = atoi(lbuf); } static void click(Mouse_Button b, int x, int y, int shift, int ctrl, int alt) { int thistime = GetMessageTime(); if (send_raw_mouse && !(shift && conf_get_int(conf, CONF_mouse_override))) { lastbtn = MBT_NOTHING; term_mouse(term, b, translate_button(b), MA_CLICK, x, y, shift, ctrl, alt); return; } if (lastbtn == b && thistime - lasttime < dbltime) { |
︙ | ︙ | |||
1913 1914 1915 1916 1917 1918 1919 | * into a cooked one (SELECT, EXTEND, PASTE). */ static Mouse_Button translate_button(Mouse_Button button) { if (button == MBT_LEFT) return MBT_SELECT; if (button == MBT_MIDDLE) | > | > | | | | 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 1983 | * into a cooked one (SELECT, EXTEND, PASTE). */ static Mouse_Button translate_button(Mouse_Button button) { if (button == MBT_LEFT) return MBT_SELECT; if (button == MBT_MIDDLE) return conf_get_int(conf, CONF_mouse_is_xterm) == 1 ? MBT_PASTE : MBT_EXTEND; if (button == MBT_RIGHT) return conf_get_int(conf, CONF_mouse_is_xterm) == 1 ? MBT_EXTEND : MBT_PASTE; return 0; /* shouldn't happen */ } static void show_mouseptr(int show) { /* NB that the counter in ShowCursor() is also frobbed by * update_mouse_pointer() */ static int cursor_visible = 1; if (!conf_get_int(conf, CONF_hide_mouseptr)) show = 1; /* override if this feature disabled */ if (cursor_visible && !show) ShowCursor(FALSE); else if (!cursor_visible && show) ShowCursor(TRUE); cursor_visible = show; } |
︙ | ︙ | |||
1950 1951 1952 1953 1954 1955 1956 | return FALSE; } static int resizing; void notify_remote_exit(void *fe) { | | > | | | | > | > | > > > > > > > > > > | | | 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 2060 2061 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 2079 2080 2081 | return FALSE; } static int resizing; void notify_remote_exit(void *fe) { int exitcode, close_on_exit; if (!session_closed && (exitcode = back->exitcode(backhandle)) >= 0) { close_on_exit = conf_get_int(conf, CONF_close_on_exit); /* Abnormal exits will already have set session_closed and taken * appropriate action. */ if (close_on_exit == FORCE_ON || (close_on_exit == AUTO && exitcode != INT_MAX)) { PostQuitMessage(0); } else { queue_toplevel_callback(close_session, NULL); session_closed = TRUE; /* exitcode == INT_MAX indicates that the connection was closed * by a fatal error, so an error box will be coming our way and * we should not generate this informational one. */ if (exitcode != INT_MAX) MessageBox(hwnd, "Connection closed by remote host", appname, MB_OK | MB_ICONINFORMATION); } } } void timer_change_notify(unsigned long next) { unsigned long now = GETTICKCOUNT(); long ticks; if (now - next < INT_MAX) ticks = 0; else ticks = next - now; KillTimer(hwnd, TIMING_TIMER_ID); SetTimer(hwnd, TIMING_TIMER_ID, ticks, NULL); timing_next_time = next; } static void conf_cache_data(void) { /* Cache some items from conf to speed lookups in very hot code */ cursor_type = conf_get_int(conf, CONF_cursor_type); vtmode = conf_get_int(conf, CONF_vtmode); } static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { HDC hdc; static int ignore_clip = FALSE; static int need_backend_resize = FALSE; static int fullscr_on_max = FALSE; static int processed_resize = FALSE; static UINT last_mousemove = 0; int resize_action; switch (message) { case WM_TIMER: if ((UINT_PTR)wParam == TIMING_TIMER_ID) { unsigned long next; KillTimer(hwnd, TIMING_TIMER_ID); if (run_timers(timing_next_time, &next)) { timer_change_notify(next); } else { } } return 0; case WM_CREATE: break; case WM_CLOSE: { char *str; show_mouseptr(1); str = dupprintf("%s Exit Confirmation", appname); if (session_closed || !conf_get_int(conf, CONF_warn_on_close) || MessageBox(hwnd, "Are you sure you want to close this session?", str, MB_ICONWARNING | MB_OKCANCEL | MB_DEFBUTTON1) == IDOK) DestroyWindow(hwnd); sfree(str); } |
︙ | ︙ | |||
2057 2058 2059 2060 2061 2062 2063 | if (wParam == IDM_DUPSESS) { /* * Allocate a file-mapping memory chunk for the * config structure. */ SECURITY_ATTRIBUTES sa; | | > > > | | < < < > | | 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 2124 2125 2126 2127 2128 2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141 2142 2143 2144 2145 2146 2147 2148 | if (wParam == IDM_DUPSESS) { /* * Allocate a file-mapping memory chunk for the * config structure. */ SECURITY_ATTRIBUTES sa; void *p; int size; size = conf_serialised_size(conf); sa.nLength = sizeof(sa); sa.lpSecurityDescriptor = NULL; sa.bInheritHandle = TRUE; filemap = CreateFileMapping(INVALID_HANDLE_VALUE, &sa, PAGE_READWRITE, 0, size, NULL); if (filemap && filemap != INVALID_HANDLE_VALUE) { p = MapViewOfFile(filemap, FILE_MAP_WRITE, 0, 0, size); if (p) { conf_serialise(conf, p); UnmapViewOfFile(p); } } inherit_handles = TRUE; sprintf(c, "putty &%p:%u", filemap, (unsigned)size); cl = c; } else if (wParam == IDM_SAVEDSESS) { unsigned int sessno = ((lParam - IDM_SAVED_MIN) / MENU_SAVED_STEP) + 1; if (sessno < (unsigned)sesslist.nsessions) { char *session = sesslist.sessions[sessno]; cl = dupprintf("putty @%s", session); |
︙ | ︙ | |||
2103 2104 2105 2106 2107 2108 2109 2110 2111 2112 2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 2124 2125 2126 | si.lpDesktop = NULL; si.lpTitle = NULL; si.dwFlags = 0; si.cbReserved2 = 0; si.lpReserved2 = NULL; CreateProcess(b, cl, NULL, NULL, inherit_handles, NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi); if (filemap) CloseHandle(filemap); if (freecl) sfree(cl); } break; case IDM_RESTART: if (!back) { logevent(NULL, "----- Session restarted -----"); term_pwron(term, FALSE); start_backend(); } break; case IDM_RECONF: { | > > | > > > > > > > | | | > | > > > > | | < < | > | | | | | > > | > | | | | > > | > | | | | > | | | | | | > > > > | > | > | | | | > | | > > | | > | | | > | > | 2161 2162 2163 2164 2165 2166 2167 2168 2169 2170 2171 2172 2173 2174 2175 2176 2177 2178 2179 2180 2181 2182 2183 2184 2185 2186 2187 2188 2189 2190 2191 2192 2193 2194 2195 2196 2197 2198 2199 2200 2201 2202 2203 2204 2205 2206 2207 2208 2209 2210 2211 2212 2213 2214 2215 2216 2217 2218 2219 2220 2221 2222 2223 2224 2225 2226 2227 2228 2229 2230 2231 2232 2233 2234 2235 2236 2237 2238 2239 2240 2241 2242 2243 2244 2245 2246 2247 2248 2249 2250 2251 2252 2253 2254 2255 2256 2257 2258 2259 2260 2261 2262 2263 2264 2265 2266 2267 2268 2269 2270 2271 2272 2273 2274 2275 2276 2277 2278 2279 2280 2281 2282 2283 2284 2285 2286 2287 2288 2289 2290 2291 2292 2293 2294 2295 2296 2297 2298 2299 2300 2301 2302 2303 2304 2305 2306 2307 2308 2309 2310 2311 2312 2313 2314 2315 2316 2317 2318 2319 2320 2321 2322 2323 2324 2325 2326 2327 2328 2329 2330 2331 2332 2333 2334 2335 2336 2337 2338 2339 2340 2341 2342 2343 2344 2345 2346 2347 2348 2349 2350 2351 2352 2353 2354 2355 2356 2357 2358 2359 2360 2361 2362 2363 2364 2365 2366 2367 2368 2369 2370 2371 2372 2373 2374 | si.lpDesktop = NULL; si.lpTitle = NULL; si.dwFlags = 0; si.cbReserved2 = 0; si.lpReserved2 = NULL; CreateProcess(b, cl, NULL, NULL, inherit_handles, NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi); CloseHandle(pi.hProcess); CloseHandle(pi.hThread); if (filemap) CloseHandle(filemap); if (freecl) sfree(cl); } break; case IDM_RESTART: if (!back) { logevent(NULL, "----- Session restarted -----"); term_pwron(term, FALSE); start_backend(); } break; case IDM_RECONF: { Conf *prev_conf; int init_lvl = 1; int reconfig_result; if (reconfiguring) break; else reconfiguring = TRUE; /* * Copy the current window title into the stored * previous configuration, so that doing nothing to * the window title field in the config box doesn't * reset the title to its startup state. */ conf_set_str(conf, CONF_wintitle, window_name); prev_conf = conf_copy(conf); reconfig_result = do_reconfig(hwnd, back ? back->cfg_info(backhandle) : 0); reconfiguring = FALSE; if (!reconfig_result) { conf_free(prev_conf); break; } conf_cache_data(); resize_action = conf_get_int(conf, CONF_resize_action); { /* Disable full-screen if resizing forbidden */ int i; for (i = 0; i < lenof(popup_menus); i++) EnableMenuItem(popup_menus[i].menu, IDM_FULLSCREEN, MF_BYCOMMAND | (resize_action == RESIZE_DISABLED) ? MF_GRAYED : MF_ENABLED); /* Gracefully unzoom if necessary */ if (IsZoomed(hwnd) && (resize_action == RESIZE_DISABLED)) ShowWindow(hwnd, SW_RESTORE); } /* Pass new config data to the logging module */ log_reconfig(logctx, conf); sfree(logpal); /* * Flush the line discipline's edit buffer in the * case where local editing has just been disabled. */ ldisc_configure(ldisc, conf); if (ldisc) ldisc_send(ldisc, NULL, 0, 0); if (pal) DeleteObject(pal); logpal = NULL; pal = NULL; conftopalette(); init_palette(); /* Pass new config data to the terminal */ term_reconfig(term, conf); /* Pass new config data to the back end */ if (back) back->reconfig(backhandle, conf); /* Screen size changed ? */ if (conf_get_int(conf, CONF_height) != conf_get_int(prev_conf, CONF_height) || conf_get_int(conf, CONF_width) != conf_get_int(prev_conf, CONF_width) || conf_get_int(conf, CONF_savelines) != conf_get_int(prev_conf, CONF_savelines) || resize_action == RESIZE_FONT || (resize_action == RESIZE_EITHER && IsZoomed(hwnd)) || resize_action == RESIZE_DISABLED) term_size(term, conf_get_int(conf, CONF_height), conf_get_int(conf, CONF_width), conf_get_int(conf, CONF_savelines)); /* Enable or disable the scroll bar, etc */ { LONG nflg, flag = GetWindowLongPtr(hwnd, GWL_STYLE); LONG nexflag, exflag = GetWindowLongPtr(hwnd, GWL_EXSTYLE); nexflag = exflag; if (conf_get_int(conf, CONF_alwaysontop) != conf_get_int(prev_conf, CONF_alwaysontop)) { if (conf_get_int(conf, CONF_alwaysontop)) { nexflag |= WS_EX_TOPMOST; SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); } else { nexflag &= ~(WS_EX_TOPMOST); SetWindowPos(hwnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); } } if (conf_get_int(conf, CONF_sunken_edge)) nexflag |= WS_EX_CLIENTEDGE; else nexflag &= ~(WS_EX_CLIENTEDGE); nflg = flag; if (conf_get_int(conf, is_full_screen() ? CONF_scrollbar_in_fullscreen : CONF_scrollbar)) nflg |= WS_VSCROLL; else nflg &= ~WS_VSCROLL; if (resize_action == RESIZE_DISABLED || is_full_screen()) nflg &= ~WS_THICKFRAME; else nflg |= WS_THICKFRAME; if (resize_action == RESIZE_DISABLED) nflg &= ~WS_MAXIMIZEBOX; else nflg |= WS_MAXIMIZEBOX; if (nflg != flag || nexflag != exflag) { if (nflg != flag) SetWindowLongPtr(hwnd, GWL_STYLE, nflg); if (nexflag != exflag) SetWindowLongPtr(hwnd, GWL_EXSTYLE, nexflag); SetWindowPos(hwnd, NULL, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOCOPYBITS | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED); init_lvl = 2; } } /* Oops */ if (resize_action == RESIZE_DISABLED && IsZoomed(hwnd)) { force_normal(hwnd); init_lvl = 2; } set_title(NULL, conf_get_str(conf, CONF_wintitle)); if (IsIconic(hwnd)) { SetWindowText(hwnd, conf_get_int(conf, CONF_win_name_always) ? window_name : icon_name); } { FontSpec *font = conf_get_fontspec(conf, CONF_font); FontSpec *prev_font = conf_get_fontspec(prev_conf, CONF_font); if (!strcmp(font->name, prev_font->name) || !strcmp(conf_get_str(conf, CONF_line_codepage), conf_get_str(prev_conf, CONF_line_codepage)) || font->isbold != prev_font->isbold || font->height != prev_font->height || font->charset != prev_font->charset || conf_get_int(conf, CONF_font_quality) != conf_get_int(prev_conf, CONF_font_quality) || conf_get_int(conf, CONF_vtmode) != conf_get_int(prev_conf, CONF_vtmode) || conf_get_int(conf, CONF_bold_style) != conf_get_int(prev_conf, CONF_bold_style) || resize_action == RESIZE_DISABLED || resize_action == RESIZE_EITHER || resize_action != conf_get_int(prev_conf, CONF_resize_action)) init_lvl = 2; } InvalidateRect(hwnd, NULL, TRUE); reset_window(init_lvl); conf_free(prev_conf); } break; case IDM_COPYALL: term_copyall(term); break; case IDM_PASTE: request_paste(NULL); |
︙ | ︙ | |||
2331 2332 2333 2334 2335 2336 2337 | * which would cause us to reference invalid memory * and crash. Perhaps I'm just too paranoid here. */ if (i >= n_specials) break; if (back) back->special(backhandle, specials[i].code); | < | > | 2421 2422 2423 2424 2425 2426 2427 2428 2429 2430 2431 2432 2433 2434 2435 2436 2437 2438 2439 2440 2441 2442 2443 2444 2445 2446 2447 2448 2449 2450 2451 2452 | * which would cause us to reference invalid memory * and crash. Perhaps I'm just too paranoid here. */ if (i >= n_specials) break; if (back) back->special(backhandle, specials[i].code); } } break; #define X_POS(l) ((int)(short)LOWORD(l)) #define Y_POS(l) ((int)(short)HIWORD(l)) #define TO_CHR_X(x) ((((x)<0 ? (x)-font_width+1 : (x))-offset_width) / font_width) #define TO_CHR_Y(y) ((((y)<0 ? (y)-font_height+1: (y))-offset_height) / font_height) case WM_LBUTTONDOWN: case WM_MBUTTONDOWN: case WM_RBUTTONDOWN: case WM_LBUTTONUP: case WM_MBUTTONUP: case WM_RBUTTONUP: if (message == WM_RBUTTONDOWN && ((wParam & MK_CONTROL) || (conf_get_int(conf, CONF_mouse_is_xterm) == 2))) { POINT cursorpos; show_mouseptr(1); /* make sure pointer is visible */ GetCursorPos(&cursorpos); TrackPopupMenu(popup_menus[CTXMENU].menu, TPM_LEFTALIGN | TPM_TOPALIGN | TPM_RIGHTBUTTON, cursorpos.x, cursorpos.y, |
︙ | ︙ | |||
2610 2611 2612 2613 2614 2615 2616 | SelectObject(hdc, GetStockObject(SYSTEM_FONT)); SelectObject(hdc, GetStockObject(WHITE_PEN)); EndPaint(hwnd, &p); ShowCaret(hwnd); } return 0; case WM_NETEVENT: | < < > > | > > > > | | < | < | | < | | < | 2700 2701 2702 2703 2704 2705 2706 2707 2708 2709 2710 2711 2712 2713 2714 2715 2716 2717 2718 2719 2720 2721 2722 2723 2724 2725 2726 2727 | SelectObject(hdc, GetStockObject(SYSTEM_FONT)); SelectObject(hdc, GetStockObject(WHITE_PEN)); EndPaint(hwnd, &p); ShowCaret(hwnd); } return 0; case WM_NETEVENT: { /* * To protect against re-entrancy when Windows's recv() * immediately triggers a new WSAAsyncSelect window * message, we don't call select_result directly from this * handler but instead wait until we're back out at the * top level of the message loop. */ struct wm_netevent_params *params = snew(struct wm_netevent_params); params->wParam = wParam; params->lParam = lParam; queue_toplevel_callback(wm_netevent_callback, params); } return 0; case WM_SETFOCUS: term_set_focus(term, TRUE); CreateCaret(hwnd, caretbm, font_width, font_height); ShowCaret(hwnd); flash_window(0); /* stop */ compose_state = 0; |
︙ | ︙ | |||
2655 2656 2657 2658 2659 2660 2661 | case WM_EXITSIZEMOVE: EnableSizeTip(0); resizing = FALSE; #ifdef RDB_DEBUG_PATCH debug((27, "WM_EXITSIZEMOVE")); #endif if (need_backend_resize) { | | > > > | | | | > < < < | > | 2745 2746 2747 2748 2749 2750 2751 2752 2753 2754 2755 2756 2757 2758 2759 2760 2761 2762 2763 2764 2765 2766 2767 2768 2769 2770 2771 2772 2773 2774 2775 2776 2777 2778 2779 2780 2781 2782 2783 2784 2785 2786 2787 2788 2789 2790 | case WM_EXITSIZEMOVE: EnableSizeTip(0); resizing = FALSE; #ifdef RDB_DEBUG_PATCH debug((27, "WM_EXITSIZEMOVE")); #endif if (need_backend_resize) { term_size(term, conf_get_int(conf, CONF_height), conf_get_int(conf, CONF_width), conf_get_int(conf, CONF_savelines)); InvalidateRect(hwnd, NULL, TRUE); } break; case WM_SIZING: /* * This does two jobs: * 1) Keep the sizetip uptodate * 2) Make sure the window size is _stepped_ in units of the font size. */ resize_action = conf_get_int(conf, CONF_resize_action); if (resize_action == RESIZE_TERM || (resize_action == RESIZE_EITHER && !is_alt_pressed())) { int width, height, w, h, ew, eh; LPRECT r = (LPRECT) lParam; if (!need_backend_resize && resize_action == RESIZE_EITHER && (conf_get_int(conf, CONF_height) != term->rows || conf_get_int(conf, CONF_width) != term->cols)) { /* * Great! It seems that both the terminal size and the * font size have been changed and the user is now dragging. * * It will now be difficult to get back to the configured * font size! * * This would be easier but it seems to be too confusing. */ conf_set_int(conf, CONF_height, term->rows); conf_set_int(conf, CONF_width, term->cols); InvalidateRect(hwnd, NULL, TRUE); need_backend_resize = TRUE; } width = r->right - r->left - extra_width; height = r->bottom - r->top - extra_height; |
︙ | ︙ | |||
2721 2722 2723 2724 2725 2726 2727 | } if (ew || eh) return 1; else return 0; } else { int width, height, w, h, rv = 0; | > | | | 2813 2814 2815 2816 2817 2818 2819 2820 2821 2822 2823 2824 2825 2826 2827 2828 2829 | } if (ew || eh) return 1; else return 0; } else { int width, height, w, h, rv = 0; int window_border = conf_get_int(conf, CONF_window_border); int ex_width = extra_width + (window_border - offset_width) * 2; int ex_height = extra_height + (window_border - offset_height) * 2; LPRECT r = (LPRECT) lParam; width = r->right - r->left - ex_width; height = r->bottom - r->top - ex_height; w = (width + term->cols/2)/term->cols; h = (height + term->rows/2)/term->rows; |
︙ | ︙ | |||
2758 2759 2760 2761 2762 2763 2764 2765 2766 2767 2768 2769 2770 2771 2772 2773 2774 2775 | case WM_FULLSCR_ON_MAX: fullscr_on_max = TRUE; break; case WM_MOVE: sys_cursor_update(); break; case WM_SIZE: #ifdef RDB_DEBUG_PATCH debug((27, "WM_SIZE %s (%d,%d)", (wParam == SIZE_MINIMIZED) ? "SIZE_MINIMIZED": (wParam == SIZE_MAXIMIZED) ? "SIZE_MAXIMIZED": (wParam == SIZE_RESTORED && resizing) ? "to": (wParam == SIZE_RESTORED) ? "SIZE_RESTORED": "...", LOWORD(lParam), HIWORD(lParam))); #endif if (wParam == SIZE_MINIMIZED) SetWindowText(hwnd, | > > | | 2851 2852 2853 2854 2855 2856 2857 2858 2859 2860 2861 2862 2863 2864 2865 2866 2867 2868 2869 2870 2871 2872 2873 2874 2875 2876 2877 2878 | case WM_FULLSCR_ON_MAX: fullscr_on_max = TRUE; break; case WM_MOVE: sys_cursor_update(); break; case WM_SIZE: resize_action = conf_get_int(conf, CONF_resize_action); #ifdef RDB_DEBUG_PATCH debug((27, "WM_SIZE %s (%d,%d)", (wParam == SIZE_MINIMIZED) ? "SIZE_MINIMIZED": (wParam == SIZE_MAXIMIZED) ? "SIZE_MAXIMIZED": (wParam == SIZE_RESTORED && resizing) ? "to": (wParam == SIZE_RESTORED) ? "SIZE_RESTORED": "...", LOWORD(lParam), HIWORD(lParam))); #endif if (wParam == SIZE_MINIMIZED) SetWindowText(hwnd, conf_get_int(conf, CONF_win_name_always) ? window_name : icon_name); if (wParam == SIZE_RESTORED || wParam == SIZE_MAXIMIZED) SetWindowText(hwnd, window_name); if (wParam == SIZE_RESTORED) { processed_resize = FALSE; clear_full_screen(); if (processed_resize) { /* |
︙ | ︙ | |||
2802 2803 2804 2805 2806 2807 2808 | */ return 0; } } processed_resize = TRUE; | | > | > > > > > > > > > > > > | > > | | | | | | | | | | | | | 2897 2898 2899 2900 2901 2902 2903 2904 2905 2906 2907 2908 2909 2910 2911 2912 2913 2914 2915 2916 2917 2918 2919 2920 2921 2922 2923 2924 2925 2926 2927 2928 2929 2930 2931 2932 2933 2934 2935 2936 2937 2938 2939 2940 2941 2942 2943 2944 2945 2946 2947 2948 2949 2950 2951 2952 2953 2954 2955 2956 2957 2958 2959 2960 2961 2962 2963 2964 2965 2966 2967 2968 2969 2970 2971 2972 2973 2974 2975 2976 2977 2978 2979 2980 2981 2982 2983 | */ return 0; } } processed_resize = TRUE; if (resize_action == RESIZE_DISABLED) { /* A resize, well it better be a minimize. */ reset_window(-1); } else { int width, height, w, h; int window_border = conf_get_int(conf, CONF_window_border); width = LOWORD(lParam); height = HIWORD(lParam); if (wParam == SIZE_MAXIMIZED && !was_zoomed) { was_zoomed = 1; prev_rows = term->rows; prev_cols = term->cols; if (resize_action == RESIZE_TERM) { w = width / font_width; if (w < 1) w = 1; h = height / font_height; if (h < 1) h = 1; if (resizing) { /* * As below, if we're in the middle of an * interactive resize we don't call * back->size. In Windows 7, this case can * arise in maximisation as well via the Aero * snap UI. */ need_backend_resize = TRUE; conf_set_int(conf, CONF_height, h); conf_set_int(conf, CONF_width, w); } else { term_size(term, h, w, conf_get_int(conf, CONF_savelines)); } } reset_window(0); } else if (wParam == SIZE_RESTORED && was_zoomed) { was_zoomed = 0; if (resize_action == RESIZE_TERM) { w = (width-window_border*2) / font_width; if (w < 1) w = 1; h = (height-window_border*2) / font_height; if (h < 1) h = 1; term_size(term, h, w, conf_get_int(conf, CONF_savelines)); reset_window(2); } else if (resize_action != RESIZE_FONT) reset_window(2); else reset_window(0); } else if (wParam == SIZE_MINIMIZED) { /* do nothing */ } else if (resize_action == RESIZE_TERM || (resize_action == RESIZE_EITHER && !is_alt_pressed())) { w = (width-window_border*2) / font_width; if (w < 1) w = 1; h = (height-window_border*2) / font_height; if (h < 1) h = 1; if (resizing) { /* * Don't call back->size in mid-resize. (To * prevent massive numbers of resize events * getting sent down the connection during an NT * opaque drag.) */ need_backend_resize = TRUE; conf_set_int(conf, CONF_height, h); conf_set_int(conf, CONF_width, w); } else { term_size(term, h, w, conf_get_int(conf, CONF_savelines)); } } else { reset_window(0); } } sys_cursor_update(); return 0; |
︙ | ︙ | |||
2889 2890 2891 2892 2893 2894 2895 | term_scroll(term, 0, +term->rows / 2); break; case SB_PAGEUP: term_scroll(term, 0, -term->rows / 2); break; case SB_THUMBPOSITION: case SB_THUMBTRACK: | > > > > > > > > > > > | > | 2999 3000 3001 3002 3003 3004 3005 3006 3007 3008 3009 3010 3011 3012 3013 3014 3015 3016 3017 3018 3019 3020 3021 3022 3023 3024 3025 | term_scroll(term, 0, +term->rows / 2); break; case SB_PAGEUP: term_scroll(term, 0, -term->rows / 2); break; case SB_THUMBPOSITION: case SB_THUMBTRACK: /* * Use GetScrollInfo instead of HIWORD(wParam) to get * 32-bit scroll position. */ { SCROLLINFO si; si.cbSize = sizeof(si); si.fMask = SIF_TRACKPOS; if (GetScrollInfo(hwnd, SB_VERT, &si) == 0) si.nTrackPos = HIWORD(wParam); term_scroll(term, 1, si.nTrackPos); } break; } break; case WM_PALETTECHANGED: if ((HWND) wParam != hwnd && pal != NULL) { HDC hdc = get_ctx(NULL); if (hdc) { |
︙ | ︙ | |||
2950 2951 2952 2953 2954 2955 2956 | } else break; /* pass to Windows for default processing */ } else { len = TranslateKey(message, wParam, lParam, buf); if (len == -1) return DefWindowProc(hwnd, message, wParam, lParam); if (len != 0) { | < < < < < < < < < | 3072 3073 3074 3075 3076 3077 3078 3079 3080 3081 3082 3083 3084 3085 3086 3087 3088 3089 3090 3091 3092 3093 3094 3095 3096 3097 3098 3099 3100 | } else break; /* pass to Windows for default processing */ } else { len = TranslateKey(message, wParam, lParam, buf); if (len == -1) return DefWindowProc(hwnd, message, wParam, lParam); if (len != 0) { /* * We need not bother about stdin backlogs * here, because in GUI PuTTY we can't do * anything about it anyway; there's no means * of asking Windows to hold off on KEYDOWN * messages. We _have_ to buffer everything * we're sent. */ term_seen_key_event(term); if (ldisc) ldisc_send(ldisc, buf, len, 1); show_mouseptr(0); } } } return 0; case WM_INPUTLANGCHANGE: /* wParam == Font number */ /* lParam == Locale */ set_input_locale((HKL)lParam); sys_cursor_update(); break; |
︙ | ︙ | |||
3014 3015 3016 3017 3018 3019 3020 | /* * Jaeyoun Chung reports that Korean character * input doesn't work correctly if we do a single * luni_send() covering the whole of buff. So * instead we luni_send the characters one by one. */ term_seen_key_event(term); | > > | > > > | > > > > > > | 3127 3128 3129 3130 3131 3132 3133 3134 3135 3136 3137 3138 3139 3140 3141 3142 3143 3144 3145 3146 3147 3148 3149 3150 3151 3152 3153 3154 | /* * Jaeyoun Chung reports that Korean character * input doesn't work correctly if we do a single * luni_send() covering the whole of buff. So * instead we luni_send the characters one by one. */ term_seen_key_event(term); /* don't divide SURROGATE PAIR */ if (ldisc) { for (i = 0; i < n; i += 2) { WCHAR hs = *(unsigned short *)(buff+i); if (IS_HIGH_SURROGATE(hs) && i+2 < n) { WCHAR ls = *(unsigned short *)(buff+i+2); if (IS_LOW_SURROGATE(ls)) { luni_send(ldisc, (unsigned short *)(buff+i), 2, 1); i += 2; continue; } } luni_send(ldisc, (unsigned short *)(buff+i), 1, 1); } } free(buff); } ImmReleaseContext(hwnd, hIMC); return 1; } |
︙ | ︙ | |||
3056 3057 3058 3059 3060 3061 3062 | char c = (unsigned char)wParam; term_seen_key_event(term); if (ldisc) lpage_send(ldisc, CP_ACP, &c, 1, 1); } return 0; case WM_SYSCOLORCHANGE: | | | 3180 3181 3182 3183 3184 3185 3186 3187 3188 3189 3190 3191 3192 3193 3194 | char c = (unsigned char)wParam; term_seen_key_event(term); if (ldisc) lpage_send(ldisc, CP_ACP, &c, 1, 1); } return 0; case WM_SYSCOLORCHANGE: if (conf_get_int(conf, CONF_system_colour)) { /* Refresh palette from system colours. */ /* XXX actually this zaps the entire palette. */ systopalette(); init_palette(); /* Force a repaint of the terminal window. */ term_invalidate(term); } |
︙ | ︙ | |||
3108 3109 3110 3111 3112 3113 3114 | } else if (wheel_accumulator < 0) { b = MBT_WHEEL_DOWN; wheel_accumulator += WHEEL_DELTA; } else break; if (send_raw_mouse && | > | | 3232 3233 3234 3235 3236 3237 3238 3239 3240 3241 3242 3243 3244 3245 3246 3247 | } else if (wheel_accumulator < 0) { b = MBT_WHEEL_DOWN; wheel_accumulator += WHEEL_DELTA; } else break; if (send_raw_mouse && !(conf_get_int(conf, CONF_mouse_override) && shift_pressed)) { /* Mouse wheel position is in screen coordinates for * some reason */ POINT p; p.x = X_POS(lParam); p.y = Y_POS(lParam); if (ScreenToClient(hwnd, &p)) { /* send a mouse-down followed by a mouse up */ term_mouse(term, b, translate_button(b), |
︙ | ︙ | |||
3215 3216 3217 3218 3219 3220 3221 3222 3223 3224 3225 3226 3227 3228 3229 3230 3231 3232 3233 3234 3235 3236 3237 3238 3239 3240 3241 | HDC hdc = ctx; RECT line_box; int force_manual_underline = 0; int fnt_width, char_width; int text_adjust = 0; int xoffset = 0; int maxlen, remaining, opaque; static int *lpDx = NULL; static int lpDx_len = 0; int *lpDx_maybe; lattr &= LATTR_MODE; char_width = fnt_width = font_width * (1 + (lattr != LATTR_NORM)); if (attr & ATTR_WIDE) char_width *= 2; /* Only want the left half of double width lines */ if (lattr != LATTR_NORM && x*2 >= term->cols) return; x *= fnt_width; y *= font_height; x += offset_width; y += offset_height; | > > | < < < > | > | 3340 3341 3342 3343 3344 3345 3346 3347 3348 3349 3350 3351 3352 3353 3354 3355 3356 3357 3358 3359 3360 3361 3362 3363 3364 3365 3366 3367 3368 3369 3370 3371 3372 3373 3374 3375 3376 3377 3378 3379 3380 3381 3382 3383 3384 3385 3386 3387 3388 3389 3390 3391 3392 3393 3394 3395 3396 3397 3398 3399 3400 3401 | HDC hdc = ctx; RECT line_box; int force_manual_underline = 0; int fnt_width, char_width; int text_adjust = 0; int xoffset = 0; int maxlen, remaining, opaque; int is_cursor = FALSE; static int *lpDx = NULL; static int lpDx_len = 0; int *lpDx_maybe; int len2; /* for SURROGATE PAIR */ lattr &= LATTR_MODE; char_width = fnt_width = font_width * (1 + (lattr != LATTR_NORM)); if (attr & ATTR_WIDE) char_width *= 2; /* Only want the left half of double width lines */ if (lattr != LATTR_NORM && x*2 >= term->cols) return; x *= fnt_width; y *= font_height; x += offset_width; y += offset_height; if ((attr & TATTR_ACTCURS) && (cursor_type == 0 || term->big_cursor)) { attr &= ~(ATTR_REVERSE|ATTR_BLINK|ATTR_COLOURS); /* cursor fg and bg */ attr |= (260 << ATTR_FGSHIFT) | (261 << ATTR_BGSHIFT); is_cursor = TRUE; } nfont = 0; if (vtmode == VT_POORMAN && lattr != LATTR_NORM) { /* Assume a poorman font is borken in other ways too. */ lattr = LATTR_WIDE; } else switch (lattr) { case LATTR_NORM: break; case LATTR_WIDE: nfont |= FONT_WIDE; break; default: nfont |= FONT_WIDE + FONT_HIGH; break; } if (attr & ATTR_NARROW) nfont |= FONT_NARROW; #ifdef USES_VTLINE_HACK /* Special hack for the VT100 linedraw glyphs. */ if (text[0] >= 0x23BA && text[0] <= 0x23BD) { switch ((unsigned char) (text[0])) { case 0xBA: text_adjust = -2 * font_height / 5; break; case 0xBB: |
︙ | ︙ | |||
3286 3287 3288 3289 3290 3291 3292 3293 3294 | text_adjust *= 2; text[0] = ucsdata.unitab_xterm['q']; if (attr & ATTR_UNDER) { attr &= ~ATTR_UNDER; force_manual_underline = 1; } } /* Anything left as an original character set is unprintable. */ | > | > | | | > > > > > > > > > > > > > > > > > > | 3412 3413 3414 3415 3416 3417 3418 3419 3420 3421 3422 3423 3424 3425 3426 3427 3428 3429 3430 3431 3432 3433 3434 3435 3436 3437 3438 3439 3440 3441 3442 3443 3444 3445 3446 3447 3448 3449 3450 3451 3452 3453 3454 3455 3456 3457 3458 3459 3460 3461 3462 3463 3464 3465 3466 3467 3468 3469 3470 3471 3472 3473 3474 3475 3476 3477 3478 3479 3480 3481 3482 3483 3484 3485 3486 3487 3488 3489 3490 3491 3492 3493 3494 3495 3496 3497 3498 3499 3500 | text_adjust *= 2; text[0] = ucsdata.unitab_xterm['q']; if (attr & ATTR_UNDER) { attr &= ~ATTR_UNDER; force_manual_underline = 1; } } #endif /* Anything left as an original character set is unprintable. */ if (DIRECT_CHAR(text[0]) && (len < 2 || !IS_SURROGATE_PAIR(text[0], text[1]))) { int i; for (i = 0; i < len; i++) text[i] = 0xFFFD; } /* OEM CP */ if ((text[0] & CSET_MASK) == CSET_OEMCP) nfont |= FONT_OEM; nfg = ((attr & ATTR_FGMASK) >> ATTR_FGSHIFT); nbg = ((attr & ATTR_BGMASK) >> ATTR_BGSHIFT); if (bold_font_mode == BOLD_FONT && (attr & ATTR_BOLD)) nfont |= FONT_BOLD; if (und_mode == UND_FONT && (attr & ATTR_UNDER)) nfont |= FONT_UNDERLINE; another_font(nfont); if (!fonts[nfont]) { if (nfont & FONT_UNDERLINE) force_manual_underline = 1; /* Don't do the same for manual bold, it could be bad news. */ nfont &= ~(FONT_BOLD | FONT_UNDERLINE); } another_font(nfont); if (!fonts[nfont]) nfont = FONT_NORMAL; if (attr & ATTR_REVERSE) { t = nfg; nfg = nbg; nbg = t; } if (bold_colours && (attr & ATTR_BOLD) && !is_cursor) { if (nfg < 16) nfg |= 8; else if (nfg >= 256) nfg |= 1; } if (bold_colours && (attr & ATTR_BLINK)) { if (nbg < 16) nbg |= 8; else if (nbg >= 256) nbg |= 1; } fg = colours[nfg]; bg = colours[nbg]; SelectObject(hdc, fonts[nfont]); SetTextColor(hdc, fg); SetBkColor(hdc, bg); if (attr & TATTR_COMBINING) SetBkMode(hdc, TRANSPARENT); else SetBkMode(hdc, OPAQUE); line_box.left = x; line_box.top = y; line_box.right = x + char_width * len; line_box.bottom = y + font_height; /* adjust line_box.right for SURROGATE PAIR & VARIATION SELECTOR */ { int i; int rc_width = 0; for (i = 0; i < len ; i++) { if (i+1 < len && IS_HIGH_VARSEL(text[i], text[i+1])) { i++; } else if (i+1 < len && IS_SURROGATE_PAIR(text[i], text[i+1])) { rc_width += char_width; i++; } else if (IS_LOW_VARSEL(text[i])) { /* do nothing */ } else { rc_width += char_width; } } line_box.right = line_box.left + rc_width; } /* Only want the left half of double width lines */ if (line_box.right > font_width*term->cols+offset_width) line_box.right = font_width*term->cols+offset_width; if (font_varpitch) { /* |
︙ | ︙ | |||
3371 3372 3373 3374 3375 3376 3377 | SetTextAlign(hdc, TA_TOP | TA_LEFT | TA_NOUPDATECP); lpDx_maybe = lpDx; maxlen = len; } opaque = TRUE; /* start by erasing the rectangle */ for (remaining = len; remaining > 0; | | > > > > > > > > > > | | | | | | > | | > > > | > > > > > > > > > > > > > > | 3517 3518 3519 3520 3521 3522 3523 3524 3525 3526 3527 3528 3529 3530 3531 3532 3533 3534 3535 3536 3537 3538 3539 3540 3541 3542 3543 3544 3545 3546 3547 3548 3549 3550 3551 3552 3553 3554 3555 3556 3557 3558 3559 3560 3561 3562 3563 3564 3565 3566 3567 3568 3569 3570 3571 | SetTextAlign(hdc, TA_TOP | TA_LEFT | TA_NOUPDATECP); lpDx_maybe = lpDx; maxlen = len; } opaque = TRUE; /* start by erasing the rectangle */ for (remaining = len; remaining > 0; text += len, remaining -= len, x += char_width * len2) { len = (maxlen < remaining ? maxlen : remaining); /* don't divide SURROGATE PAIR and VARIATION SELECTOR */ len2 = len; if (maxlen == 1) { if (remaining >= 1 && IS_SURROGATE_PAIR(text[0], text[1])) len++; if (remaining-len >= 1 && IS_LOW_VARSEL(text[len])) len++; else if (remaining-len >= 2 && IS_HIGH_VARSEL(text[len], text[len+1])) len += 2; } if (len > lpDx_len) { lpDx_len = len * 9 / 8 + 16; lpDx = sresize(lpDx, lpDx_len, int); if (lpDx_maybe) lpDx_maybe = lpDx; } { int i; /* only last char has dx width in SURROGATE PAIR and * VARIATION sequence */ for (i = 0; i < len; i++) { lpDx[i] = char_width; if (i+1 < len && IS_HIGH_VARSEL(text[i], text[i+1])) { if (i > 0) lpDx[i-1] = 0; lpDx[i] = 0; i++; lpDx[i] = char_width; } else if (i+1 < len && IS_SURROGATE_PAIR(text[i],text[i+1])) { lpDx[i] = 0; i++; lpDx[i] = char_width; } else if (IS_LOW_VARSEL(text[i])) { if (i > 0) lpDx[i-1] = 0; lpDx[i] = char_width; } } } /* We're using a private area for direct to font. (512 chars.) */ if (ucsdata.dbcs_screenfont && (text[0] & CSET_MASK) == CSET_ACP) { /* Ho Hum, dbcs fonts are a PITA! */ /* To display on W9x I have to convert to UCS */ static wchar_t *uni_buf = 0; |
︙ | ︙ | |||
3428 3429 3430 3431 3432 3433 3434 | return; /* Eeek! */ ExtTextOutW(hdc, x + xoffset, y - font_height * (lattr == LATTR_BOT) + text_adjust, ETO_CLIPPED | (opaque ? ETO_OPAQUE : 0), &line_box, uni_buf, nlen, lpDx_maybe); | | | 3602 3603 3604 3605 3606 3607 3608 3609 3610 3611 3612 3613 3614 3615 3616 | return; /* Eeek! */ ExtTextOutW(hdc, x + xoffset, y - font_height * (lattr == LATTR_BOT) + text_adjust, ETO_CLIPPED | (opaque ? ETO_OPAQUE : 0), &line_box, uni_buf, nlen, lpDx_maybe); if (bold_font_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) { SetBkMode(hdc, TRANSPARENT); ExtTextOutW(hdc, x + xoffset - 1, y - font_height * (lattr == LATTR_BOT) + text_adjust, ETO_CLIPPED, &line_box, uni_buf, nlen, lpDx_maybe); } |
︙ | ︙ | |||
3453 3454 3455 3456 3457 3458 3459 | for (i = 0; i < len; i++) directbuf[i] = text[i] & 0xFF; ExtTextOut(hdc, x + xoffset, y - font_height * (lattr == LATTR_BOT) + text_adjust, ETO_CLIPPED | (opaque ? ETO_OPAQUE : 0), &line_box, directbuf, len, lpDx_maybe); | | | 3627 3628 3629 3630 3631 3632 3633 3634 3635 3636 3637 3638 3639 3640 3641 | for (i = 0; i < len; i++) directbuf[i] = text[i] & 0xFF; ExtTextOut(hdc, x + xoffset, y - font_height * (lattr == LATTR_BOT) + text_adjust, ETO_CLIPPED | (opaque ? ETO_OPAQUE : 0), &line_box, directbuf, len, lpDx_maybe); if (bold_font_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) { SetBkMode(hdc, TRANSPARENT); /* GRR: This draws the character outside its box and * can leave 'droppings' even with the clip box! I * suppose I could loop it one character at a time ... * yuk. * |
︙ | ︙ | |||
3492 3493 3494 3495 3496 3497 3498 | /* print Glyphs as they are, without Windows' Shaping*/ general_textout(hdc, x + xoffset, y - font_height * (lattr==LATTR_BOT) + text_adjust, &line_box, wbuf, len, lpDx, opaque && !(attr & TATTR_COMBINING)); /* And the shadow bold hack. */ | | | 3666 3667 3668 3669 3670 3671 3672 3673 3674 3675 3676 3677 3678 3679 3680 | /* print Glyphs as they are, without Windows' Shaping*/ general_textout(hdc, x + xoffset, y - font_height * (lattr==LATTR_BOT) + text_adjust, &line_box, wbuf, len, lpDx, opaque && !(attr & TATTR_COMBINING)); /* And the shadow bold hack. */ if (bold_font_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) { SetBkMode(hdc, TRANSPARENT); ExtTextOutW(hdc, x + xoffset - 1, y - font_height * (lattr == LATTR_BOT) + text_adjust, ETO_CLIPPED, &line_box, wbuf, len, lpDx_maybe); } } |
︙ | ︙ | |||
3532 3533 3534 3535 3536 3537 3538 | * Wrapper that handles combining characters. */ void do_text(Context ctx, int x, int y, wchar_t *text, int len, unsigned long attr, int lattr) { if (attr & TATTR_COMBINING) { unsigned long a = 0; | > > > > > | > > > > > > > > > > > > > > > > > > > | > > | | 3706 3707 3708 3709 3710 3711 3712 3713 3714 3715 3716 3717 3718 3719 3720 3721 3722 3723 3724 3725 3726 3727 3728 3729 3730 3731 3732 3733 3734 3735 3736 3737 3738 3739 3740 3741 3742 3743 3744 3745 3746 3747 3748 3749 3750 3751 3752 3753 3754 3755 3756 3757 3758 3759 3760 3761 3762 3763 | * Wrapper that handles combining characters. */ void do_text(Context ctx, int x, int y, wchar_t *text, int len, unsigned long attr, int lattr) { if (attr & TATTR_COMBINING) { unsigned long a = 0; int len0 = 1; /* don't divide SURROGATE PAIR and VARIATION SELECTOR */ if (len >= 2 && IS_SURROGATE_PAIR(text[0], text[1])) len0 = 2; if (len-len0 >= 1 && IS_LOW_VARSEL(text[len0])) { attr &= ~TATTR_COMBINING; do_text_internal(ctx, x, y, text, len0+1, attr, lattr); text += len0+1; len -= len0+1; a = TATTR_COMBINING; } else if (len-len0 >= 2 && IS_HIGH_VARSEL(text[len0], text[len0+1])) { attr &= ~TATTR_COMBINING; do_text_internal(ctx, x, y, text, len0+2, attr, lattr); text += len0+2; len -= len0+2; a = TATTR_COMBINING; } else { attr &= ~TATTR_COMBINING; } while (len--) { if (len >= 1 && IS_SURROGATE_PAIR(text[0], text[1])) { do_text_internal(ctx, x, y, text, 2, attr | a, lattr); len--; text++; } else { do_text_internal(ctx, x, y, text, 1, attr | a, lattr); } text++; a = TATTR_COMBINING; } } else do_text_internal(ctx, x, y, text, len, attr, lattr); } void do_cursor(Context ctx, int x, int y, wchar_t *text, int len, unsigned long attr, int lattr) { int fnt_width; int char_width; HDC hdc = ctx; int ctype = cursor_type; lattr &= LATTR_MODE; if ((attr & TATTR_ACTCURS) && (ctype == 0 || term->big_cursor)) { if (*text != UCSWIDE) { do_text(ctx, x, y, text, len, attr, lattr); return; |
︙ | ︙ | |||
3679 3680 3681 3682 3683 3684 3685 3686 3687 3688 3689 3690 3691 3692 3693 3694 3695 3696 3697 3698 3699 3700 3701 3702 3703 | } ibuf += font_width / 2 -1; ibuf /= font_width; return ibuf; } /* * Translate a WM_(SYS)?KEY(UP|DOWN) message into a string of ASCII * 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) { BYTE keystate[256]; int scan, left_alt = 0, key_down, shift_state; int r, i, code; unsigned char *p = output; static int alt_sum = 0; HKL kbd_layout = GetKeyboardLayout(0); | > > > > > > > > > > > > > > > < | | | 3879 3880 3881 3882 3883 3884 3885 3886 3887 3888 3889 3890 3891 3892 3893 3894 3895 3896 3897 3898 3899 3900 3901 3902 3903 3904 3905 3906 3907 3908 3909 3910 3911 3912 3913 3914 3915 3916 3917 3918 3919 3920 3921 3922 3923 3924 3925 3926 3927 3928 | } ibuf += font_width / 2 -1; ibuf /= font_width; return ibuf; } DECL_WINDOWS_FUNCTION(static, BOOL, FlashWindowEx, (PFLASHWINFO)); DECL_WINDOWS_FUNCTION(static, BOOL, ToUnicodeEx, (UINT, UINT, const BYTE *, LPWSTR, int, UINT, HKL)); static void init_winfuncs(void) { HMODULE user32_module = load_system32_dll("user32.dll"); GET_WINDOWS_FUNCTION(user32_module, FlashWindowEx); GET_WINDOWS_FUNCTION(user32_module, ToUnicodeEx); } /* * Translate a WM_(SYS)?KEY(UP|DOWN) message into a string of ASCII * 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) { BYTE keystate[256]; int scan, left_alt = 0, key_down, shift_state; int r, i, code; unsigned char *p = output; static int alt_sum = 0; int funky_type = conf_get_int(conf, CONF_funky_type); int no_applic_k = conf_get_int(conf, CONF_no_applic_k); int ctrlaltkeys = conf_get_int(conf, CONF_ctrlaltkeys); int nethack_keypad = conf_get_int(conf, CONF_nethack_keypad); HKL kbd_layout = GetKeyboardLayout(0); static wchar_t keys_unicode[3]; static int compose_char = 0; static WPARAM compose_keycode = 0; r = GetKeyboardState(keystate); if (!r) memset(keystate, 0, sizeof(keystate)); else { #if 0 #define SHOW_TOASCII_RESULT |
︙ | ︙ | |||
3750 3751 3752 3753 3754 3755 3756 | ch = MapVirtualKeyEx(wParam, 2, kbd_layout); if (ch >= ' ' && ch <= '~') debug((", '%c'", ch)); else if (ch) debug((", $%02x", ch)); | | | | | | | | 3964 3965 3966 3967 3968 3969 3970 3971 3972 3973 3974 3975 3976 3977 3978 3979 3980 3981 3982 3983 | ch = MapVirtualKeyEx(wParam, 2, kbd_layout); if (ch >= ' ' && ch <= '~') debug((", '%c'", ch)); else if (ch) debug((", $%02x", ch)); if (keys_unicode[0]) debug((", KB0=%04x", keys_unicode[0])); if (keys_unicode[1]) debug((", KB1=%04x", keys_unicode[1])); if (keys_unicode[2]) debug((", KB2=%04x", keys_unicode[2])); if ((keystate[VK_SHIFT] & 0x80) != 0) debug((", S")); if ((keystate[VK_CONTROL] & 0x80) != 0) debug((", C")); if ((HIWORD(lParam) & KF_EXTENDED)) debug((", E")); |
︙ | ︙ | |||
3787 3788 3789 3790 3791 3792 3793 | if (wParam == VK_MENU && (HIWORD(lParam) & KF_EXTENDED)) { keystate[VK_RMENU] = keystate[VK_MENU]; } /* Nastyness with NUMLock - Shift-NUMLock is left alone though */ | | | | | 4001 4002 4003 4004 4005 4006 4007 4008 4009 4010 4011 4012 4013 4014 4015 4016 4017 | if (wParam == VK_MENU && (HIWORD(lParam) & KF_EXTENDED)) { keystate[VK_RMENU] = keystate[VK_MENU]; } /* Nastyness with NUMLock - Shift-NUMLock is left alone though */ if ((funky_type == FUNKY_VT400 || (funky_type <= FUNKY_LINUX && term->app_keypad_keys && !no_applic_k)) && wParam == VK_NUMLOCK && !(keystate[VK_SHIFT] & 0x80)) { wParam = VK_EXECUTE; /* UnToggle NUMLock */ if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == 0) keystate[VK_NUMLOCK] ^= 1; |
︙ | ︙ | |||
3815 3816 3817 3818 3819 3820 3821 | if ((HIWORD(lParam) & KF_ALTDOWN) && (keystate[VK_RMENU] & 0x80) == 0) left_alt = 1; key_down = ((HIWORD(lParam) & KF_UP) == 0); /* Make sure Ctrl-ALT is not the same as AltGr for ToAscii unless told. */ if (left_alt && (keystate[VK_CONTROL] & 0x80)) { | | | | | | | | | | | 4029 4030 4031 4032 4033 4034 4035 4036 4037 4038 4039 4040 4041 4042 4043 4044 4045 4046 4047 4048 4049 4050 4051 4052 4053 4054 4055 4056 4057 4058 4059 4060 4061 4062 4063 4064 4065 4066 4067 4068 4069 4070 4071 4072 4073 4074 4075 4076 4077 4078 4079 4080 4081 4082 4083 | if ((HIWORD(lParam) & KF_ALTDOWN) && (keystate[VK_RMENU] & 0x80) == 0) left_alt = 1; key_down = ((HIWORD(lParam) & KF_UP) == 0); /* Make sure Ctrl-ALT is not the same as AltGr for ToAscii unless told. */ if (left_alt && (keystate[VK_CONTROL] & 0x80)) { if (ctrlaltkeys) keystate[VK_MENU] = 0; else { keystate[VK_RMENU] = 0x80; left_alt = 0; } } scan = (HIWORD(lParam) & (KF_UP | KF_EXTENDED | 0xFF)); shift_state = ((keystate[VK_SHIFT] & 0x80) != 0) + ((keystate[VK_CONTROL] & 0x80) != 0) * 2; /* Note if AltGr was pressed and if it was used as a compose key */ if (!compose_state) { compose_keycode = 0x100; if (conf_get_int(conf, CONF_compose_key)) { if (wParam == VK_MENU && (HIWORD(lParam) & KF_EXTENDED)) compose_keycode = wParam; } if (wParam == VK_APPS) compose_keycode = wParam; } if (wParam == compose_keycode) { if (compose_state == 0 && (HIWORD(lParam) & (KF_UP | KF_REPEAT)) == 0) compose_state = 1; else if (compose_state == 1 && (HIWORD(lParam) & KF_UP)) compose_state = 2; else compose_state = 0; } else if (compose_state == 1 && wParam != VK_CONTROL) compose_state = 0; if (compose_state > 1 && left_alt) compose_state = 0; /* Sanitize the number pad if not using a PC NumPad */ if (left_alt || (term->app_keypad_keys && !no_applic_k && funky_type != FUNKY_XTERM) || funky_type == FUNKY_VT400 || nethack_keypad || compose_state) { if ((HIWORD(lParam) & KF_EXTENDED) == 0) { int nParam = 0; switch (wParam) { case VK_INSERT: nParam = VK_NUMPAD0; break; case VK_END: |
︙ | ︙ | |||
3932 3933 3934 3935 3936 3937 3938 | term_scroll_to_selection(term, (wParam == VK_PRIOR ? 0 : 1)); return 0; } if (wParam == VK_INSERT && shift_state == 1) { request_paste(NULL); return 0; } | | | > | > | | | 4146 4147 4148 4149 4150 4151 4152 4153 4154 4155 4156 4157 4158 4159 4160 4161 4162 4163 4164 4165 4166 4167 4168 4169 4170 4171 4172 4173 4174 4175 4176 4177 4178 4179 4180 4181 4182 | term_scroll_to_selection(term, (wParam == VK_PRIOR ? 0 : 1)); return 0; } if (wParam == VK_INSERT && shift_state == 1) { request_paste(NULL); return 0; } if (left_alt && wParam == VK_F4 && conf_get_int(conf, CONF_alt_f4)) { return -1; } if (left_alt && wParam == VK_SPACE && conf_get_int(conf, CONF_alt_space)) { SendMessage(hwnd, WM_SYSCOMMAND, SC_KEYMENU, 0); return -1; } if (left_alt && wParam == VK_RETURN && conf_get_int(conf, CONF_fullscreenonaltenter) && (conf_get_int(conf, CONF_resize_action) != RESIZE_DISABLED)) { if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) != KF_REPEAT) flip_full_screen(); return -1; } /* Control-Numlock for app-keypad mode switch */ if (wParam == VK_PAUSE && shift_state == 2) { term->app_keypad_keys ^= 1; return 0; } /* Nethack keypad */ if (nethack_keypad && !left_alt) { switch (wParam) { case VK_NUMPAD1: *p++ = "bB\002\002"[shift_state & 3]; return p - output; case VK_NUMPAD2: *p++ = "jJ\012\012"[shift_state & 3]; return p - output; |
︙ | ︙ | |||
3988 3989 3990 3991 3992 3993 3994 | } } /* Application Keypad */ if (!left_alt) { int xkey = 0; | | | | | | 4204 4205 4206 4207 4208 4209 4210 4211 4212 4213 4214 4215 4216 4217 4218 4219 4220 4221 4222 4223 4224 4225 4226 4227 4228 4229 4230 4231 4232 4233 4234 | } } /* Application Keypad */ if (!left_alt) { int xkey = 0; if (funky_type == FUNKY_VT400 || (funky_type <= FUNKY_LINUX && term->app_keypad_keys && !no_applic_k)) switch (wParam) { case VK_EXECUTE: xkey = 'P'; break; case VK_DIVIDE: xkey = 'Q'; break; case VK_MULTIPLY: xkey = 'R'; break; case VK_SUBTRACT: xkey = 'S'; break; } if (term->app_keypad_keys && !no_applic_k) switch (wParam) { case VK_NUMPAD0: xkey = 'p'; break; case VK_NUMPAD1: xkey = 'q'; break; |
︙ | ︙ | |||
4041 4042 4043 4044 4045 4046 4047 | xkey = 'y'; break; case VK_DECIMAL: xkey = 'n'; break; case VK_ADD: | | | | | | 4257 4258 4259 4260 4261 4262 4263 4264 4265 4266 4267 4268 4269 4270 4271 4272 4273 4274 4275 4276 4277 4278 4279 4280 4281 4282 4283 4284 4285 4286 4287 4288 4289 4290 4291 | xkey = 'y'; break; case VK_DECIMAL: xkey = 'n'; break; case VK_ADD: if (funky_type == FUNKY_XTERM) { if (shift_state) xkey = 'l'; else xkey = 'k'; } else if (shift_state) xkey = 'm'; else xkey = 'l'; break; case VK_DIVIDE: if (funky_type == FUNKY_XTERM) xkey = 'o'; break; case VK_MULTIPLY: if (funky_type == FUNKY_XTERM) xkey = 'j'; break; case VK_SUBTRACT: if (funky_type == FUNKY_XTERM) xkey = 'm'; break; case VK_RETURN: if (HIWORD(lParam) & KF_EXTENDED) xkey = 'M'; break; |
︙ | ︙ | |||
4083 4084 4085 4086 4087 4088 4089 | } else p += sprintf((char *) p, "\x1BO%c", xkey); return p - output; } } if (wParam == VK_BACK && shift_state == 0) { /* Backspace */ | | | | 4299 4300 4301 4302 4303 4304 4305 4306 4307 4308 4309 4310 4311 4312 4313 4314 4315 4316 4317 4318 4319 | } else p += sprintf((char *) p, "\x1BO%c", xkey); return p - output; } } if (wParam == VK_BACK && shift_state == 0) { /* Backspace */ *p++ = (conf_get_int(conf, CONF_bksp_is_delete) ? 0x7F : 0x08); *p++ = 0; return -2; } if (wParam == VK_BACK && shift_state == 1) { /* Shift Backspace */ /* We do the opposite of what is configured */ *p++ = (conf_get_int(conf, CONF_bksp_is_delete) ? 0x08 : 0x7F); *p++ = 0; return -2; } if (wParam == VK_TAB && shift_state == 1) { /* Shift tab */ *p++ = 0x1B; *p++ = '['; *p++ = 'Z'; |
︙ | ︙ | |||
4233 4234 4235 4236 4237 4238 4239 | code = 5; break; case VK_NEXT: code = 6; break; } /* Reorder edit keys to physical order */ | | < | > | 4449 4450 4451 4452 4453 4454 4455 4456 4457 4458 4459 4460 4461 4462 4463 4464 4465 4466 4467 4468 4469 4470 4471 4472 | code = 5; break; case VK_NEXT: code = 6; break; } /* Reorder edit keys to physical order */ if (funky_type == FUNKY_VT400 && code <= 6) code = "\0\2\1\4\5\3\6"[code]; if (term->vt52_mode && code > 0 && code <= 6) { p += sprintf((char *) p, "\x1B%c", " HLMEIG"[code]); return p - output; } if (funky_type == FUNKY_SCO && code >= 11 && code <= 34) { /* SCO function keys */ char codes[] = "MNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz@[\\]^_`{"; int index = 0; switch (wParam) { case VK_F1: index = 0; break; case VK_F2: index = 1; break; case VK_F3: index = 2; break; case VK_F4: index = 3; break; |
︙ | ︙ | |||
4264 4265 4266 4267 4268 4269 4270 | case VK_F12: index = 11; break; } if (keystate[VK_SHIFT] & 0x80) index += 12; if (keystate[VK_CONTROL] & 0x80) index += 24; p += sprintf((char *) p, "\x1B[%c", codes[index]); return p - output; } | | | | | | > | 4480 4481 4482 4483 4484 4485 4486 4487 4488 4489 4490 4491 4492 4493 4494 4495 4496 4497 4498 4499 4500 4501 4502 4503 4504 4505 4506 4507 4508 4509 4510 4511 4512 4513 4514 4515 4516 4517 4518 4519 4520 4521 4522 4523 4524 4525 4526 4527 4528 4529 | case VK_F12: index = 11; break; } if (keystate[VK_SHIFT] & 0x80) index += 12; if (keystate[VK_CONTROL] & 0x80) index += 24; p += sprintf((char *) p, "\x1B[%c", codes[index]); return p - output; } if (funky_type == FUNKY_SCO && /* SCO small keypad */ code >= 1 && code <= 6) { char codes[] = "HL.FIG"; if (code == 3) { *p++ = '\x7F'; } else { p += sprintf((char *) p, "\x1B[%c", codes[code-1]); } return p - output; } if ((term->vt52_mode || funky_type == FUNKY_VT100P) && code >= 11 && code <= 24) { int offt = 0; if (code > 15) offt++; if (code > 21) offt++; if (term->vt52_mode) p += sprintf((char *) p, "\x1B%c", code + 'P' - 11 - offt); else p += sprintf((char *) p, "\x1BO%c", code + 'P' - 11 - offt); return p - output; } if (funky_type == FUNKY_LINUX && code >= 11 && code <= 15) { p += sprintf((char *) p, "\x1B[[%c", code + 'A' - 11); return p - output; } if (funky_type == FUNKY_XTERM && code >= 11 && code <= 14) { if (term->vt52_mode) p += sprintf((char *) p, "\x1B%c", code + 'P' - 11); else p += sprintf((char *) p, "\x1BO%c", code + 'P' - 11); return p - output; } if ((code == 1 || code == 4) && conf_get_int(conf, CONF_rxvt_homeend)) { p += sprintf((char *) p, code == 1 ? "\x1B[H" : "\x1BOw"); return p - output; } if (code) { p += sprintf((char *) p, "\x1B[%d~", code); return p - output; } |
︙ | ︙ | |||
4358 4359 4360 4361 4362 4363 4364 | /* Okay we've done everything interesting; let windows deal with * the boring stuff */ { BOOL capsOn=0; /* helg: clear CAPS LOCK state if caps lock switches to cyrillic */ | | > | > > > < | | < > > > > > > > | | < < < < < < < | | | | | | | | | | | < | | | | | | | | > | | 4575 4576 4577 4578 4579 4580 4581 4582 4583 4584 4585 4586 4587 4588 4589 4590 4591 4592 4593 4594 4595 4596 4597 4598 4599 4600 4601 4602 4603 4604 4605 4606 4607 4608 4609 4610 4611 4612 4613 4614 4615 4616 4617 4618 4619 4620 4621 4622 4623 4624 4625 4626 4627 4628 4629 4630 4631 4632 4633 4634 4635 4636 4637 4638 4639 4640 4641 4642 4643 4644 4645 4646 4647 4648 4649 4650 4651 4652 4653 4654 4655 4656 4657 4658 4659 4660 4661 4662 4663 4664 4665 4666 4667 4668 4669 4670 4671 4672 4673 4674 4675 4676 4677 4678 4679 4680 4681 4682 4683 4684 4685 4686 4687 4688 4689 4690 4691 4692 4693 4694 4695 4696 4697 4698 4699 4700 4701 4702 4703 4704 4705 4706 4707 4708 4709 4710 4711 4712 4713 4714 4715 4716 4717 4718 4719 4720 4721 4722 4723 4724 4725 4726 4727 4728 4729 4730 4731 4732 4733 4734 4735 4736 4737 4738 4739 4740 4741 4742 4743 4744 4745 4746 4747 4748 4749 4750 4751 4752 4753 4754 4755 4756 4757 4758 4759 4760 4761 4762 4763 4764 4765 4766 4767 4768 4769 | /* Okay we've done everything interesting; let windows deal with * the boring stuff */ { BOOL capsOn=0; /* helg: clear CAPS LOCK state if caps lock switches to cyrillic */ if(keystate[VK_CAPITAL] != 0 && conf_get_int(conf, CONF_xlat_capslockcyr)) { capsOn= !left_alt; keystate[VK_CAPITAL] = 0; } /* XXX how do we know what the max size of the keys array should * be is? There's indication on MS' website of an Inquire/InquireEx * functioning returning a KBINFO structure which tells us. */ if (osVersion.dwPlatformId == VER_PLATFORM_WIN32_NT && p_ToUnicodeEx) { r = p_ToUnicodeEx(wParam, scan, keystate, keys_unicode, lenof(keys_unicode), 0, kbd_layout); } else { /* XXX 'keys' parameter is declared in MSDN documentation as * 'LPWORD lpChar'. * The experience of a French user indicates that on * Win98, WORD[] should be passed in, but on Win2K, it should * be BYTE[]. German WinXP and my Win2K with "US International" * driver corroborate this. * Experimentally I've conditionalised the behaviour on the * Win9x/NT split, but I suspect it's worse than that. * See wishlist item `win-dead-keys' for more horrible detail * and speculations. */ int i; static WORD keys[3]; static BYTE keysb[3]; r = ToAsciiEx(wParam, scan, keystate, keys, 0, kbd_layout); if (r > 0) { for (i = 0; i < r; i++) { keysb[i] = (BYTE)keys[i]; } MultiByteToWideChar(CP_ACP, 0, (LPCSTR)keysb, r, keys_unicode, lenof(keys_unicode)); } } #ifdef SHOW_TOASCII_RESULT if (r == 1 && !key_down) { if (alt_sum) { if (in_utf(term) || ucsdata.dbcs_screenfont) debug((", (U+%04x)", alt_sum)); else debug((", LCH(%d)", alt_sum)); } else { debug((", ACH(%d)", keys_unicode[0])); } } else if (r > 0) { int r1; debug((", ASC(")); for (r1 = 0; r1 < r; r1++) { debug(("%s%d", r1 ? "," : "", keys_unicode[r1])); } debug((")")); } #endif if (r > 0) { WCHAR keybuf; p = output; for (i = 0; i < r; i++) { wchar_t wch = keys_unicode[i]; if (compose_state == 2 && wch >= ' ' && wch < 0x80) { compose_char = wch; compose_state++; continue; } if (compose_state == 3 && wch >= ' ' && wch < 0x80) { int nc; compose_state = 0; if ((nc = check_compose(compose_char, wch)) == -1) { MessageBeep(MB_ICONHAND); return 0; } keybuf = nc; term_seen_key_event(term); if (ldisc) luni_send(ldisc, &keybuf, 1, 1); continue; } compose_state = 0; if (!key_down) { if (alt_sum) { if (in_utf(term) || ucsdata.dbcs_screenfont) { keybuf = alt_sum; term_seen_key_event(term); if (ldisc) luni_send(ldisc, &keybuf, 1, 1); } else { char ch = (char) alt_sum; /* * We need not bother about stdin * backlogs here, because in GUI PuTTY * we can't do anything about it * anyway; there's no means of asking * Windows to hold off on KEYDOWN * messages. We _have_ to buffer * everything we're sent. */ term_seen_key_event(term); if (ldisc) ldisc_send(ldisc, &ch, 1, 1); } alt_sum = 0; } else { term_seen_key_event(term); if (ldisc) luni_send(ldisc, &wch, 1, 1); } } else { if(capsOn && wch < 0x80) { WCHAR cbuf[2]; cbuf[0] = 27; cbuf[1] = xlat_uskbd2cyrllic(wch); term_seen_key_event(term); if (ldisc) luni_send(ldisc, cbuf+!left_alt, 1+!!left_alt, 1); } else { WCHAR cbuf[2]; cbuf[0] = '\033'; cbuf[1] = wch; term_seen_key_event(term); if (ldisc) luni_send(ldisc, cbuf +!left_alt, 1+!!left_alt, 1); } } show_mouseptr(0); } /* This is so the ALT-Numpad and dead keys work correctly. */ keys_unicode[0] = 0; return p - output; } /* If we're definitly not building up an ALT-54321 then clear it */ if (!left_alt) keys_unicode[0] = 0; /* If we will be using alt_sum fix the 256s */ else if (keys_unicode[0] && (in_utf(term) || ucsdata.dbcs_screenfont)) keys_unicode[0] = 10; } /* * ALT alone may or may not want to bring up the System menu. * If it's not meant to, we return 0 on presses or releases of * ALT, to show that we've swallowed the keystroke. Otherwise * we return -1, which means Windows will give the keystroke * its default handling (i.e. bring up the System menu). */ if (wParam == VK_MENU && !conf_get_int(conf, CONF_alt_only)) return 0; return -1; } void set_title(void *frontend, char *title) { sfree(window_name); window_name = snewn(1 + strlen(title), char); strcpy(window_name, title); if (conf_get_int(conf, CONF_win_name_always) || !IsIconic(hwnd)) SetWindowText(hwnd, title); } void set_icon(void *frontend, char *title) { sfree(icon_name); icon_name = snewn(1 + strlen(title), char); strcpy(icon_name, title); if (!conf_get_int(conf, CONF_win_name_always) && IsIconic(hwnd)) SetWindowText(hwnd, title); } void set_sbar(void *frontend, int total, int start, int page) { SCROLLINFO si; if (!conf_get_int(conf, is_full_screen() ? CONF_scrollbar_in_fullscreen : CONF_scrollbar)) return; si.cbSize = sizeof(si); si.fMask = SIF_ALL | SIF_DISABLENOSCROLL; si.nMin = 0; si.nMax = total - 1; si.nPage = page; |
︙ | ︙ | |||
4584 4585 4586 4587 4588 4589 4590 | colours[n] = RGB(r, g, b); } void palette_set(void *frontend, int n, int r, int g, int b) { if (n >= 16) n += 256 - 16; | | | 4803 4804 4805 4806 4807 4808 4809 4810 4811 4812 4813 4814 4815 4816 4817 | colours[n] = RGB(r, g, b); } void palette_set(void *frontend, int n, int r, int g, int b) { if (n >= 16) n += 256 - 16; if (n >= NALLCOLOURS) return; real_palette_set(n, r, g, b); if (pal) { HDC hdc = get_ctx(frontend); UnrealizeObject(pal); RealizePalette(hdc); free_ctx(hdc); |
︙ | ︙ | |||
4684 4685 4686 4687 4688 4689 4690 | if (!clipdata || !clipdata2) { if (clipdata) GlobalFree(clipdata); if (clipdata2) GlobalFree(clipdata2); return; } | | > > > | > > > > | > | | | 4903 4904 4905 4906 4907 4908 4909 4910 4911 4912 4913 4914 4915 4916 4917 4918 4919 4920 4921 4922 4923 4924 4925 4926 4927 4928 4929 4930 4931 4932 4933 4934 4935 4936 4937 4938 4939 4940 4941 4942 4943 4944 4945 4946 4947 4948 4949 4950 4951 4952 4953 4954 | if (!clipdata || !clipdata2) { if (clipdata) GlobalFree(clipdata); if (clipdata2) GlobalFree(clipdata2); return; } if (!(lock = GlobalLock(clipdata))) { GlobalFree(clipdata); GlobalFree(clipdata2); return; } if (!(lock2 = GlobalLock(clipdata2))) { GlobalUnlock(clipdata); GlobalFree(clipdata); GlobalFree(clipdata2); return; } memcpy(lock, data, len * sizeof(wchar_t)); WideCharToMultiByte(CP_ACP, 0, data, len, lock2, len2, NULL, NULL); if (conf_get_int(conf, CONF_rtf_paste)) { wchar_t unitab[256]; char *rtf = NULL; unsigned char *tdata = (unsigned char *)lock2; wchar_t *udata = (wchar_t *)lock; int rtflen = 0, uindex = 0, tindex = 0; int rtfsize = 0; int multilen, blen, alen, totallen, i; char before[16], after[4]; int fgcolour, lastfgcolour = 0; int bgcolour, lastbgcolour = 0; int attrBold, lastAttrBold = 0; int attrUnder, lastAttrUnder = 0; int palette[NALLCOLOURS]; int numcolours; FontSpec *font = conf_get_fontspec(conf, CONF_font); get_unitab(CP_ACP, unitab, 0); rtfsize = 100 + strlen(font->name); rtf = snewn(rtfsize, char); rtflen = sprintf(rtf, "{\\rtf1\\ansi\\deff0{\\fonttbl\\f0\\fmodern %s;}\\f0\\fs%d", font->name, font->height*2); /* * Add colour palette * {\colortbl ;\red255\green0\blue0;\red0\green0\blue128;} */ /* |
︙ | ︙ | |||
4736 4737 4738 4739 4740 4741 4742 | if (attr[i] & ATTR_REVERSE) { int tmpcolour = fgcolour; /* Swap foreground and background */ fgcolour = bgcolour; bgcolour = tmpcolour; } | | | 4963 4964 4965 4966 4967 4968 4969 4970 4971 4972 4973 4974 4975 4976 4977 | if (attr[i] & ATTR_REVERSE) { int tmpcolour = fgcolour; /* Swap foreground and background */ fgcolour = bgcolour; bgcolour = tmpcolour; } if (bold_colours && (attr[i] & ATTR_BOLD)) { if (fgcolour < 8) /* ANSI colours */ fgcolour += 8; else if (fgcolour >= 256) /* Default colours */ fgcolour ++; } if (attr[i] & ATTR_BLINK) { |
︙ | ︙ | |||
4827 4828 4829 4830 4831 4832 4833 | if (attr[tindex] & ATTR_REVERSE) { int tmpcolour = fgcolour; /* Swap foreground and background */ fgcolour = bgcolour; bgcolour = tmpcolour; } | | | | | 5054 5055 5056 5057 5058 5059 5060 5061 5062 5063 5064 5065 5066 5067 5068 5069 5070 5071 5072 5073 5074 5075 5076 5077 5078 5079 5080 5081 5082 5083 5084 5085 5086 5087 5088 5089 5090 5091 5092 5093 5094 5095 5096 5097 5098 5099 5100 5101 5102 5103 5104 | if (attr[tindex] & ATTR_REVERSE) { int tmpcolour = fgcolour; /* Swap foreground and background */ fgcolour = bgcolour; bgcolour = tmpcolour; } if (bold_colours && (attr[tindex] & ATTR_BOLD)) { if (fgcolour < 8) /* ANSI colours */ fgcolour += 8; else if (fgcolour >= 256) /* Default colours */ fgcolour ++; } if (attr[tindex] & ATTR_BLINK) { if (bgcolour < 8) /* ANSI colours */ bgcolour += 8; else if (bgcolour >= 256) /* Default colours */ bgcolour ++; } /* * Collect other attributes */ if (bold_font_mode != BOLD_NONE) attrBold = attr[tindex] & ATTR_BOLD; else attrBold = 0; attrUnder = attr[tindex] & ATTR_UNDER; /* * Reverse video * o If video isn't reversed, ignore colour attributes for default foregound * or background. * o Special case where bolded text is displayed using the default foregound * and background colours - force to bolded RTF. */ if (!(attr[tindex] & ATTR_REVERSE)) { if (bgcolour >= 256) /* Default color */ bgcolour = -1; /* No coloring */ if (fgcolour >= 256) { /* Default colour */ if (bold_colours && (fgcolour & 1) && bgcolour == -1) attrBold = ATTR_BOLD; /* Emphasize text with bold attribute */ fgcolour = -1; /* No coloring */ } } /* |
︙ | ︙ | |||
5128 5129 5130 5131 5132 5133 5134 | sprintf(morestuff, "%.70s Fatal Error", appname); MessageBox(hwnd, stuff, morestuff, MB_SYSTEMMODAL | MB_ICONERROR | MB_OK); sfree(stuff); cleanup_exit(1); } | < > > > > | < > > | < < > > > > > > | 5355 5356 5357 5358 5359 5360 5361 5362 5363 5364 5365 5366 5367 5368 5369 5370 5371 5372 5373 5374 5375 5376 5377 5378 5379 5380 5381 5382 | sprintf(morestuff, "%.70s Fatal Error", appname); MessageBox(hwnd, stuff, morestuff, MB_SYSTEMMODAL | MB_ICONERROR | MB_OK); sfree(stuff); cleanup_exit(1); } /* * Print a message box and don't close the connection. */ void nonfatal(char *fmt, ...) { va_list ap; char *stuff, morestuff[100]; va_start(ap, fmt); stuff = dupvprintf(fmt, ap); va_end(ap); sprintf(morestuff, "%.70s Error", appname); MessageBox(hwnd, stuff, morestuff, MB_ICONERROR | MB_OK); sfree(stuff); } static BOOL flash_window_ex(DWORD dwFlags, UINT uCount, DWORD dwTimeout) { if (p_FlashWindowEx) { FLASHWINFO fi; fi.cbSize = sizeof(fi); |
︙ | ︙ | |||
5159 5160 5161 5162 5163 5164 5165 | static long next_flash; static int flashing = 0; /* * Timer for platforms where we must maintain window flashing manually * (e.g., Win95). */ | | | > | | 5394 5395 5396 5397 5398 5399 5400 5401 5402 5403 5404 5405 5406 5407 5408 5409 5410 5411 5412 5413 5414 5415 5416 5417 5418 5419 5420 5421 5422 | static long next_flash; static int flashing = 0; /* * Timer for platforms where we must maintain window flashing manually * (e.g., Win95). */ static void flash_window_timer(void *ctx, unsigned long now) { if (flashing && now == next_flash) { flash_window(1); } } /* * Manage window caption / taskbar flashing, if enabled. * 0 = stop, 1 = maintain, 2 = start */ static void flash_window(int mode) { int beep_ind = conf_get_int(conf, CONF_beep_ind); if ((mode == 0) || (beep_ind == B_IND_DISABLED)) { /* stop */ if (flashing) { flashing = 0; if (p_FlashWindowEx) flash_window_ex(FLASHW_STOP, 0, 0); else FlashWindow(hwnd, FALSE); |
︙ | ︙ | |||
5194 5195 5196 5197 5198 5199 5200 | /* For so-called "steady" mode, we use uCount=2, which * seems to be the traditional number of flashes used * by user notifications (e.g., by Explorer). * uCount=0 appears to enable continuous flashing, per * "flashing" mode, although I haven't seen this * documented. */ flash_window_ex(FLASHW_ALL | FLASHW_TIMER, | | | | 5430 5431 5432 5433 5434 5435 5436 5437 5438 5439 5440 5441 5442 5443 5444 5445 5446 5447 5448 5449 5450 5451 5452 5453 | /* For so-called "steady" mode, we use uCount=2, which * seems to be the traditional number of flashes used * by user notifications (e.g., by Explorer). * uCount=0 appears to enable continuous flashing, per * "flashing" mode, although I haven't seen this * documented. */ flash_window_ex(FLASHW_ALL | FLASHW_TIMER, (beep_ind == B_IND_FLASH ? 0 : 2), 0 /* system cursor blink rate */); /* No need to schedule timer */ } else { FlashWindow(hwnd, TRUE); next_flash = schedule_timer(450, flash_window_timer, hwnd); } } } else if ((mode == 1) && (beep_ind == B_IND_FLASH)) { /* maintain */ if (flashing && !p_FlashWindowEx) { FlashWindow(hwnd, TRUE); /* toggle */ next_flash = schedule_timer(450, flash_window_timer, hwnd); } } } |
︙ | ︙ | |||
5237 5238 5239 5240 5241 5242 5243 | MessageBeep(MB_OK); /* * The above MessageBeep call takes time, so we record the * time _after_ it finishes rather than before it starts. */ lastbeep = GetTickCount(); } else if (mode == BELL_WAVEFILE) { | > | | | | | 5473 5474 5475 5476 5477 5478 5479 5480 5481 5482 5483 5484 5485 5486 5487 5488 5489 5490 5491 5492 5493 5494 5495 5496 5497 | MessageBeep(MB_OK); /* * The above MessageBeep call takes time, so we record the * time _after_ it finishes rather than before it starts. */ lastbeep = GetTickCount(); } else if (mode == BELL_WAVEFILE) { Filename *bell_wavefile = conf_get_filename(conf, CONF_bell_wavefile); if (!PlaySound(bell_wavefile->path, NULL, SND_ASYNC | SND_FILENAME)) { char buf[sizeof(bell_wavefile->path) + 80]; char otherbuf[100]; sprintf(buf, "Unable to play sound file\n%s\n" "Using default sound instead", bell_wavefile->path); sprintf(otherbuf, "%.70s Sound Error", appname); MessageBox(hwnd, buf, otherbuf, MB_OK | MB_ICONEXCLAMATION); conf_set_int(conf, CONF_beep, BELL_DEFAULT); } } else if (mode == BELL_PCSPEAKER) { static long lastbeep = 0; long beepdiff; beepdiff = GetTickCount() - lastbeep; if (beepdiff >= 0 && beepdiff < 50) |
︙ | ︙ | |||
5292 5293 5294 5295 5296 5297 5298 | } /* * Move the window in response to a server-side request. */ void move_window(void *frontend, int x, int y) { | > | | | | 5529 5530 5531 5532 5533 5534 5535 5536 5537 5538 5539 5540 5541 5542 5543 5544 5545 5546 5547 5548 5549 5550 5551 5552 5553 5554 5555 5556 5557 5558 | } /* * Move the window in response to a server-side request. */ void move_window(void *frontend, int x, int y) { int resize_action = conf_get_int(conf, CONF_resize_action); if (resize_action == RESIZE_DISABLED || resize_action == RESIZE_FONT || IsZoomed(hwnd)) return; SetWindowPos(hwnd, NULL, x, y, 0, 0, SWP_NOSIZE | SWP_NOZORDER); } /* * Move the window to the top or bottom of the z-order in response * to a server-side request. */ void set_zorder(void *frontend, int top) { if (conf_get_int(conf, CONF_alwaysontop)) return; /* ignore */ SetWindowPos(hwnd, top ? HWND_TOP : HWND_BOTTOM, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); } /* * Refresh the window in response to a server-side request. |
︙ | ︙ | |||
5428 5429 5430 5431 5432 5433 5434 | if (is_full_screen()) return; /* Remove the window furniture. */ style = GetWindowLongPtr(hwnd, GWL_STYLE); style &= ~(WS_CAPTION | WS_BORDER | WS_THICKFRAME); | | | 5666 5667 5668 5669 5670 5671 5672 5673 5674 5675 5676 5677 5678 5679 5680 | if (is_full_screen()) return; /* Remove the window furniture. */ style = GetWindowLongPtr(hwnd, GWL_STYLE); style &= ~(WS_CAPTION | WS_BORDER | WS_THICKFRAME); if (conf_get_int(conf, CONF_scrollbar_in_fullscreen)) style |= WS_VSCROLL; else style &= ~WS_VSCROLL; SetWindowLongPtr(hwnd, GWL_STYLE, style); /* Resize ourselves to exactly cover the nearest monitor. */ get_fullscreen_rect(&ss); |
︙ | ︙ | |||
5463 5464 5465 5466 5467 5468 5469 | static void clear_full_screen() { DWORD oldstyle, style; /* Reinstate the window furniture. */ style = oldstyle = GetWindowLongPtr(hwnd, GWL_STYLE); style |= WS_CAPTION | WS_BORDER; | | | | 5701 5702 5703 5704 5705 5706 5707 5708 5709 5710 5711 5712 5713 5714 5715 5716 5717 5718 5719 | static void clear_full_screen() { DWORD oldstyle, style; /* Reinstate the window furniture. */ style = oldstyle = GetWindowLongPtr(hwnd, GWL_STYLE); style |= WS_CAPTION | WS_BORDER; if (conf_get_int(conf, CONF_resize_action) == RESIZE_DISABLED) style &= ~WS_THICKFRAME; else style |= WS_THICKFRAME; if (conf_get_int(conf, CONF_scrollbar)) style |= WS_VSCROLL; else style &= ~WS_VSCROLL; if (style != oldstyle) { SetWindowLongPtr(hwnd, GWL_STYLE, style); SetWindowPos(hwnd, NULL, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | |
︙ | ︙ | |||
5521 5522 5523 5524 5525 5526 5527 5528 5529 5530 5531 5532 5533 5534 | return term_data(term, is_stderr, data, len); } int from_backend_untrusted(void *frontend, const char *data, int len) { return term_data_untrusted(term, data, len); } int get_userpass_input(prompts_t *p, unsigned char *in, int inlen) { int ret; ret = cmdline_get_passwd_input(p, in, inlen); if (ret == -1) ret = term_get_userpass_input(term, p, in, inlen); | > > > > > | 5759 5760 5761 5762 5763 5764 5765 5766 5767 5768 5769 5770 5771 5772 5773 5774 5775 5776 5777 | return term_data(term, is_stderr, data, len); } int from_backend_untrusted(void *frontend, const char *data, int len) { return term_data_untrusted(term, data, len); } int from_backend_eof(void *frontend) { return TRUE; /* do respond to incoming EOF with outgoing */ } int get_userpass_input(prompts_t *p, unsigned char *in, int inlen) { int ret; ret = cmdline_get_passwd_input(p, in, inlen); if (ret == -1) ret = term_get_userpass_input(term, p, in, inlen); |
︙ | ︙ |
Changes to windows/wingss.c.
︙ | ︙ | |||
61 62 63 64 65 66 67 | const Ssh_gss_buf gss_mech_krb5={9,"\x2A\x86\x48\x86\xF7\x12\x01\x02\x02"}; const char *gsslogmsg = NULL; static void ssh_sspi_bind_fns(struct ssh_gss_library *lib); | | > | 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 | const Ssh_gss_buf gss_mech_krb5={9,"\x2A\x86\x48\x86\xF7\x12\x01\x02\x02"}; const char *gsslogmsg = NULL; static void ssh_sspi_bind_fns(struct ssh_gss_library *lib); struct ssh_gss_liblist *ssh_gss_setup(Conf *conf) { HMODULE module; HKEY regkey; struct ssh_gss_liblist *list = snew(struct ssh_gss_liblist); char *path; list->libraries = snewn(3, struct ssh_gss_library); list->nlibraries = 0; /* MIT Kerberos GSSAPI implementation */ /* TODO: For 64-bit builds, check for gssapi64.dll */ module = NULL; |
︙ | ︙ | |||
144 145 146 147 148 149 150 | ssh_sspi_bind_fns(lib); } /* * Custom GSSAPI DLL. */ module = NULL; | | > | | | 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 | ssh_sspi_bind_fns(lib); } /* * Custom GSSAPI DLL. */ module = NULL; path = conf_get_filename(conf, CONF_ssh_gss_custom)->path; if (*path) { module = LoadLibrary(path); } if (module) { struct ssh_gss_library *lib = &list->libraries[list->nlibraries++]; lib->id = 2; lib->gsslogmsg = dupprintf("Using GSSAPI from user-specified" " library '%s'", path); lib->handle = (void *)module; #define BIND_GSS_FN(name) \ lib->u.gssapi.name = (t_gss_##name) GetProcAddress(module, "gss_" #name) BIND_GSS_FN(delete_sec_context); BIND_GSS_FN(display_status); |
︙ | ︙ |
Changes to windows/winhandl.c.
︙ | ︙ | |||
60 61 62 63 64 65 66 67 68 69 70 71 72 73 | HANDLE ev_from_main; /* event used to signal back to us */ int moribund; /* are we going to kill this soon? */ int done; /* request subthread to terminate */ int defunct; /* has the subthread already gone? */ int busy; /* operation currently in progress? */ void *privdata; /* for client to remember who they are */ }; /* ---------------------------------------------------------------------- * Input threads. */ /* * Data required by an input thread. | > > | 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 | HANDLE ev_from_main; /* event used to signal back to us */ int moribund; /* are we going to kill this soon? */ int done; /* request subthread to terminate */ int defunct; /* has the subthread already gone? */ int busy; /* operation currently in progress? */ void *privdata; /* for client to remember who they are */ }; typedef enum { INPUT, OUTPUT, FOREIGN } HandleType; /* ---------------------------------------------------------------------- * Input threads. */ /* * Data required by an input thread. |
︙ | ︙ | |||
246 247 248 249 250 251 252 253 254 255 256 257 258 259 | DWORD lenwritten; /* how much data we actually wrote */ int writeerr; /* return value from WriteFile */ /* * Data only ever read or written by the main thread. */ bufchain queued_data; /* data still waiting to be written */ /* * Callback function called when the backlog in the bufchain * drops. */ handle_outputfn_t sentdata; }; | > | 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 | DWORD lenwritten; /* how much data we actually wrote */ int writeerr; /* return value from WriteFile */ /* * Data only ever read or written by the main thread. */ bufchain queued_data; /* data still waiting to be written */ enum { EOF_NO, EOF_PENDING, EOF_SENT } outgoingeof; /* * Callback function called when the backlog in the bufchain * drops. */ handle_outputfn_t sentdata; }; |
︙ | ︙ | |||
316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 | if (!ctx->busy && bufchain_size(&ctx->queued_data)) { bufchain_prefix(&ctx->queued_data, &senddata, &sendlen); ctx->buffer = senddata; ctx->len = sendlen; SetEvent(ctx->ev_from_main); ctx->busy = TRUE; } } /* ---------------------------------------------------------------------- * Unified code handling both input and output threads. */ struct handle { | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > | 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 | if (!ctx->busy && bufchain_size(&ctx->queued_data)) { bufchain_prefix(&ctx->queued_data, &senddata, &sendlen); ctx->buffer = senddata; ctx->len = sendlen; SetEvent(ctx->ev_from_main); ctx->busy = TRUE; } else if (!ctx->busy && bufchain_size(&ctx->queued_data) == 0 && ctx->outgoingeof == EOF_PENDING) { CloseHandle(ctx->h); ctx->h = INVALID_HANDLE_VALUE; ctx->outgoingeof = EOF_SENT; } } /* ---------------------------------------------------------------------- * 'Foreign events'. These are handle structures which just contain a * single event object passed to us by another module such as * winnps.c, so that they can make use of our handle_get_events / * handle_got_event mechanism for communicating with application main * loops. */ struct handle_foreign { /* * Copy of the handle_generic structure. */ HANDLE h; /* the handle itself */ HANDLE ev_to_main; /* event used to signal main thread */ HANDLE ev_from_main; /* event used to signal back to us */ int moribund; /* are we going to kill this soon? */ int done; /* request subthread to terminate */ int defunct; /* has the subthread already gone? */ int busy; /* operation currently in progress? */ void *privdata; /* for client to remember who they are */ /* * Our own data, just consisting of knowledge of who to call back. */ void (*callback)(void *); void *ctx; }; /* ---------------------------------------------------------------------- * Unified code handling both input and output threads. */ struct handle { HandleType type; union { struct handle_generic g; struct handle_input i; struct handle_output o; struct handle_foreign f; } u; }; static tree234 *handles_by_evtomain; static int handle_cmp_evtomain(void *av, void *bv) { |
︙ | ︙ | |||
366 367 368 369 370 371 372 | struct handle *handle_input_new(HANDLE handle, handle_inputfn_t gotdata, void *privdata, int flags) { struct handle *h = snew(struct handle); DWORD in_threadid; /* required for Win9x */ | | | 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 | struct handle *handle_input_new(HANDLE handle, handle_inputfn_t gotdata, void *privdata, int flags) { struct handle *h = snew(struct handle); DWORD in_threadid; /* required for Win9x */ h->type = INPUT; h->u.i.h = handle; h->u.i.ev_to_main = CreateEvent(NULL, FALSE, FALSE, NULL); h->u.i.ev_from_main = CreateEvent(NULL, FALSE, FALSE, NULL); h->u.i.gotdata = gotdata; h->u.i.defunct = FALSE; h->u.i.moribund = FALSE; h->u.i.done = FALSE; |
︙ | ︙ | |||
394 395 396 397 398 399 400 | struct handle *handle_output_new(HANDLE handle, handle_outputfn_t sentdata, void *privdata, int flags) { struct handle *h = snew(struct handle); DWORD out_threadid; /* required for Win9x */ | | > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > | 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 | struct handle *handle_output_new(HANDLE handle, handle_outputfn_t sentdata, void *privdata, int flags) { struct handle *h = snew(struct handle); DWORD out_threadid; /* required for Win9x */ h->type = OUTPUT; h->u.o.h = handle; h->u.o.ev_to_main = CreateEvent(NULL, FALSE, FALSE, NULL); h->u.o.ev_from_main = CreateEvent(NULL, FALSE, FALSE, NULL); h->u.o.busy = FALSE; h->u.o.defunct = FALSE; h->u.o.moribund = FALSE; h->u.o.done = FALSE; h->u.o.privdata = privdata; bufchain_init(&h->u.o.queued_data); h->u.o.outgoingeof = EOF_NO; h->u.o.sentdata = sentdata; h->u.o.flags = flags; if (!handles_by_evtomain) handles_by_evtomain = newtree234(handle_cmp_evtomain); add234(handles_by_evtomain, h); CreateThread(NULL, 0, handle_output_threadfunc, &h->u.o, 0, &out_threadid); return h; } struct handle *handle_add_foreign_event(HANDLE event, void (*callback)(void *), void *ctx) { struct handle *h = snew(struct handle); h->type = FOREIGN; h->u.f.h = INVALID_HANDLE_VALUE; h->u.f.ev_to_main = event; h->u.f.ev_from_main = INVALID_HANDLE_VALUE; h->u.f.defunct = TRUE; /* we have no thread in the first place */ h->u.f.moribund = FALSE; h->u.f.done = FALSE; h->u.f.privdata = NULL; h->u.f.callback = callback; h->u.f.ctx = ctx; h->u.f.busy = TRUE; if (!handles_by_evtomain) handles_by_evtomain = newtree234(handle_cmp_evtomain); add234(handles_by_evtomain, h); return h; } int handle_write(struct handle *h, const void *data, int len) { assert(h->type == OUTPUT); assert(h->u.o.outgoingeof == EOF_NO); bufchain_add(&h->u.o.queued_data, data, len); handle_try_output(&h->u.o); return bufchain_size(&h->u.o.queued_data); } void handle_write_eof(struct handle *h) { /* * This function is called when we want to proactively send an * end-of-file notification on the handle. We can only do this by * actually closing the handle - so never call this on a * bidirectional handle if we're still interested in its incoming * direction! */ assert(h->type == OUTPUT); if (!h->u.o.outgoingeof == EOF_NO) { h->u.o.outgoingeof = EOF_PENDING; handle_try_output(&h->u.o); } } HANDLE *handle_get_events(int *nevents) { HANDLE *ret; struct handle *h; int i, n, size; |
︙ | ︙ | |||
455 456 457 458 459 460 461 | *nevents = n; return ret; } static void handle_destroy(struct handle *h) { | | | 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 | *nevents = n; return ret; } static void handle_destroy(struct handle *h) { if (h->type == OUTPUT) bufchain_clear(&h->u.o.queued_data); CloseHandle(h->u.g.ev_from_main); CloseHandle(h->u.g.ev_to_main); del234(handles_by_evtomain, h); sfree(h); } |
︙ | ︙ | |||
532 533 534 535 536 537 538 | h->u.g.done = TRUE; h->u.g.busy = TRUE; SetEvent(h->u.g.ev_from_main); } return; } | | > > | > > > > > > > | | | 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 | h->u.g.done = TRUE; h->u.g.busy = TRUE; SetEvent(h->u.g.ev_from_main); } return; } switch (h->type) { int backlog; case INPUT: h->u.i.busy = FALSE; /* * A signal on an input handle means data has arrived. */ if (h->u.i.len == 0) { /* * EOF, or (nearly equivalently) read error. */ 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); handle_throttle(&h->u.i, backlog); } break; case OUTPUT: h->u.o.busy = FALSE; /* * A signal on an output handle means we have completed a * write. Call the callback to indicate that the output * buffer size has decreased, or to indicate an error. */ 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, -h->u.o.writeerr); h->u.o.defunct = TRUE; } else { bufchain_consume(&h->u.o.queued_data, h->u.o.lenwritten); h->u.o.sentdata(h, bufchain_size(&h->u.o.queued_data)); handle_try_output(&h->u.o); } break; case FOREIGN: /* Just call the callback. */ h->u.f.callback(h->u.f.ctx); break; } } void handle_unthrottle(struct handle *h, int backlog) { assert(h->type == INPUT); handle_throttle(&h->u.i, backlog); } int handle_backlog(struct handle *h) { assert(h->type == OUTPUT); return bufchain_size(&h->u.o.queued_data); } void *handle_get_privdata(struct handle *h) { return h->u.g.privdata; } |
Changes to windows/winhelp.c.
︙ | ︙ | |||
77 78 79 80 81 82 83 | { /* * FIXME: it would be nice here to disregard help_path on * platforms that didn't have WINHLP32. But that's probably * unrealistic, since even Vista will have it if the user * specifically downloads it. */ | | | 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 | { /* * FIXME: it would be nice here to disregard help_path on * platforms that didn't have WINHLP32. But that's probably * unrealistic, since even Vista will have it if the user * specifically downloads it. */ return (help_path != NULL #ifndef NO_HTMLHELP || chm_path #endif /* NO_HTMLHELP */ ); } void launch_help(HWND hwnd, const char *topic) |
︙ | ︙ |
Changes to windows/winhelp.h.
︙ | ︙ | |||
95 96 97 98 99 100 101 102 103 104 105 106 107 108 | #define WINHELP_CTX_ssh_nopty "ssh.nopty:config-ssh-pty" #define WINHELP_CTX_ssh_ttymodes "ssh.ttymodes:config-ttymodes" #define WINHELP_CTX_ssh_noshell "ssh.noshell:config-ssh-noshell" #define WINHELP_CTX_ssh_ciphers "ssh.ciphers:config-ssh-encryption" #define WINHELP_CTX_ssh_protocol "ssh.protocol:config-ssh-prot" #define WINHELP_CTX_ssh_command "ssh.command:config-command" #define WINHELP_CTX_ssh_compress "ssh.compress:config-ssh-comp" #define WINHELP_CTX_ssh_kexlist "ssh.kex.order:config-ssh-kex-order" #define WINHELP_CTX_ssh_kex_repeat "ssh.kex.repeat:config-ssh-kex-rekey" #define WINHELP_CTX_ssh_auth_bypass "ssh.auth.bypass:config-ssh-noauth" #define WINHELP_CTX_ssh_auth_banner "ssh.auth.banner:config-ssh-banner" #define WINHELP_CTX_ssh_auth_privkey "ssh.auth.privkey:config-ssh-privkey" #define WINHELP_CTX_ssh_auth_agentfwd "ssh.auth.agentfwd:config-ssh-agentfwd" #define WINHELP_CTX_ssh_auth_changeuser "ssh.auth.changeuser:config-ssh-changeuser" | > | 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 | #define WINHELP_CTX_ssh_nopty "ssh.nopty:config-ssh-pty" #define WINHELP_CTX_ssh_ttymodes "ssh.ttymodes:config-ttymodes" #define WINHELP_CTX_ssh_noshell "ssh.noshell:config-ssh-noshell" #define WINHELP_CTX_ssh_ciphers "ssh.ciphers:config-ssh-encryption" #define WINHELP_CTX_ssh_protocol "ssh.protocol:config-ssh-prot" #define WINHELP_CTX_ssh_command "ssh.command:config-command" #define WINHELP_CTX_ssh_compress "ssh.compress:config-ssh-comp" #define WINHELP_CTX_ssh_share "ssh.sharing:config-ssh-sharing" #define WINHELP_CTX_ssh_kexlist "ssh.kex.order:config-ssh-kex-order" #define WINHELP_CTX_ssh_kex_repeat "ssh.kex.repeat:config-ssh-kex-rekey" #define WINHELP_CTX_ssh_auth_bypass "ssh.auth.bypass:config-ssh-noauth" #define WINHELP_CTX_ssh_auth_banner "ssh.auth.banner:config-ssh-banner" #define WINHELP_CTX_ssh_auth_privkey "ssh.auth.privkey:config-ssh-privkey" #define WINHELP_CTX_ssh_auth_agentfwd "ssh.auth.agentfwd:config-ssh-agentfwd" #define WINHELP_CTX_ssh_auth_changeuser "ssh.auth.changeuser:config-ssh-changeuser" |
︙ | ︙ | |||
140 141 142 143 144 145 146 147 148 149 150 151 152 153 | #define WINHELP_CTX_ssh_bugs_ignore2 "ssh.bugs.ignore2:config-ssh-bug-ignore2" #define WINHELP_CTX_ssh_bugs_hmac2 "ssh.bugs.hmac2:config-ssh-bug-hmac2" #define WINHELP_CTX_ssh_bugs_derivekey2 "ssh.bugs.derivekey2:config-ssh-bug-derivekey2" #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" #define WINHELP_CTX_serial_stopbits "serial.stopbits:config-serial-stopbits" #define WINHELP_CTX_serial_parity "serial.parity:config-serial-parity" #define WINHELP_CTX_serial_flow "serial.flow:config-serial-flow" | > | 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 | #define WINHELP_CTX_ssh_bugs_ignore2 "ssh.bugs.ignore2:config-ssh-bug-ignore2" #define WINHELP_CTX_ssh_bugs_hmac2 "ssh.bugs.hmac2:config-ssh-bug-hmac2" #define WINHELP_CTX_ssh_bugs_derivekey2 "ssh.bugs.derivekey2:config-ssh-bug-derivekey2" #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_ssh_bugs_winadj "ssh.bugs.winadj:config-ssh-bug-winadj" #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" #define WINHELP_CTX_serial_stopbits "serial.stopbits:config-serial-stopbits" #define WINHELP_CTX_serial_parity "serial.parity:config-serial-parity" #define WINHELP_CTX_serial_flow "serial.flow:config-serial-flow" |
︙ | ︙ | |||
164 165 166 167 168 169 170 | #define WINHELP_CTX_puttygen_comment "puttygen.comment:puttygen-comment" #define WINHELP_CTX_puttygen_passphrase "puttygen.passphrase:puttygen-passphrase" #define WINHELP_CTX_puttygen_savepriv "puttygen.savepriv:puttygen-savepriv" #define WINHELP_CTX_puttygen_savepub "puttygen.savepub:puttygen-savepub" #define WINHELP_CTX_puttygen_pastekey "puttygen.pastekey:puttygen-pastekey" #define WINHELP_CTX_puttygen_load "puttygen.load:puttygen-load" #define WINHELP_CTX_puttygen_conversions "puttygen.conversions:puttygen-conversions" | < < < < < < < < < < < < < | 166 167 168 169 170 171 172 173 174 175 176 177 178 179 | #define WINHELP_CTX_puttygen_comment "puttygen.comment:puttygen-comment" #define WINHELP_CTX_puttygen_passphrase "puttygen.passphrase:puttygen-passphrase" #define WINHELP_CTX_puttygen_savepriv "puttygen.savepriv:puttygen-savepriv" #define WINHELP_CTX_puttygen_savepub "puttygen.savepub:puttygen-savepub" #define WINHELP_CTX_puttygen_pastekey "puttygen.pastekey:puttygen-pastekey" #define WINHELP_CTX_puttygen_load "puttygen.load:puttygen-load" #define WINHELP_CTX_puttygen_conversions "puttygen.conversions:puttygen-conversions" /* These are used in Windows-specific bits of the frontend. * We (ab)use "help context identifiers" (dwContextId) to identify them. */ #define HELPCTXID(x) WINHELP_CTXID_ ## x #define WINHELP_CTXID_no_help 0 |
︙ | ︙ |
Added windows/winhsock.c.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 | /* * General mechanism for wrapping up reading/writing of Windows * HANDLEs into a PuTTY Socket abstraction. */ #include <stdio.h> #include <assert.h> #include <limits.h> #define DEFINE_PLUG_METHOD_MACROS #include "tree234.h" #include "putty.h" #include "network.h" typedef struct Socket_handle_tag *Handle_Socket; struct Socket_handle_tag { const struct socket_function_table *fn; /* the above variable absolutely *must* be the first in this structure */ HANDLE send_H, recv_H; struct handle *send_h, *recv_h; /* * Freezing one of these sockets is a slightly fiddly business, * because the reads from the handle are happening in a separate * thread as blocking system calls and so once one is in progress * it can't sensibly be interrupted. Hence, after the user tries * to freeze one of these sockets, it's unavoidable that we may * receive one more load of data before we manage to get * winhandl.c to stop reading. */ enum { UNFROZEN, /* reading as normal */ FREEZING, /* have been set to frozen but winhandl is still reading */ FROZEN, /* really frozen - winhandl has been throttled */ THAWING /* we're gradually releasing our remaining data */ } frozen; /* We buffer data here if we receive it from winhandl while frozen. */ bufchain inputdata; char *error; Plug plug; }; static int handle_gotdata(struct handle *h, void *data, int len) { Handle_Socket ps = (Handle_Socket) handle_get_privdata(h); if (len < 0) { return plug_closing(ps->plug, "Read error from handle", 0, 0); } else if (len == 0) { return plug_closing(ps->plug, NULL, 0, 0); } else { assert(ps->frozen != FREEZING && ps->frozen != THAWING); if (ps->frozen == FREEZING) { /* * If we've received data while this socket is supposed to * be frozen (because the read winhandl.c started before * sk_set_frozen was called has now returned) then buffer * the data for when we unfreeze. */ bufchain_add(&ps->inputdata, data, len); /* * And return a very large backlog, to prevent further * data arriving from winhandl until we unfreeze. */ return INT_MAX; } else { return plug_receive(ps->plug, 0, data, len); } } } static void handle_sentdata(struct handle *h, int new_backlog) { Handle_Socket ps = (Handle_Socket) handle_get_privdata(h); plug_sent(ps->plug, new_backlog); } static Plug sk_handle_plug(Socket s, Plug p) { Handle_Socket ps = (Handle_Socket) s; Plug ret = ps->plug; if (p) ps->plug = p; return ret; } static void sk_handle_close(Socket s) { Handle_Socket ps = (Handle_Socket) s; handle_free(ps->send_h); handle_free(ps->recv_h); CloseHandle(ps->send_H); if (ps->recv_H != ps->send_H) CloseHandle(ps->recv_H); bufchain_clear(&ps->inputdata); sfree(ps); } static int sk_handle_write(Socket s, const char *data, int len) { Handle_Socket ps = (Handle_Socket) s; return handle_write(ps->send_h, data, len); } static int sk_handle_write_oob(Socket s, const char *data, int len) { /* * oob data is treated as inband; nasty, but nothing really * better we can do */ return sk_handle_write(s, data, len); } static void sk_handle_write_eof(Socket s) { Handle_Socket ps = (Handle_Socket) s; handle_write_eof(ps->send_h); } static void sk_handle_flush(Socket s) { /* Handle_Socket ps = (Handle_Socket) s; */ /* do nothing */ } static void handle_socket_unfreeze(void *psv) { Handle_Socket ps = (Handle_Socket) psv; void *data; int len, new_backlog; /* * If we've been put into a state other than THAWING since the * last callback, then we're done. */ if (ps->frozen != THAWING) return; /* * Get some of the data we've buffered. */ bufchain_prefix(&ps->inputdata, &data, &len); assert(len > 0); /* * Hand it off to the plug. */ new_backlog = plug_receive(ps->plug, 0, data, len); if (bufchain_size(&ps->inputdata) > 0) { /* * If there's still data in our buffer, stay in THAWING state, * and reschedule ourself. */ queue_toplevel_callback(handle_socket_unfreeze, ps); } else { /* * Otherwise, we've successfully thawed! */ ps->frozen = UNFROZEN; handle_unthrottle(ps->recv_h, new_backlog); } } static void sk_handle_set_frozen(Socket s, int is_frozen) { Handle_Socket ps = (Handle_Socket) s; if (is_frozen) { switch (ps->frozen) { case FREEZING: case FROZEN: return; /* nothing to do */ case THAWING: /* * We were in the middle of emptying our bufchain, and got * frozen again. In that case, winhandl.c is already * throttled, so just return to FROZEN state. The toplevel * callback will notice and disable itself. */ ps->frozen = FROZEN; break; case UNFROZEN: /* * The normal case. Go to FREEZING, and expect one more * load of data from winhandl if we're unlucky. */ ps->frozen = FREEZING; break; } } else { switch (ps->frozen) { case UNFROZEN: case THAWING: return; /* nothing to do */ case FREEZING: /* * If winhandl didn't send us any data throughout the time * we were frozen, then we'll still be in this state and * can just unfreeze in the trivial way. */ assert(bufchain_size(&ps->inputdata) == 0); ps->frozen = UNFROZEN; break; case FROZEN: /* * If we have buffered data, go to THAWING and start * releasing it in top-level callbacks. */ ps->frozen = THAWING; queue_toplevel_callback(handle_socket_unfreeze, ps); } } } static const char *sk_handle_socket_error(Socket s) { Handle_Socket ps = (Handle_Socket) s; return ps->error; } Socket make_handle_socket(HANDLE send_H, HANDLE recv_H, Plug plug, int overlapped) { static const struct socket_function_table socket_fn_table = { sk_handle_plug, sk_handle_close, sk_handle_write, sk_handle_write_oob, sk_handle_write_eof, sk_handle_flush, sk_handle_set_frozen, sk_handle_socket_error }; Handle_Socket ret; int flags = (overlapped ? HANDLE_FLAG_OVERLAPPED : 0); ret = snew(struct Socket_handle_tag); ret->fn = &socket_fn_table; ret->plug = plug; ret->error = NULL; ret->frozen = UNFROZEN; bufchain_init(&ret->inputdata); ret->recv_H = recv_H; ret->recv_h = handle_input_new(ret->recv_H, handle_gotdata, ret, flags); ret->send_H = send_H; ret->send_h = handle_output_new(ret->send_H, handle_sentdata, ret, flags); return (Socket) ret; } |
Changes to windows/winjump.c.
︙ | ︙ | |||
349 350 351 352 353 354 355 | 0x886d8eeb, 0x8cf2, 0x4446, {0x8d,0x02,0xcd,0xba,0x1d,0xbd,0xcf,0x99} }; static const PROPERTYKEY PKEY_Title = { {0xf29f85e0, 0x4ff9, 0x1068, {0xab,0x91,0x08,0x00,0x2b,0x27,0xb3,0xd9}}, 0x00000002 }; | | < | > > < | | 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 | 0x886d8eeb, 0x8cf2, 0x4446, {0x8d,0x02,0xcd,0xba,0x1d,0xbd,0xcf,0x99} }; static const PROPERTYKEY PKEY_Title = { {0xf29f85e0, 0x4ff9, 0x1068, {0xab,0x91,0x08,0x00,0x2b,0x27,0xb3,0xd9}}, 0x00000002 }; /* Type-checking macro to provide arguments for CoCreateInstance() * etc, ensuring that 'obj' really is a 'type **'. */ #define typecheck(checkexpr, result) \ (sizeof(checkexpr) ? (result) : (result)) #define COMPTR(type, obj) &IID_##type, \ typecheck((obj)-(type **)(obj), (void **)(void *)(obj)) static char putty_path[2048]; /* * Function to make an IShellLink describing a particular PuTTY * command. If 'appname' is null, the command run will be the one * returned by GetModuleFileName, i.e. our own executable; if it's |
︙ | ︙ | |||
406 407 408 409 410 411 412 | } else { app_path = dupstr(putty_path); } /* Check if this is a valid session, otherwise don't add. */ if (sessionname) { psettings_tmp = open_settings_r(sessionname); | | > > | > > | 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 | } else { app_path = dupstr(putty_path); } /* Check if this is a valid session, otherwise don't add. */ if (sessionname) { psettings_tmp = open_settings_r(sessionname); if (!psettings_tmp) { sfree(app_path); return NULL; } close_settings_r(psettings_tmp); } /* Create the new item. */ if (!SUCCEEDED(CoCreateInstance(&CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, COMPTR(IShellLink, &ret)))) { sfree(app_path); return NULL; } /* Set path, parameters, icon and description. */ ret->lpVtbl->SetPath(ret, app_path); if (sessionname) { param_string = dupcat("@", sessionname, NULL); } else { |
︙ | ︙ |
Changes to windows/winmisc.c.
︙ | ︙ | |||
10 11 12 13 14 15 16 | OSVERSIONINFO osVersion; char *platform_get_x_display(void) { /* We may as well check for DISPLAY in case it's useful. */ return dupstr(getenv("DISPLAY")); } | | | < | > > > > > | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 | OSVERSIONINFO osVersion; char *platform_get_x_display(void) { /* We may as well check for DISPLAY in case it's useful. */ return dupstr(getenv("DISPLAY")); } Filename *filename_from_str(const char *str) { Filename *ret = snew(Filename); ret->path = dupstr(str); return ret; } Filename *filename_copy(const Filename *fn) { return filename_from_str(fn->path); } const char *filename_to_str(const Filename *fn) { return fn->path; } int filename_equal(const Filename *f1, const Filename *f2) { return !strcmp(f1->path, f2->path); } int filename_is_null(const Filename *fn) { return !*fn->path; } void filename_free(Filename *fn) { sfree(fn->path); sfree(fn); } int filename_serialise(const Filename *f, void *vdata) { char *data = (char *)vdata; int len = strlen(f->path) + 1; /* include trailing NUL */ if (data) { strcpy(data, f->path); } return len; } Filename *filename_deserialise(void *vdata, int maxsize, int *used) { char *data = (char *)vdata; char *end; end = memchr(data, '\0', maxsize); if (!end) return NULL; end++; *used = end - data; return filename_from_str(data); } #ifndef NO_SECUREZEROMEMORY /* * Windows implementation of smemclr (see misc.c) using SecureZeroMemory. */ void smemclr(void *b, size_t n) { if (b && n > 0) SecureZeroMemory(b, n); } #endif char *get_username(void) { DWORD namelen; char *user; int got_username = FALSE; DECL_WINDOWS_FUNCTION(static, BOOLEAN, GetUserNameExA, |
︙ | ︙ | |||
127 128 129 130 131 132 133 134 135 136 137 138 139 140 | } fullpath = dupcat(sysdir, "\\", libname, NULL); ret = LoadLibrary(fullpath); sfree(fullpath); return ret; } #ifdef DEBUG static FILE *debug_fp = NULL; static HANDLE debug_hdl = INVALID_HANDLE_VALUE; static int debug_got_console = 0; void dputs(char *buf) | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 | } fullpath = dupcat(sysdir, "\\", libname, NULL); ret = LoadLibrary(fullpath); sfree(fullpath); return ret; } /* * A tree234 containing mappings from system error codes to strings. */ struct errstring { int error; char *text; }; static int errstring_find(void *av, void *bv) { int *a = (int *)av; struct errstring *b = (struct errstring *)bv; if (*a < b->error) return -1; if (*a > b->error) return +1; return 0; } static int errstring_compare(void *av, void *bv) { struct errstring *a = (struct errstring *)av; return errstring_find(&a->error, bv); } static tree234 *errstrings = NULL; const char *win_strerror(int error) { struct errstring *es; if (!errstrings) errstrings = newtree234(errstring_compare); es = find234(errstrings, &error, errstring_find); if (!es) { int bufsize; char msgtext[65536]; /* maximum size for FormatMessage is 64K */ es = snew(struct errstring); es->error = error; if (!FormatMessage((FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS), NULL, error, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), msgtext, lenof(msgtext)-1, NULL)) { sprintf(msgtext, "(unable to format: FormatMessage returned %d)", error, GetLastError()); } else { int len = strlen(msgtext); if (len > 0 && msgtext[len-1] == '\n') msgtext[len-1] = '\0'; } es->text = dupprintf("Error %d: %s", error, msgtext); add234(errstrings, es); } return es->text; } #ifdef DEBUG static FILE *debug_fp = NULL; static HANDLE debug_hdl = INVALID_HANDLE_VALUE; static int debug_got_console = 0; void dputs(char *buf) |
︙ | ︙ | |||
375 376 377 378 379 380 381 | oldsize = minefield_get_size(p); memcpy(q, p, (oldsize < size ? oldsize : size)); minefield_free(p); return q; } #endif /* MINEFIELD */ | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 | oldsize = minefield_get_size(p); memcpy(q, p, (oldsize < size ? oldsize : size)); minefield_free(p); return q; } #endif /* MINEFIELD */ FontSpec *fontspec_new(const char *name, int bold, int height, int charset) { FontSpec *f = snew(FontSpec); f->name = dupstr(name); f->isbold = bold; f->height = height; f->charset = charset; return f; } FontSpec *fontspec_copy(const FontSpec *f) { return fontspec_new(f->name, f->isbold, f->height, f->charset); } void fontspec_free(FontSpec *f) { sfree(f->name); sfree(f); } int fontspec_serialise(FontSpec *f, void *vdata) { char *data = (char *)vdata; int len = strlen(f->name) + 1; /* include trailing NUL */ if (data) { strcpy(data, f->name); PUT_32BIT_MSB_FIRST(data + len, f->isbold); PUT_32BIT_MSB_FIRST(data + len + 4, f->height); PUT_32BIT_MSB_FIRST(data + len + 8, f->charset); } return len + 12; /* also include three 4-byte ints */ } FontSpec *fontspec_deserialise(void *vdata, int maxsize, int *used) { char *data = (char *)vdata; char *end; if (maxsize < 13) return NULL; end = memchr(data, '\0', maxsize-12); if (!end) return NULL; end++; *used = end - data + 12; return fontspec_new(data, GET_32BIT_MSB_FIRST(end), GET_32BIT_MSB_FIRST(end + 4), GET_32BIT_MSB_FIRST(end + 8)); } |
Changes to windows/winnet.c.
︙ | ︙ | |||
49 50 51 52 53 54 55 | struct Socket_tag { const struct socket_function_table *fn; /* the above variable absolutely *must* be the first in this structure */ char *error; SOCKET s; Plug plug; | < > > > | 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 | struct Socket_tag { const struct socket_function_table *fn; /* the above variable absolutely *must* be the first in this structure */ char *error; SOCKET s; Plug plug; bufchain output_data; int connected; int writable; int frozen; /* this causes readability notifications to be ignored */ int frozen_readable; /* this means we missed at least one readability * notification while we were frozen */ int localhost_only; /* for listening sockets */ char oobdata[1]; int sending_oob; int oobinline, nodelay, keepalive, privport; enum { EOF_NO, EOF_PENDING, EOF_SENT } outgoingeof; SockAddr addr; SockAddrStep step; int port; int pending_error; /* in case send() returns error */ /* * We sometimes need pairs of Socket structures to be linked: * if we are listening on the same IPv6 and v4 port, for * example. So here we define `parent' and `child' pointers to * track this link. */ Actual_Socket parent, child; }; struct SockAddr_tag { int refcount; char *error; int resolved; int namedpipe; /* indicates that this SockAddr is phony, holding a Windows * named pipe pathname instead of a network address */ #ifndef NO_IPV6 struct addrinfo *ais; /* Addresses IPv6 style. */ #endif unsigned long *addresses; /* Addresses IPv4 style. */ int naddresses; char hostname[512]; /* Store an unresolved host name. */ }; |
︙ | ︙ | |||
163 164 165 166 167 168 169 170 171 172 173 174 175 176 | DECL_WINDOWS_FUNCTION(static, int, bind, (SOCKET, const struct sockaddr FAR *, int)); DECL_WINDOWS_FUNCTION(static, int, setsockopt, (SOCKET, int, int, const char FAR *, int)); DECL_WINDOWS_FUNCTION(static, SOCKET, socket, (int, int, int)); DECL_WINDOWS_FUNCTION(static, int, listen, (SOCKET, int)); DECL_WINDOWS_FUNCTION(static, int, send, (SOCKET, const char FAR *, int, int)); DECL_WINDOWS_FUNCTION(static, int, ioctlsocket, (SOCKET, long, u_long FAR *)); DECL_WINDOWS_FUNCTION(static, SOCKET, accept, (SOCKET, struct sockaddr FAR *, int FAR *)); DECL_WINDOWS_FUNCTION(static, int, recv, (SOCKET, char FAR *, int, int)); DECL_WINDOWS_FUNCTION(static, int, WSAIoctl, (SOCKET, DWORD, LPVOID, DWORD, LPVOID, DWORD, | > | 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 | DECL_WINDOWS_FUNCTION(static, int, bind, (SOCKET, const struct sockaddr FAR *, int)); DECL_WINDOWS_FUNCTION(static, int, setsockopt, (SOCKET, int, int, const char FAR *, int)); DECL_WINDOWS_FUNCTION(static, SOCKET, socket, (int, int, int)); DECL_WINDOWS_FUNCTION(static, int, listen, (SOCKET, int)); DECL_WINDOWS_FUNCTION(static, int, send, (SOCKET, const char FAR *, int, int)); DECL_WINDOWS_FUNCTION(static, int, shutdown, (SOCKET, int)); DECL_WINDOWS_FUNCTION(static, int, ioctlsocket, (SOCKET, long, u_long FAR *)); DECL_WINDOWS_FUNCTION(static, SOCKET, accept, (SOCKET, struct sockaddr FAR *, int FAR *)); DECL_WINDOWS_FUNCTION(static, int, recv, (SOCKET, char FAR *, int, int)); DECL_WINDOWS_FUNCTION(static, int, WSAIoctl, (SOCKET, DWORD, LPVOID, DWORD, LPVOID, DWORD, |
︙ | ︙ | |||
287 288 289 290 291 292 293 294 295 296 297 298 299 300 | GET_WINDOWS_FUNCTION(winsock_module, inet_ntoa); GET_WINDOWS_FUNCTION(winsock_module, connect); GET_WINDOWS_FUNCTION(winsock_module, bind); GET_WINDOWS_FUNCTION(winsock_module, setsockopt); GET_WINDOWS_FUNCTION(winsock_module, socket); GET_WINDOWS_FUNCTION(winsock_module, listen); GET_WINDOWS_FUNCTION(winsock_module, send); GET_WINDOWS_FUNCTION(winsock_module, ioctlsocket); GET_WINDOWS_FUNCTION(winsock_module, accept); GET_WINDOWS_FUNCTION(winsock_module, recv); GET_WINDOWS_FUNCTION(winsock_module, WSAIoctl); /* Try to get the best WinSock version we can get */ if (!sk_startup(2,2) && | > | 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 | GET_WINDOWS_FUNCTION(winsock_module, inet_ntoa); GET_WINDOWS_FUNCTION(winsock_module, connect); GET_WINDOWS_FUNCTION(winsock_module, bind); GET_WINDOWS_FUNCTION(winsock_module, setsockopt); GET_WINDOWS_FUNCTION(winsock_module, socket); GET_WINDOWS_FUNCTION(winsock_module, listen); GET_WINDOWS_FUNCTION(winsock_module, send); GET_WINDOWS_FUNCTION(winsock_module, shutdown); GET_WINDOWS_FUNCTION(winsock_module, ioctlsocket); GET_WINDOWS_FUNCTION(winsock_module, accept); GET_WINDOWS_FUNCTION(winsock_module, recv); GET_WINDOWS_FUNCTION(winsock_module, WSAIoctl); /* Try to get the best WinSock version we can get */ if (!sk_startup(2,2) && |
︙ | ︙ | |||
325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 | FreeLibrary(winsock_module); #ifndef NO_IPV6 if (wship6_module) FreeLibrary(wship6_module); #endif } char *winsock_error_string(int error) { switch (error) { case WSAEACCES: return "Network error: Permission denied"; case WSAEADDRINUSE: return "Network error: Address already in use"; case WSAEADDRNOTAVAIL: return "Network error: Cannot assign requested address"; | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 | FreeLibrary(winsock_module); #ifndef NO_IPV6 if (wship6_module) FreeLibrary(wship6_module); #endif } struct errstring { int error; char *text; }; static int errstring_find(void *av, void *bv) { int *a = (int *)av; struct errstring *b = (struct errstring *)bv; if (*a < b->error) return -1; if (*a > b->error) return +1; return 0; } static int errstring_compare(void *av, void *bv) { struct errstring *a = (struct errstring *)av; return errstring_find(&a->error, bv); } static tree234 *errstrings = NULL; char *winsock_error_string(int error) { const char prefix[] = "Network error: "; struct errstring *es; /* * Error codes we know about and have historically had reasonably * sensible error messages for. */ switch (error) { case WSAEACCES: return "Network error: Permission denied"; case WSAEADDRINUSE: return "Network error: Address already in use"; case WSAEADDRNOTAVAIL: return "Network error: Cannot assign requested address"; |
︙ | ︙ | |||
399 400 401 402 403 404 405 | return "Network error: Socket type not supported"; case WSAETIMEDOUT: return "Network error: Connection timed out"; case WSAEWOULDBLOCK: return "Network error: Resource temporarily unavailable"; case WSAEDISCON: return "Network error: Graceful shutdown in progress"; | > | > > > > > > > > > > > > | > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 | return "Network error: Socket type not supported"; case WSAETIMEDOUT: return "Network error: Connection timed out"; case WSAEWOULDBLOCK: return "Network error: Resource temporarily unavailable"; case WSAEDISCON: return "Network error: Graceful shutdown in progress"; } /* * Generic code to handle any other error. * * Slightly nasty hack here: we want to return a static string * which the caller will never have to worry about freeing, but on * the other hand if we call FormatMessage to get it then it will * want to either allocate a buffer or write into one we own. * * So what we do is to maintain a tree234 of error strings we've * already used. New ones are allocated from the heap, but then * put in this tree and kept forever. */ if (!errstrings) errstrings = newtree234(errstring_compare); es = find234(errstrings, &error, errstring_find); if (!es) { int bufsize, bufused; es = snew(struct errstring); es->error = error; /* maximum size for FormatMessage is 64K */ bufsize = 65535 + sizeof(prefix); es->text = snewn(bufsize, char); strcpy(es->text, prefix); bufused = strlen(es->text); if (!FormatMessage((FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS), NULL, error, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), es->text + bufused, bufsize - bufused, NULL)) { sprintf(es->text + bufused, "Windows error code %d (and FormatMessage returned %d)", error, GetLastError()); } else { int len = strlen(es->text); if (len > 0 && es->text[len-1] == '\n') es->text[len-1] = '\0'; } es->text = sresize(es->text, strlen(es->text) + 1, char); add234(errstrings, es); } return es->text; } SockAddr sk_namelookup(const char *host, char **canonicalname, int address_family) { SockAddr ret = snew(struct SockAddr_tag); unsigned long a; |
︙ | ︙ | |||
424 425 426 427 428 429 430 431 432 433 434 435 436 437 | AF_UNSPEC); /* Clear the structure and default to IPv4. */ memset(ret, 0, sizeof(struct SockAddr_tag)); #ifndef NO_IPV6 ret->ais = NULL; #endif ret->addresses = NULL; ret->resolved = FALSE; ret->refcount = 1; *realhost = '\0'; if ((a = p_inet_addr(host)) == (unsigned long) INADDR_NONE) { struct hostent *h = NULL; | > | 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 | AF_UNSPEC); /* Clear the structure and default to IPv4. */ memset(ret, 0, sizeof(struct SockAddr_tag)); #ifndef NO_IPV6 ret->ais = NULL; #endif ret->namedpipe = FALSE; ret->addresses = NULL; ret->resolved = FALSE; ret->refcount = 1; *realhost = '\0'; if ((a = p_inet_addr(host)) == (unsigned long) INADDR_NONE) { struct hostent *h = NULL; |
︙ | ︙ | |||
530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 | { SockAddr ret = snew(struct SockAddr_tag); ret->error = NULL; ret->resolved = FALSE; #ifndef NO_IPV6 ret->ais = NULL; #endif ret->addresses = NULL; ret->naddresses = 0; ret->refcount = 1; strncpy(ret->hostname, host, lenof(ret->hostname)); ret->hostname[lenof(ret->hostname)-1] = '\0'; return ret; } int sk_nextaddr(SockAddr addr, SockAddrStep *step) { #ifndef NO_IPV6 if (step->ai) { if (step->ai->ai_next) { step->ai = step->ai->ai_next; | > > > > > > > > > > > > > > > > > > | 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 | { SockAddr ret = snew(struct SockAddr_tag); ret->error = NULL; ret->resolved = FALSE; #ifndef NO_IPV6 ret->ais = NULL; #endif ret->namedpipe = FALSE; ret->addresses = NULL; ret->naddresses = 0; ret->refcount = 1; strncpy(ret->hostname, host, lenof(ret->hostname)); ret->hostname[lenof(ret->hostname)-1] = '\0'; return ret; } SockAddr sk_namedpipe_addr(const char *pipename) { SockAddr ret = snew(struct SockAddr_tag); ret->error = NULL; ret->resolved = FALSE; #ifndef NO_IPV6 ret->ais = NULL; #endif ret->namedpipe = TRUE; ret->addresses = NULL; ret->naddresses = 0; ret->refcount = 1; strncpy(ret->hostname, pipename, lenof(ret->hostname)); ret->hostname[lenof(ret->hostname)-1] = '\0'; return ret; } int sk_nextaddr(SockAddr addr, SockAddrStep *step) { #ifndef NO_IPV6 if (step->ai) { if (step->ai->ai_next) { step->ai = step->ai->ai_next; |
︙ | ︙ | |||
591 592 593 594 595 596 597 | buf[buflen-1] = '\0'; } else { strncpy(buf, addr->hostname, buflen); buf[buflen-1] = '\0'; } } | > > > > > | | 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 | buf[buflen-1] = '\0'; } else { strncpy(buf, addr->hostname, buflen); buf[buflen-1] = '\0'; } } int sk_addr_needs_port(SockAddr addr) { return addr->namedpipe ? FALSE : TRUE; } int sk_hostname_is_local(const char *name) { return !strcmp(name, "localhost") || !strcmp(name, "::1") || !strncmp(name, "127.", 4); } static INTERFACE_INFO local_interfaces[16]; |
︙ | ︙ | |||
638 639 640 641 642 643 644 | SockAddrStep step; int family; START_STEP(addr, step); family = SOCKADDR_FAMILY(addr, step); #ifndef NO_IPV6 if (family == AF_INET6) { | | > > > > > | 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 | SockAddrStep step; int family; START_STEP(addr, step); family = SOCKADDR_FAMILY(addr, step); #ifndef NO_IPV6 if (family == AF_INET6) { return IN6_IS_ADDR_LOOPBACK(&((const struct sockaddr_in6 *)step.ai->ai_addr)->sin6_addr); } else #endif if (family == AF_INET) { #ifndef NO_IPV6 if (step.ai) { return ipv4_is_local_addr(((struct sockaddr_in *)step.ai->ai_addr) ->sin_addr); } else #endif { struct in_addr a; assert(addr->addresses && step.curraddr < addr->naddresses); a.s_addr = p_htonl(addr->addresses[step.curraddr]); return ipv4_is_local_addr(a); } } else { assert(family == AF_UNSPEC); return 0; /* we don't know; assume not */ } } int sk_address_is_special_local(SockAddr addr) { return 0; /* no Unix-domain socket analogue here */ } int sk_addrtype(SockAddr addr) { SockAddrStep step; int family; START_STEP(addr, step); family = SOCKADDR_FAMILY(addr, step); |
︙ | ︙ | |||
741 742 743 744 745 746 747 | * so we don't need to do anything here. :-) */ } static void sk_tcp_close(Socket s); static int sk_tcp_write(Socket s, const char *data, int len); static int sk_tcp_write_oob(Socket s, const char *data, int len); | | < | | | < > | | 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 | * so we don't need to do anything here. :-) */ } static void sk_tcp_close(Socket s); static int sk_tcp_write(Socket s, const char *data, int len); static int sk_tcp_write_oob(Socket s, const char *data, int len); static void sk_tcp_write_eof(Socket s); static void sk_tcp_set_frozen(Socket s, int is_frozen); static const char *sk_tcp_socket_error(Socket s); extern char *do_select(SOCKET skt, int startup); static Socket sk_tcp_accept(accept_ctx_t ctx, Plug plug) { static const struct socket_function_table fn_table = { sk_tcp_plug, sk_tcp_close, sk_tcp_write, sk_tcp_write_oob, sk_tcp_write_eof, sk_tcp_flush, sk_tcp_set_frozen, sk_tcp_socket_error }; DWORD err; char *errstr; Actual_Socket ret; /* * Create Socket structure. */ ret = snew(struct Socket_tag); ret->fn = &fn_table; ret->error = NULL; ret->plug = plug; bufchain_init(&ret->output_data); ret->writable = 1; /* to start with */ ret->sending_oob = 0; ret->outgoingeof = EOF_NO; ret->frozen = 1; ret->frozen_readable = 0; ret->localhost_only = 0; /* unused, but best init anyway */ ret->pending_error = 0; ret->parent = ret->child = NULL; ret->addr = NULL; ret->s = (SOCKET)ctx.p; if (ret->s == INVALID_SOCKET) { err = p_WSAGetLastError(); ret->error = winsock_error_string(err); return (Socket) ret; } |
︙ | ︙ | |||
1003 1004 1005 1006 1007 1008 1009 | int nodelay, int keepalive, Plug plug) { static const struct socket_function_table fn_table = { sk_tcp_plug, sk_tcp_close, sk_tcp_write, sk_tcp_write_oob, | | | < > | 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 | int nodelay, int keepalive, Plug plug) { static const struct socket_function_table fn_table = { sk_tcp_plug, sk_tcp_close, sk_tcp_write, sk_tcp_write_oob, sk_tcp_write_eof, sk_tcp_flush, sk_tcp_set_frozen, sk_tcp_socket_error }; Actual_Socket ret; DWORD err; /* * Create Socket structure. */ ret = snew(struct Socket_tag); ret->fn = &fn_table; ret->error = NULL; ret->plug = plug; bufchain_init(&ret->output_data); ret->connected = 0; /* to start with */ ret->writable = 0; /* to start with */ ret->sending_oob = 0; ret->outgoingeof = EOF_NO; ret->frozen = 0; ret->frozen_readable = 0; ret->localhost_only = 0; /* unused, but best init anyway */ ret->pending_error = 0; ret->parent = ret->child = NULL; ret->oobinline = oobinline; ret->nodelay = nodelay; |
︙ | ︙ | |||
1054 1055 1056 1057 1058 1059 1060 | int orig_address_family) { static const struct socket_function_table fn_table = { sk_tcp_plug, sk_tcp_close, sk_tcp_write, sk_tcp_write_oob, | | | < | 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 | int orig_address_family) { static const struct socket_function_table fn_table = { sk_tcp_plug, sk_tcp_close, sk_tcp_write, sk_tcp_write_oob, sk_tcp_write_eof, sk_tcp_flush, sk_tcp_set_frozen, sk_tcp_socket_error }; SOCKET s; #ifndef NO_IPV6 SOCKADDR_IN6 a6; |
︙ | ︙ | |||
1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 | ret = snew(struct Socket_tag); ret->fn = &fn_table; ret->error = NULL; ret->plug = plug; bufchain_init(&ret->output_data); ret->writable = 0; /* to start with */ ret->sending_oob = 0; ret->frozen = 0; ret->frozen_readable = 0; ret->localhost_only = local_host_only; ret->pending_error = 0; ret->parent = ret->child = NULL; ret->addr = NULL; | > | 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 | ret = snew(struct Socket_tag); ret->fn = &fn_table; ret->error = NULL; ret->plug = plug; bufchain_init(&ret->output_data); ret->writable = 0; /* to start with */ ret->sending_oob = 0; ret->outgoingeof = EOF_NO; ret->frozen = 0; ret->frozen_readable = 0; ret->localhost_only = local_host_only; ret->pending_error = 0; ret->parent = ret->child = NULL; ret->addr = NULL; |
︙ | ︙ | |||
1196 1197 1198 1199 1200 1201 1202 | ret->error = winsock_error_string(err); return (Socket) ret; } if (p_listen(s, SOMAXCONN) == SOCKET_ERROR) { p_closesocket(s); | | | 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 | ret->error = winsock_error_string(err); return (Socket) ret; } if (p_listen(s, SOMAXCONN) == SOCKET_ERROR) { p_closesocket(s); ret->error = winsock_error_string(p_WSAGetLastError()); return (Socket) ret; } /* Set up a select mechanism. This could be an AsyncSelect on a * window, or an EventSelect on an event object. */ errstr = do_select(s, 1); if (errstr) { |
︙ | ︙ | |||
1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 | del234(sktree, s); do_select(s->s, 0); p_closesocket(s->s); if (s->addr) sk_addr_free(s->addr); sfree(s); } /* * The function which tries to send on a socket once it's deemed * writable. */ void try_send(Actual_Socket s) { | > > > > > > > > > > > > > > > > > > > > > | 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 | del234(sktree, s); do_select(s->s, 0); p_closesocket(s->s); if (s->addr) sk_addr_free(s->addr); sfree(s); } /* * Deal with socket errors detected in try_send(). */ static void socket_error_callback(void *vs) { Actual_Socket s = (Actual_Socket)vs; /* * Just in case other socket work has caused this socket to vanish * or become somehow non-erroneous before this callback arrived... */ if (!find234(sktree, s, NULL) || !s->pending_error) return; /* * An error has occurred on this socket. Pass it to the plug. */ plug_closing(s->plug, winsock_error_string(s->pending_error), s->pending_error, 0); } /* * The function which tries to send on a socket once it's deemed * writable. */ void try_send(Actual_Socket s) { |
︙ | ︙ | |||
1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 | * _in_ a call from the code we'd be calling back * to, so we'd have to make half the SSH code * reentrant. Instead we flag a pending error on * the socket, to be dealt with (by calling * plug_closing()) at some suitable future moment. */ s->pending_error = err; return; } else { /* We're inside the Windows frontend here, so we know * that the frontend handle is unnecessary. */ logevent(NULL, winsock_error_string(err)); fatalbox("%s", winsock_error_string(err)); } } else { if (s->sending_oob) { if (nsent < len) { memmove(s->oobdata, s->oobdata+nsent, len-nsent); s->sending_oob = len - nsent; } else { s->sending_oob = 0; } } else { bufchain_consume(&s->output_data, nsent); } } } } static int sk_tcp_write(Socket sock, const char *buf, int len) { Actual_Socket s = (Actual_Socket) sock; /* * Add the data to the buffer list on the socket. */ bufchain_add(&s->output_data, buf, len); /* * Now try sending from the start of the buffer list. */ if (s->writable) try_send(s); return bufchain_size(&s->output_data); } static int sk_tcp_write_oob(Socket sock, const char *buf, int len) { Actual_Socket s = (Actual_Socket) sock; /* * Replace the buffer list on the socket with the data. */ bufchain_clear(&s->output_data); assert(len <= sizeof(s->oobdata)); memcpy(s->oobdata, buf, len); s->sending_oob = len; /* * Now try sending from the start of the buffer list. */ if (s->writable) try_send(s); return s->sending_oob; } int select_result(WPARAM wParam, LPARAM lParam) { int ret, open; DWORD err; char buf[20480]; /* nice big buffer for plenty of speed */ Actual_Socket s; | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 | * _in_ a call from the code we'd be calling back * to, so we'd have to make half the SSH code * reentrant. Instead we flag a pending error on * the socket, to be dealt with (by calling * plug_closing()) at some suitable future moment. */ s->pending_error = err; queue_toplevel_callback(socket_error_callback, s); return; } else { /* We're inside the Windows frontend here, so we know * that the frontend handle is unnecessary. */ logevent(NULL, winsock_error_string(err)); fatalbox("%s", winsock_error_string(err)); } } else { if (s->sending_oob) { if (nsent < len) { memmove(s->oobdata, s->oobdata+nsent, len-nsent); s->sending_oob = len - nsent; } else { s->sending_oob = 0; } } else { bufchain_consume(&s->output_data, nsent); } } } /* * If we reach here, we've finished sending everything we might * have needed to send. Send EOF, if we need to. */ if (s->outgoingeof == EOF_PENDING) { p_shutdown(s->s, SD_SEND); s->outgoingeof = EOF_SENT; } } static int sk_tcp_write(Socket sock, const char *buf, int len) { Actual_Socket s = (Actual_Socket) sock; assert(s->outgoingeof == EOF_NO); /* * Add the data to the buffer list on the socket. */ bufchain_add(&s->output_data, buf, len); /* * Now try sending from the start of the buffer list. */ if (s->writable) try_send(s); return bufchain_size(&s->output_data); } static int sk_tcp_write_oob(Socket sock, const char *buf, int len) { Actual_Socket s = (Actual_Socket) sock; assert(s->outgoingeof == EOF_NO); /* * Replace the buffer list on the socket with the data. */ bufchain_clear(&s->output_data); assert(len <= sizeof(s->oobdata)); memcpy(s->oobdata, buf, len); s->sending_oob = len; /* * Now try sending from the start of the buffer list. */ if (s->writable) try_send(s); return s->sending_oob; } static void sk_tcp_write_eof(Socket sock) { Actual_Socket s = (Actual_Socket) sock; assert(s->outgoingeof == EOF_NO); /* * Mark the socket as pending outgoing EOF. */ s->outgoingeof = EOF_PENDING; /* * Now try sending from the start of the buffer list. */ if (s->writable) try_send(s); } int select_result(WPARAM wParam, LPARAM lParam) { int ret, open; DWORD err; char buf[20480]; /* nice big buffer for plenty of speed */ Actual_Socket s; |
︙ | ︙ | |||
1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 | #ifdef NO_IPV6 struct sockaddr_in isa; #else struct sockaddr_storage isa; #endif int addrlen = sizeof(isa); SOCKET t; /* socket of connection */ memset(&isa, 0, sizeof(isa)); err = 0; t = p_accept(s->s,(struct sockaddr *)&isa,&addrlen); if (t == INVALID_SOCKET) { err = p_WSAGetLastError(); if (err == WSATRY_AGAIN) break; } #ifndef NO_IPV6 if (isa.ss_family == AF_INET && s->localhost_only && !ipv4_is_local_addr(((struct sockaddr_in *)&isa)->sin_addr)) #else if (s->localhost_only && !ipv4_is_local_addr(isa.sin_addr)) #endif { p_closesocket(t); /* dodgy WinSock let nonlocal through */ | > > > > | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 | #ifdef NO_IPV6 struct sockaddr_in isa; #else struct sockaddr_storage isa; #endif int addrlen = sizeof(isa); SOCKET t; /* socket of connection */ accept_ctx_t actx; memset(&isa, 0, sizeof(isa)); err = 0; t = p_accept(s->s,(struct sockaddr *)&isa,&addrlen); if (t == INVALID_SOCKET) { err = p_WSAGetLastError(); if (err == WSATRY_AGAIN) break; } actx.p = (void *)t; #ifndef NO_IPV6 if (isa.ss_family == AF_INET && s->localhost_only && !ipv4_is_local_addr(((struct sockaddr_in *)&isa)->sin_addr)) #else if (s->localhost_only && !ipv4_is_local_addr(isa.sin_addr)) #endif { p_closesocket(t); /* dodgy WinSock let nonlocal through */ } else if (plug_accepting(s->plug, sk_tcp_accept, actx)) { p_closesocket(t); /* denied or error */ } } } return 1; } /* * Special error values are returned from sk_namelookup and sk_new * if there's a problem. These functions extract an error message, * or return NULL if there's no problem. */ const char *sk_addr_error(SockAddr addr) { |
︙ | ︙ |
Changes to windows/winnoise.c.
1 2 3 4 5 6 7 8 9 10 11 12 | /* * Noise generation for PuTTY's cryptographic random number * generator. */ #include <stdio.h> #include "putty.h" #include "ssh.h" #include "storage.h" /* | > > > > > > > > > > | < < > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 | /* * Noise generation for PuTTY's cryptographic random number * generator. */ #include <stdio.h> #include "putty.h" #include "ssh.h" #include "storage.h" #include <wincrypt.h> DECL_WINDOWS_FUNCTION(static, BOOL, CryptAcquireContextA, (HCRYPTPROV *, LPCTSTR, LPCTSTR, DWORD, DWORD)); DECL_WINDOWS_FUNCTION(static, BOOL, CryptGenRandom, (HCRYPTPROV, DWORD, BYTE *)); DECL_WINDOWS_FUNCTION(static, BOOL, CryptReleaseContext, (HCRYPTPROV, DWORD)); static HMODULE wincrypt_module = NULL; /* * This function is called once, at PuTTY startup. */ void noise_get_heavy(void (*func) (void *, int)) { HANDLE srch; WIN32_FIND_DATA finddata; DWORD pid; HCRYPTPROV crypt_provider; char winpath[MAX_PATH + 3]; GetWindowsDirectory(winpath, sizeof(winpath)); strcat(winpath, "\\*"); srch = FindFirstFile(winpath, &finddata); if (srch != INVALID_HANDLE_VALUE) { do { func(&finddata, sizeof(finddata)); } while (FindNextFile(srch, &finddata)); FindClose(srch); } pid = GetCurrentProcessId(); func(&pid, sizeof(pid)); if (!wincrypt_module) { wincrypt_module = load_system32_dll("advapi32.dll"); GET_WINDOWS_FUNCTION(wincrypt_module, CryptAcquireContextA); GET_WINDOWS_FUNCTION(wincrypt_module, CryptGenRandom); GET_WINDOWS_FUNCTION(wincrypt_module, CryptReleaseContext); } if (wincrypt_module && p_CryptAcquireContextA && p_CryptGenRandom && p_CryptReleaseContext && p_CryptAcquireContextA(&crypt_provider, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) { BYTE buf[32]; if (p_CryptGenRandom(crypt_provider, 32, buf)) { func(buf, sizeof(buf)); } p_CryptReleaseContext(crypt_provider, 0); } read_random_seed(func); /* Update the seed immediately, in case another instance uses it. */ random_save_seed(); } void random_save_seed(void) |
︙ | ︙ |
Added windows/winnpc.c.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 | /* * Windows support module which deals with being a named-pipe client. */ #include <stdio.h> #include <assert.h> #define DEFINE_PLUG_METHOD_MACROS #include "tree234.h" #include "putty.h" #include "network.h" #include "proxy.h" #include "ssh.h" #if !defined NO_SECURITY #include "winsecur.h" Socket make_handle_socket(HANDLE send_H, HANDLE recv_H, Plug plug, int overlapped); Socket new_named_pipe_client(const char *pipename, Plug plug) { HANDLE pipehandle; PSID usersid, pipeowner; PSECURITY_DESCRIPTOR psd; char *err; Socket ret; assert(strncmp(pipename, "\\\\.\\pipe\\", 9) == 0); assert(strchr(pipename + 9, '\\') == NULL); while (1) { pipehandle = CreateFile(pipename, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); if (pipehandle != INVALID_HANDLE_VALUE) break; if (GetLastError() != ERROR_PIPE_BUSY) { err = dupprintf("Unable to open named pipe '%s': %s", pipename, win_strerror(GetLastError())); ret = new_error_socket(err, plug); sfree(err); return ret; } /* * If we got ERROR_PIPE_BUSY, wait for the server to * create a new pipe instance. (Since the server is * expected to be winnps.c, which will do that immediately * after a previous connection is accepted, that shouldn't * take excessively long.) */ if (!WaitNamedPipe(pipename, NMPWAIT_USE_DEFAULT_WAIT)) { err = dupprintf("Error waiting for named pipe '%s': %s", pipename, win_strerror(GetLastError())); ret = new_error_socket(err, plug); sfree(err); return ret; } } if ((usersid = get_user_sid()) == NULL) { CloseHandle(pipehandle); err = dupprintf("Unable to get user SID"); ret = new_error_socket(err, plug); sfree(err); return ret; } if (p_GetSecurityInfo(pipehandle, SE_KERNEL_OBJECT, OWNER_SECURITY_INFORMATION, &pipeowner, NULL, NULL, NULL, &psd) != ERROR_SUCCESS) { err = dupprintf("Unable to get named pipe security information: %s", win_strerror(GetLastError())); ret = new_error_socket(err, plug); sfree(err); CloseHandle(pipehandle); sfree(usersid); return ret; } if (!EqualSid(pipeowner, usersid)) { err = dupprintf("Owner of named pipe '%s' is not us", pipename); ret = new_error_socket(err, plug); sfree(err); CloseHandle(pipehandle); LocalFree(psd); sfree(usersid); return ret; } LocalFree(psd); sfree(usersid); return make_handle_socket(pipehandle, pipehandle, plug, TRUE); } #endif /* !defined NO_SECURITY */ |
Added windows/winnps.c.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 | /* * Windows support module which deals with being a named-pipe server. */ #include <stdio.h> #include <assert.h> #define DEFINE_PLUG_METHOD_MACROS #include "tree234.h" #include "putty.h" #include "network.h" #include "proxy.h" #include "ssh.h" #if !defined NO_SECURITY #include "winsecur.h" Socket make_handle_socket(HANDLE send_H, HANDLE recv_H, Plug plug, int overlapped); typedef struct Socket_named_pipe_server_tag *Named_Pipe_Server_Socket; struct Socket_named_pipe_server_tag { const struct socket_function_table *fn; /* the above variable absolutely *must* be the first in this structure */ /* Parameters for (repeated) creation of named pipe objects */ PSECURITY_DESCRIPTOR psd; PSID networksid; PACL acl; char *pipename; /* The current named pipe object + attempt to connect to it */ HANDLE pipehandle; OVERLAPPED connect_ovl; /* PuTTY Socket machinery */ Plug plug; char *error; }; static Plug sk_namedpipeserver_plug(Socket s, Plug p) { Named_Pipe_Server_Socket ps = (Named_Pipe_Server_Socket) s; Plug ret = ps->plug; if (p) ps->plug = p; return ret; } static void sk_namedpipeserver_close(Socket s) { Named_Pipe_Server_Socket ps = (Named_Pipe_Server_Socket) s; CloseHandle(ps->pipehandle); CloseHandle(ps->connect_ovl.hEvent); sfree(ps->error); sfree(ps->pipename); if (ps->networksid) LocalFree(ps->networksid); if (ps->acl) LocalFree(ps->acl); if (ps->psd) LocalFree(ps->psd); sfree(ps); } static const char *sk_namedpipeserver_socket_error(Socket s) { Named_Pipe_Server_Socket ps = (Named_Pipe_Server_Socket) s; return ps->error; } static int create_named_pipe(Named_Pipe_Server_Socket ps, int first_instance) { SECURITY_ATTRIBUTES sa; memset(&sa, 0, sizeof(sa)); sa.nLength = sizeof(sa); sa.lpSecurityDescriptor = ps->psd; sa.bInheritHandle = FALSE; ps->pipehandle = CreateNamedPipe (/* lpName */ ps->pipename, /* dwOpenMode */ PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED | (first_instance ? FILE_FLAG_FIRST_PIPE_INSTANCE : 0), /* dwPipeMode */ PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT #ifdef PIPE_REJECT_REMOTE_CLIENTS | PIPE_REJECT_REMOTE_CLIENTS #endif , /* nMaxInstances */ PIPE_UNLIMITED_INSTANCES, /* nOutBufferSize, nInBufferSize */ 4096, 4096, /* FIXME: think harder about buffer sizes? */ /* nDefaultTimeOut */ 0 /* default timeout */, /* lpSecurityAttributes */ &sa); return ps->pipehandle != INVALID_HANDLE_VALUE; } static Socket named_pipe_accept(accept_ctx_t ctx, Plug plug) { HANDLE conn = (HANDLE)ctx.p; return make_handle_socket(conn, conn, plug, TRUE); } /* * Dummy SockAddr type which just holds a named pipe address. Only * used for calling plug_log from named_pipe_accept_loop() here. */ SockAddr sk_namedpipe_addr(const char *pipename); static void named_pipe_accept_loop(Named_Pipe_Server_Socket ps, int got_one_already) { while (1) { int error; char *errmsg; if (got_one_already) { /* If we were called with a connection already waiting, * skip this step. */ got_one_already = FALSE; error = 0; } else { /* * Call ConnectNamedPipe, which might succeed or might * tell us that an overlapped operation is in progress and * we should wait for our event object. */ if (ConnectNamedPipe(ps->pipehandle, &ps->connect_ovl)) error = 0; else error = GetLastError(); if (error == ERROR_IO_PENDING) return; } if (error == 0 || error == ERROR_PIPE_CONNECTED) { /* * We've successfully retrieved an incoming connection, so * ps->pipehandle now refers to that connection. So * convert that handle into a separate connection-type * Socket, and create a fresh one to be the new listening * pipe. */ HANDLE conn = ps->pipehandle; accept_ctx_t actx; actx.p = (void *)conn; if (plug_accepting(ps->plug, named_pipe_accept, actx)) { /* * If the plug didn't want the connection, might as * well close this handle. */ CloseHandle(conn); } if (!create_named_pipe(ps, FALSE)) { error = GetLastError(); } else { /* * Go round again to see if more connections can be * got, or to begin waiting on the event object. */ continue; } } errmsg = dupprintf("Error while listening to named pipe: %s", win_strerror(error)); plug_log(ps->plug, 1, sk_namedpipe_addr(ps->pipename), 0, errmsg, error); sfree(errmsg); break; } } static void named_pipe_connect_callback(void *vps) { Named_Pipe_Server_Socket ps = (Named_Pipe_Server_Socket)vps; named_pipe_accept_loop(ps, TRUE); } Socket new_named_pipe_listener(const char *pipename, Plug plug) { /* * This socket type is only used for listening, so it should never * be asked to write or flush or set_frozen. */ static const struct socket_function_table socket_fn_table = { sk_namedpipeserver_plug, sk_namedpipeserver_close, NULL /* write */, NULL /* write_oob */, NULL /* write_eof */, NULL /* flush */, NULL /* set_frozen */, sk_namedpipeserver_socket_error }; Named_Pipe_Server_Socket ret; ret = snew(struct Socket_named_pipe_server_tag); ret->fn = &socket_fn_table; ret->plug = plug; ret->error = NULL; ret->psd = NULL; ret->pipename = dupstr(pipename); ret->networksid = NULL; ret->acl = NULL; assert(strncmp(pipename, "\\\\.\\pipe\\", 9) == 0); assert(strchr(pipename + 9, '\\') == NULL); if (!make_private_security_descriptor(GENERIC_READ | GENERIC_WRITE, &ret->psd, &ret->networksid, &ret->acl, &ret->error)) { goto cleanup; } if (!create_named_pipe(ret, TRUE)) { ret->error = dupprintf("unable to create named pipe '%s': %s", pipename, win_strerror(GetLastError())); goto cleanup; } memset(&ret->connect_ovl, 0, sizeof(ret->connect_ovl)); ret->connect_ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); handle_add_foreign_event(ret->connect_ovl.hEvent, named_pipe_connect_callback, ret); named_pipe_accept_loop(ret, FALSE); cleanup: return (Socket) ret; } #endif /* !defined NO_SECURITY */ |
Changes to windows/winpgen.c.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | /* * PuTTY key generation front end (Windows). */ #include <time.h> #include <stdio.h> #include <stdlib.h> #define PUTTY_DO_GLOBALS #include "putty.h" #include "ssh.h" #include <commctrl.h> #ifdef MSVC4 #define ICON_BIG 1 #endif #define WM_DONEKEY (WM_APP + 1) | > | > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 | /* * PuTTY key generation front end (Windows). */ #include <time.h> #include <stdio.h> #include <stdlib.h> #include <assert.h> #define PUTTY_DO_GLOBALS #include "putty.h" #include "ssh.h" #include <commctrl.h> #ifdef MSVC4 #define ICON_BIG 1 #endif #define WM_DONEKEY (WM_APP + 1) #define DEFAULT_KEYSIZE 2048 static char *cmdline_keyfile = NULL; /* * Print a modal (Really Bad) message box and perform a fatal exit. */ void modalfatalbox(char *fmt, ...) { va_list ap; char *stuff; va_start(ap, fmt); stuff = dupvprintf(fmt, ap); va_end(ap); MessageBox(NULL, stuff, "PuTTYgen Fatal Error", MB_SYSTEMMODAL | MB_ICONERROR | MB_OK); sfree(stuff); exit(1); } /* * Print a non-fatal message box and do not exit. */ void nonfatal(char *fmt, ...) { va_list ap; char *stuff; va_start(ap, fmt); stuff = dupvprintf(fmt, ap); va_end(ap); MessageBox(NULL, stuff, "PuTTYgen Error", MB_SYSTEMMODAL | MB_ICONERROR | MB_OK); sfree(stuff); } /* ---------------------------------------------------------------------- * Progress report code. This is really horrible :-) */ #define PROGRESSRANGE 65535 #define MAXPHASE 5 struct progress { |
︙ | ︙ | |||
112 113 114 115 116 117 118 | SendMessage(p->progbar, PBM_SETPOS, position / p->divisor, 0); break; } } extern char ver[]; | < < | | | 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 | SendMessage(p->progbar, PBM_SETPOS, position / p->divisor, 0); break; } } extern char ver[]; struct PassphraseProcStruct { char **passphrase; char *comment; }; /* * Dialog-box function for the passphrase box. */ static int CALLBACK PassphraseProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { static char **passphrase = NULL; struct PassphraseProcStruct *p; switch (msg) { case WM_INITDIALOG: SetForegroundWindow(hwnd); SetWindowPos(hwnd, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW); |
︙ | ︙ | |||
153 154 155 156 157 158 159 | rd.right - rd.left, rd.bottom - rd.top, TRUE); } p = (struct PassphraseProcStruct *) lParam; passphrase = p->passphrase; if (p->comment) SetDlgItemText(hwnd, 101, p->comment); | > | | | < | | 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 | rd.right - rd.left, rd.bottom - rd.top, TRUE); } p = (struct PassphraseProcStruct *) lParam; passphrase = p->passphrase; if (p->comment) SetDlgItemText(hwnd, 101, p->comment); burnstr(*passphrase); *passphrase = dupstr(""); SetDlgItemText(hwnd, 102, *passphrase); return 0; case WM_COMMAND: switch (LOWORD(wParam)) { case IDOK: if (*passphrase) EndDialog(hwnd, 1); else MessageBeep(0); return 0; case IDCANCEL: EndDialog(hwnd, 0); return 0; case 102: /* edit box */ if ((HIWORD(wParam) == EN_CHANGE) && passphrase) { burnstr(*passphrase); *passphrase = GetDlgItemText_alloc(hwnd, 102); } return 0; } return 0; case WM_CLOSE: EndDialog(hwnd, 0); return 0; |
︙ | ︙ | |||
401 402 403 404 405 406 407 | } static int save_ssh1_pubkey(char *filename, struct RSAKey *key) { char *dec1, *dec2; FILE *fp; | < < > > | 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 | } static int save_ssh1_pubkey(char *filename, struct RSAKey *key) { char *dec1, *dec2; FILE *fp; fp = fopen(filename, "wb"); if (!fp) return 0; dec1 = bignum_decimal(key->exponent); dec2 = bignum_decimal(key->modulus); fprintf(fp, "%d %s %s %s\n", bignum_bitcount(key->modulus), dec1, dec2, key->comment); fclose(fp); sfree(dec1); sfree(dec2); return 1; } |
︙ | ︙ | |||
611 612 613 614 615 616 617 | do_export_menuitem(IDC_EXPORT_SSHCOM, SSH_KEYTYPE_SSHCOM); #undef do_export_menuitem break; } } void load_key_file(HWND hwnd, struct MainDlgState *state, | | | < | > | | < | | | | | > > > > | < | | | | < | < | 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 | do_export_menuitem(IDC_EXPORT_SSHCOM, SSH_KEYTYPE_SSHCOM); #undef do_export_menuitem break; } } void load_key_file(HWND hwnd, struct MainDlgState *state, Filename *filename, int was_import_cmd) { char *passphrase; int needs_pass; int type, realtype; int ret; const char *errmsg = NULL; char *comment; struct RSAKey newkey1; struct ssh2_userkey *newkey2 = NULL; type = realtype = key_type(filename); if (type != SSH_KEYTYPE_SSH1 && type != SSH_KEYTYPE_SSH2 && !import_possible(type)) { char *msg = dupprintf("Couldn't load private key (%s)", key_type_to_str(type)); message_box(msg, "PuTTYgen Error", MB_OK | MB_ICONERROR, HELPCTXID(errors_cantloadkey)); sfree(msg); return; } if (type != SSH_KEYTYPE_SSH1 && type != SSH_KEYTYPE_SSH2) { realtype = type; type = import_target_type(type); } comment = NULL; passphrase = NULL; if (realtype == SSH_KEYTYPE_SSH1) needs_pass = rsakey_encrypted(filename, &comment); else if (realtype == SSH_KEYTYPE_SSH2) needs_pass = ssh2_userkey_encrypted(filename, &comment); else needs_pass = import_encrypted(filename, realtype, &comment); do { burnstr(passphrase); passphrase = NULL; if (needs_pass) { int dlgret; struct PassphraseProcStruct pps; pps.passphrase = &passphrase; pps.comment = comment; dlgret = DialogBoxParam(hinst, MAKEINTRESOURCE(210), NULL, PassphraseProc, (LPARAM) &pps); if (!dlgret) { ret = -2; break; } assert(passphrase != NULL); } else passphrase = dupstr(""); if (type == SSH_KEYTYPE_SSH1) { if (realtype == type) ret = loadrsakey(filename, &newkey1, passphrase, &errmsg); else ret = import_ssh1(filename, realtype, &newkey1, passphrase, &errmsg); } else { if (realtype == type) newkey2 = ssh2_load_userkey(filename, passphrase, &errmsg); else newkey2 = import_ssh2(filename, realtype, passphrase, &errmsg); if (newkey2 == SSH2_WRONG_PASSPHRASE) ret = -1; else if (!newkey2) ret = 0; else ret = 1; } |
︙ | ︙ | |||
780 781 782 783 784 785 786 787 788 789 790 791 792 793 | "use the \"Save private key\" command to\n" "save it in PuTTY's own format.", key_type_to_str(realtype)); MessageBox(NULL, msg, "PuTTYgen Notice", MB_OK | MB_ICONINFORMATION); } } } /* * Dialog-box function for the main PuTTYgen dialog box. */ static int CALLBACK MainDlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) | > | 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 | "use the \"Save private key\" command to\n" "save it in PuTTY's own format.", key_type_to_str(realtype)); MessageBox(NULL, msg, "PuTTYgen Notice", MB_OK | MB_ICONINFORMATION); } } burnstr(passphrase); } /* * Dialog-box function for the main PuTTYgen dialog box. */ static int CALLBACK MainDlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) |
︙ | ︙ | |||
934 935 936 937 938 939 940 | * anything. */ ui_set_state(hwnd, state, 0); /* * Load a key file if one was provided on the command line. */ | | > | > > | | 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 | * anything. */ ui_set_state(hwnd, state, 0); /* * Load a key file if one was provided on the command line. */ if (cmdline_keyfile) { Filename *fn = filename_from_str(cmdline_keyfile); load_key_file(hwnd, state, fn, 0); filename_free(fn); } return 1; case WM_MOUSEMOVE: state = (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA); if (state->collecting_entropy && state->entropy && state->entropy_got < state->entropy_required) { state->entropy[state->entropy_got++] = lParam; state->entropy[state->entropy_got++] = GetMessageTime(); SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, state->entropy_got, 0); if (state->entropy_got >= state->entropy_required) { struct rsa_key_thread_params *params; DWORD threadid; /* * Seed the entropy pool */ random_add_heavynoise(state->entropy, state->entropy_size); smemclr(state->entropy, state->entropy_size); sfree(state->entropy); state->collecting_entropy = FALSE; SetDlgItemText(hwnd, IDC_GENERATING, generating_msg); SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0, MAKELPARAM(0, PROGRESSRANGE)); SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, 0, 0); |
︙ | ︙ | |||
1098 1099 1100 1101 1102 1103 1104 | case IDC_EXPORT_SSHCOM: if (HIWORD(wParam) != BN_CLICKED) break; state = (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA); if (state->key_exists) { char filename[FILENAME_MAX]; | | < | 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 | case IDC_EXPORT_SSHCOM: if (HIWORD(wParam) != BN_CLICKED) break; state = (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA); if (state->key_exists) { char filename[FILENAME_MAX]; char *passphrase, *passphrase2; int type, realtype; if (state->ssh2) realtype = SSH_KEYTYPE_SSH2; else realtype = SSH_KEYTYPE_SSH1; |
︙ | ︙ | |||
1125 1126 1127 1128 1129 1130 1131 | " format", (state->ssh2 ? 2 : 1), (state->ssh2 ? 1 : 2)); MessageBox(hwnd, msg, "PuTTYgen Error", MB_OK | MB_ICONERROR); break; } | | < | < > > > | > | > | > > | | | > | | | > > | 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 | " format", (state->ssh2 ? 2 : 1), (state->ssh2 ? 1 : 2)); MessageBox(hwnd, msg, "PuTTYgen Error", MB_OK | MB_ICONERROR); break; } passphrase = GetDlgItemText_alloc(hwnd, IDC_PASSPHRASE1EDIT); passphrase2 = GetDlgItemText_alloc(hwnd, IDC_PASSPHRASE2EDIT); if (strcmp(passphrase, passphrase2)) { MessageBox(hwnd, "The two passphrases given do not match.", "PuTTYgen Error", MB_OK | MB_ICONERROR); burnstr(passphrase); burnstr(passphrase2); break; } burnstr(passphrase2); if (!*passphrase) { int ret; ret = MessageBox(hwnd, "Are you sure you want to save this key\n" "without a passphrase to protect it?", "PuTTYgen Warning", MB_YESNO | MB_ICONWARNING); if (ret != IDYES) { burnstr(passphrase); break; } } if (prompt_keyfile(hwnd, "Save private key as:", filename, 1, (type == realtype))) { int ret; FILE *fp = fopen(filename, "r"); if (fp) { char *buffer; fclose(fp); buffer = dupprintf("Overwrite existing file\n%s?", filename); ret = MessageBox(hwnd, buffer, "PuTTYgen Warning", MB_YESNO | MB_ICONWARNING); sfree(buffer); if (ret != IDYES) { burnstr(passphrase); break; } } if (state->ssh2) { Filename *fn = filename_from_str(filename); if (type != realtype) ret = export_ssh2(fn, type, &state->ssh2key, *passphrase ? passphrase : NULL); else ret = ssh2_save_userkey(fn, &state->ssh2key, *passphrase ? passphrase : NULL); filename_free(fn); } else { Filename *fn = filename_from_str(filename); if (type != realtype) ret = export_ssh1(fn, type, &state->key, *passphrase ? passphrase : NULL); else ret = saversakey(fn, &state->key, *passphrase ? passphrase : NULL); filename_free(fn); } if (ret <= 0) { MessageBox(hwnd, "Unable to save key file", "PuTTYgen Error", MB_OK | MB_ICONERROR); } } burnstr(passphrase); } break; case IDC_SAVEPUB: if (HIWORD(wParam) != BN_CLICKED) break; state = (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA); |
︙ | ︙ | |||
1229 1230 1231 1232 1233 1234 1235 | if (HIWORD(wParam) != BN_CLICKED) break; state = (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA); if (!state->generation_thread_exists) { char filename[FILENAME_MAX]; if (prompt_keyfile(hwnd, "Load private key:", | | | | > > | 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 | if (HIWORD(wParam) != BN_CLICKED) break; state = (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA); if (!state->generation_thread_exists) { char filename[FILENAME_MAX]; if (prompt_keyfile(hwnd, "Load private key:", filename, 0, LOWORD(wParam)==IDC_LOAD)) { Filename *fn = filename_from_str(filename); load_key_file(hwnd, state, fn, LOWORD(wParam) != IDC_LOAD); filename_free(fn); } } break; } return 0; case WM_DONEKEY: state = (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA); state->generation_thread_exists = FALSE; |
︙ | ︙ |
Changes to windows/winpgnt.c.
︙ | ︙ | |||
10 11 12 13 14 15 16 17 18 19 | #define PUTTY_DO_GLOBALS #include "putty.h" #include "ssh.h" #include "misc.h" #include "tree234.h" #include <shellapi.h> | > < < < < < < < < < < < < < < < | 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | #define PUTTY_DO_GLOBALS #include "putty.h" #include "ssh.h" #include "misc.h" #include "tree234.h" #include "winsecur.h" #include <shellapi.h> #ifndef NO_SECURITY #include <aclapi.h> #ifdef DEBUG_IPC #define _WIN32_WINNT 0x0500 /* for ConvertSidToStringSid */ #include <sddl.h> #endif #endif |
︙ | ︙ | |||
124 125 126 127 128 129 130 | return; } } *out = '\0'; return; } | | < < < < < < | 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 | return; } } *out = '\0'; return; } static tree234 *rsakeys, *ssh2keys; static int has_security; /* * Forward references */ static void *make_keylist1(int *length); static void *make_keylist2(int *length); static void *get_keylist1(int *length); |
︙ | ︙ | |||
170 171 172 173 174 175 176 | */ struct blob { unsigned char *blob; int len; }; static int cmpkeys_ssh2_asymm(void *av, void *bv); | < < | | | 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 | */ struct blob { unsigned char *blob; int len; }; static int cmpkeys_ssh2_asymm(void *av, void *bv); struct PassphraseProcStruct { char **passphrase; char *comment; }; static tree234 *passphrases = NULL; /* * After processing a list of filenames, we want to forget the * passphrases. */ static void forget_passphrases(void) { while (count234(passphrases) > 0) { char *pp = index234(passphrases, 0); smemclr(pp, strlen(pp)); delpos234(passphrases, 0); free(pp); } } /* * Dialog-box function for the Licence box. |
︙ | ︙ | |||
252 253 254 255 256 257 258 | return 0; } return 0; } static HWND passphrase_box; | < < < < < < < < < < < < < < < | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | | 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 | return 0; } return 0; } static HWND passphrase_box; /* * Dialog-box function for the passphrase box. */ static int CALLBACK PassphraseProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { static char **passphrase = NULL; struct PassphraseProcStruct *p; switch (msg) { case WM_INITDIALOG: passphrase_box = hwnd; /* * Centre the window. |
︙ | ︙ | |||
353 354 355 356 357 358 359 | SetForegroundWindow(hwnd); SetWindowPos(hwnd, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW); p = (struct PassphraseProcStruct *) lParam; passphrase = p->passphrase; if (p->comment) SetDlgItemText(hwnd, 101, p->comment); | > | | | < | | 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 | SetForegroundWindow(hwnd); SetWindowPos(hwnd, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW); p = (struct PassphraseProcStruct *) lParam; passphrase = p->passphrase; if (p->comment) SetDlgItemText(hwnd, 101, p->comment); burnstr(*passphrase); *passphrase = dupstr(""); SetDlgItemText(hwnd, 102, *passphrase); return 0; case WM_COMMAND: switch (LOWORD(wParam)) { case IDOK: if (*passphrase) EndDialog(hwnd, 1); else MessageBeep(0); return 0; case IDCANCEL: EndDialog(hwnd, 0); return 0; case 102: /* edit box */ if ((HIWORD(wParam) == EN_CHANGE) && passphrase) { burnstr(*passphrase); *passphrase = GetDlgItemText_alloc(hwnd, 102); } return 0; } return 0; case WM_CLOSE: EndDialog(hwnd, 0); return 0; |
︙ | ︙ | |||
411 412 413 414 415 416 417 | * Update the visible key list. */ static void keylist_update(void) { struct RSAKey *rkey; struct ssh2_userkey *skey; int i; | < < < | 322 323 324 325 326 327 328 329 330 331 332 333 334 335 | * Update the visible key list. */ static void keylist_update(void) { struct RSAKey *rkey; struct ssh2_userkey *skey; int i; if (keylist) { SendDlgItemMessage(keylist, 100, LB_RESETCONTENT, 0, 0); for (i = 0; NULL != (rkey = index234(rsakeys, i)); i++) { char listentry[512], *p; /* * Replace two spaces in the fingerprint with tabs, for |
︙ | ︙ | |||
436 437 438 439 440 441 442 | p = strchr(listentry, ' '); if (p) *p = '\t'; SendDlgItemMessage(keylist, 100, LB_ADDSTRING, 0, (LPARAM) listentry); } for (i = 0; NULL != (skey = index234(ssh2keys, i)); i++) { | | | > > > | | | < < < < < | < < < | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | | < < < < | < < < < < < < < < < < < < < | < < < | < < < < < < | < < < < < < < < < < < < | > > > > > | 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 | p = strchr(listentry, ' '); if (p) *p = '\t'; SendDlgItemMessage(keylist, 100, LB_ADDSTRING, 0, (LPARAM) listentry); } for (i = 0; NULL != (skey = index234(ssh2keys, i)); i++) { char *listentry, *p; int fp_len; /* * Replace two spaces in the fingerprint with tabs, for * nice alignment in the box. */ p = skey->alg->fingerprint(skey->data); listentry = dupprintf("%s\t%s", p, skey->comment); fp_len = strlen(listentry); sfree(p); p = strchr(listentry, ' '); if (p && p < listentry + fp_len) *p = '\t'; p = strchr(listentry, ' '); if (p && p < listentry + fp_len) *p = '\t'; SendDlgItemMessage(keylist, 100, LB_ADDSTRING, 0, (LPARAM) listentry); sfree(listentry); } SendDlgItemMessage(keylist, 100, LB_SETCURSEL, (WPARAM) - 1, 0); } } /* * This function loads a key from a file and adds it. */ static void add_keyfile(Filename *filename) { char *passphrase; struct RSAKey *rkey = NULL; struct ssh2_userkey *skey = NULL; int needs_pass; int ret; int attempts; char *comment; const char *error = NULL; int type; int original_pass; type = key_type(filename); if (type != SSH_KEYTYPE_SSH1 && type != SSH_KEYTYPE_SSH2) { char *msg = dupprintf("Couldn't load this key (%s)", key_type_to_str(type)); message_box(msg, APPNAME, MB_OK | MB_ICONERROR, HELPCTXID(errors_cantloadkey)); sfree(msg); return; } /* * See if the key is already loaded (in the primary Pageant, * which may or may not be us). */ { void *blob; unsigned char *keylist, *p; int i, nkeys, bloblen, keylistlen; if (type == SSH_KEYTYPE_SSH1) { if (!rsakey_pubblob(filename, &blob, &bloblen, NULL, &error)) { char *msg = dupprintf("Couldn't load private key (%s)", error); message_box(msg, APPNAME, MB_OK | MB_ICONERROR, HELPCTXID(errors_cantloadkey)); sfree(msg); return; } keylist = get_keylist1(&keylistlen); } else { unsigned char *blob2; blob = ssh2_userkey_loadpub(filename, NULL, &bloblen, NULL, &error); if (!blob) { char *msg = dupprintf("Couldn't load private key (%s)", error); message_box(msg, APPNAME, MB_OK | MB_ICONERROR, HELPCTXID(errors_cantloadkey)); sfree(msg); return; } /* For our purposes we want the blob prefixed with its length */ blob2 = snewn(bloblen+4, unsigned char); PUT_32BIT(blob2, bloblen); memcpy(blob2 + 4, blob, bloblen); sfree(blob); blob = blob2; keylist = get_keylist2(&keylistlen); } if (keylist) { if (keylistlen < 4) { MessageBox(NULL, "Received broken key list?!", APPNAME, MB_OK | MB_ICONERROR); return; } nkeys = toint(GET_32BIT(keylist)); if (nkeys < 0) { MessageBox(NULL, "Received broken key list?!", APPNAME, MB_OK | MB_ICONERROR); return; } p = keylist + 4; keylistlen -= 4; for (i = 0; i < nkeys; i++) { if (!memcmp(blob, p, bloblen)) { /* Key is already present; we can now leave. */ sfree(keylist); |
︙ | ︙ | |||
737 738 739 740 741 742 743 | } else { int n; if (keylistlen < 4) { MessageBox(NULL, "Received broken key list?!", APPNAME, MB_OK | MB_ICONERROR); return; } | | | | | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | | | < > > > | > > > > > | > > | > | | < | | > > > > | 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 | } else { int n; if (keylistlen < 4) { MessageBox(NULL, "Received broken key list?!", APPNAME, MB_OK | MB_ICONERROR); return; } n = toint(4 + GET_32BIT(p)); if (n < 0 || keylistlen < n) { MessageBox(NULL, "Received broken key list?!", APPNAME, MB_OK | MB_ICONERROR); return; } p += n; keylistlen -= n; } /* Now skip over comment field */ { int n; if (keylistlen < 4) { MessageBox(NULL, "Received broken key list?!", APPNAME, MB_OK | MB_ICONERROR); return; } n = toint(4 + GET_32BIT(p)); if (n < 0 || keylistlen < n) { MessageBox(NULL, "Received broken key list?!", APPNAME, MB_OK | MB_ICONERROR); return; } p += n; keylistlen -= n; } } sfree(keylist); } sfree(blob); } error = NULL; if (type == SSH_KEYTYPE_SSH1) needs_pass = rsakey_encrypted(filename, &comment); else needs_pass = ssh2_userkey_encrypted(filename, &comment); attempts = 0; if (type == SSH_KEYTYPE_SSH1) rkey = snew(struct RSAKey); passphrase = NULL; original_pass = 0; do { burnstr(passphrase); passphrase = NULL; if (needs_pass) { /* try all the remembered passphrases first */ char *pp = index234(passphrases, attempts); if(pp) { passphrase = dupstr(pp); } else { int dlgret; struct PassphraseProcStruct pps; pps.passphrase = &passphrase; pps.comment = comment; original_pass = 1; dlgret = DialogBoxParam(hinst, MAKEINTRESOURCE(210), NULL, PassphraseProc, (LPARAM) &pps); passphrase_box = NULL; if (!dlgret) { if (comment) sfree(comment); if (type == SSH_KEYTYPE_SSH1) sfree(rkey); return; /* operation cancelled */ } assert(passphrase != NULL); } } else passphrase = dupstr(""); if (type == SSH_KEYTYPE_SSH1) ret = loadrsakey(filename, rkey, passphrase, &error); else { skey = ssh2_load_userkey(filename, passphrase, &error); if (skey == SSH2_WRONG_PASSPHRASE) ret = -1; else if (!skey) ret = 0; else ret = 1; } attempts++; } while (ret == -1); if(original_pass && ret) { /* If they typed in an ok passphrase, remember it */ addpos234(passphrases, passphrase, 0); } else { /* Otherwise, destroy it */ burnstr(passphrase); } passphrase = NULL; if (comment) sfree(comment); if (ret == 0) { char *msg = dupprintf("Couldn't load private key (%s)", error); message_box(msg, APPNAME, MB_OK | MB_ICONERROR, HELPCTXID(errors_cantloadkey)); |
︙ | ︙ | |||
1035 1036 1037 1038 1039 1040 1041 | /* * Create an SSH-2 key list in a malloc'ed buffer; return its * length. */ static void *make_keylist2(int *length) { | < < < < < < < < < < < < < | 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 | /* * Create an SSH-2 key list in a malloc'ed buffer; return its * length. */ static void *make_keylist2(int *length) { struct ssh2_userkey *key; int i, len, nkeys; unsigned char *blob, *p, *ret; int bloblen; /* * Count up the number and length of keys we hold. */ len = 4; nkeys = 0; for (i = 0; NULL != (key = index234(ssh2keys, i)); i++) { nkeys++; len += 4; /* length field */ blob = key->alg->public_blob(key->data, &bloblen); len += bloblen; sfree(blob); len += 4 + strlen(key->comment); } /* Allocate the buffer. */ p = ret = snewn(len, unsigned char); if (length) *length = len; /* * Packet header is the obvious five bytes, plus four * bytes for the key count. |
︙ | ︙ | |||
1088 1089 1090 1091 1092 1093 1094 | memcpy(p, blob, bloblen); p += bloblen; sfree(blob); PUT_32BIT(p, strlen(key->comment)); memcpy(p + 4, key->comment, strlen(key->comment)); p += 4 + strlen(key->comment); } | < < < < < < < < < < < < < | 778 779 780 781 782 783 784 785 786 787 788 789 790 791 | memcpy(p, blob, bloblen); p += bloblen; sfree(blob); PUT_32BIT(p, strlen(key->comment)); memcpy(p + 4, key->comment, strlen(key->comment)); p += 4 + strlen(key->comment); } assert(p - ret == len); return ret; } /* * Acquire a keylist1 from the primary Pageant; this means either |
︙ | ︙ | |||
1125 1126 1127 1128 1129 1130 1131 | int resplen, retval; request[4] = SSH1_AGENTC_REQUEST_RSA_IDENTITIES; PUT_32BIT(request, 4); retval = agent_query(request, 5, &vresponse, &resplen, NULL, NULL); assert(retval == 1); response = vresponse; | | > > | 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 | int resplen, retval; request[4] = SSH1_AGENTC_REQUEST_RSA_IDENTITIES; PUT_32BIT(request, 4); retval = agent_query(request, 5, &vresponse, &resplen, NULL, NULL); assert(retval == 1); response = vresponse; if (resplen < 5 || response[4] != SSH1_AGENT_RSA_IDENTITIES_ANSWER) { sfree(response); return NULL; } ret = snewn(resplen-5, unsigned char); memcpy(ret, response+5, resplen-5); sfree(response); if (length) *length = resplen-5; |
︙ | ︙ | |||
1160 1161 1162 1163 1164 1165 1166 | request[4] = SSH2_AGENTC_REQUEST_IDENTITIES; PUT_32BIT(request, 4); retval = agent_query(request, 5, &vresponse, &resplen, NULL, NULL); assert(retval == 1); response = vresponse; | | > > | 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 | request[4] = SSH2_AGENTC_REQUEST_IDENTITIES; PUT_32BIT(request, 4); retval = agent_query(request, 5, &vresponse, &resplen, NULL, NULL); assert(retval == 1); response = vresponse; if (resplen < 5 || response[4] != SSH2_AGENT_IDENTITIES_ANSWER) { sfree(response); return NULL; } ret = snewn(resplen-5, unsigned char); memcpy(ret, response+5, resplen-5); sfree(response); if (length) *length = resplen-5; |
︙ | ︙ | |||
1256 1257 1258 1259 1260 1261 1262 | p += 4; i = ssh1_read_bignum(p, msgend - p, &reqkey.exponent); if (i < 0) goto failure; p += i; i = ssh1_read_bignum(p, msgend - p, &reqkey.modulus); | | > > | > > > | 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 | p += 4; i = ssh1_read_bignum(p, msgend - p, &reqkey.exponent); if (i < 0) goto failure; p += i; i = ssh1_read_bignum(p, msgend - p, &reqkey.modulus); if (i < 0) { freebn(reqkey.exponent); goto failure; } p += i; i = ssh1_read_bignum(p, msgend - p, &challenge); if (i < 0) { freebn(reqkey.exponent); freebn(reqkey.modulus); goto failure; } p += i; if (msgend < p+16) { freebn(reqkey.exponent); freebn(reqkey.modulus); freebn(challenge); goto failure; } |
︙ | ︙ | |||
1286 1287 1288 1289 1290 1291 1292 | response = rsadecrypt(challenge, key); for (i = 0; i < 32; i++) response_source[i] = bignum_byte(response, 31 - i); MD5Init(&md5c); MD5Update(&md5c, response_source, 48); MD5Final(response_md5, &md5c); | | | 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 | response = rsadecrypt(challenge, key); for (i = 0; i < 32; i++) response_source[i] = bignum_byte(response, 31 - i); MD5Init(&md5c); MD5Update(&md5c, response_source, 48); MD5Final(response_md5, &md5c); smemclr(response_source, 48); /* burn the evidence */ freebn(response); /* and that evidence */ freebn(challenge); /* yes, and that evidence */ freebn(reqkey.exponent); /* and free some memory ... */ freebn(reqkey.modulus); /* ... while we're at it. */ /* * Packet is the obvious five byte header, plus sixteen |
︙ | ︙ | |||
1309 1310 1311 1312 1313 1314 1315 | case SSH2_AGENTC_SIGN_REQUEST: /* * Reply with either SSH2_AGENT_SIGN_RESPONSE or * SSH_AGENT_FAILURE, depending on whether we have that key * or not. */ { | < < < | < | | > | | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 | case SSH2_AGENTC_SIGN_REQUEST: /* * Reply with either SSH2_AGENT_SIGN_RESPONSE or * SSH_AGENT_FAILURE, depending on whether we have that key * or not. */ { struct ssh2_userkey *key; struct blob b; unsigned char *data, *signature; int datalen, siglen, len; if (msgend < p+4) goto failure; b.len = toint(GET_32BIT(p)); if (b.len < 0 || b.len > msgend - (p+4)) goto failure; p += 4; b.blob = p; p += b.len; if (msgend < p+4) goto failure; datalen = toint(GET_32BIT(p)); p += 4; if (datalen < 0 || datalen > msgend - p) goto failure; data = p; key = find234(ssh2keys, &b, cmpkeys_ssh2_asymm); if (!key) goto failure; signature = key->alg->sign(key->data, data, datalen, &siglen); len = 5 + 4 + siglen; PUT_32BIT(ret, len - 4); ret[4] = SSH2_AGENT_SIGN_RESPONSE; PUT_32BIT(ret + 5, siglen); memcpy(ret + 5 + 4, signature, siglen); sfree(signature); } |
︙ | ︙ | |||
1436 1437 1438 1439 1440 1441 1442 | p += n; if (msgend < p+4) { freersakey(key); sfree(key); goto failure; } | | | | 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 | p += n; if (msgend < p+4) { freersakey(key); sfree(key); goto failure; } commentlen = toint(GET_32BIT(p)); if (commentlen < 0 || commentlen > msgend - p) { freersakey(key); sfree(key); goto failure; } comment = snewn(commentlen+1, char); if (comment) { |
︙ | ︙ | |||
1475 1476 1477 1478 1479 1480 1481 | char *comment, *alg; int alglen, commlen; int bloblen; if (msgend < p+4) goto failure; | | | < < < < < < < < < < < < < < < < < < < < < < < < < < | 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 | char *comment, *alg; int alglen, commlen; int bloblen; if (msgend < p+4) goto failure; alglen = toint(GET_32BIT(p)); p += 4; if (alglen < 0 || alglen > msgend - p) goto failure; alg = p; p += alglen; key = snew(struct ssh2_userkey); /* Add further algorithm names here. */ if (alglen == 7 && !memcmp(alg, "ssh-rsa", 7)) key->alg = &ssh_rsa; else if (alglen == 7 && !memcmp(alg, "ssh-dss", 7)) key->alg = &ssh_dss; else { |
︙ | ︙ | |||
1537 1538 1539 1540 1541 1542 1543 | assert(p <= msgend); if (msgend < p+4) { key->alg->freekey(key->data); sfree(key); goto failure; } | | | | 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 | assert(p <= msgend); if (msgend < p+4) { key->alg->freekey(key->data); sfree(key); goto failure; } commlen = toint(GET_32BIT(p)); p += 4; if (commlen < 0 || commlen > msgend - p) { key->alg->freekey(key->data); sfree(key); goto failure; } comment = snewn(commlen + 1, char); if (comment) { memcpy(comment, p, commlen); |
︙ | ︙ | |||
1604 1605 1606 1607 1608 1609 1610 | */ { struct ssh2_userkey *key; struct blob b; if (msgend < p+4) goto failure; | | | | 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 | */ { struct ssh2_userkey *key; struct blob b; if (msgend < p+4) goto failure; b.len = toint(GET_32BIT(p)); p += 4; if (b.len < 0 || b.len > msgend - p) goto failure; b.blob = p; p += b.len; key = find234(ssh2keys, &b, cmpkeys_ssh2_asymm); if (!key) goto failure; |
︙ | ︙ | |||
1814 1815 1816 1817 1818 1819 1820 | of.lpstrFile = filelist; *filelist = '\0'; of.nMaxFile = 8192; of.lpstrFileTitle = NULL; of.lpstrTitle = "Select Private Key File"; of.Flags = OFN_ALLOWMULTISELECT | OFN_EXPLORER; if (request_file(keypath, &of, TRUE, FALSE)) { | | > | > | > | > < < < | 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 | of.lpstrFile = filelist; *filelist = '\0'; of.nMaxFile = 8192; of.lpstrFileTitle = NULL; of.lpstrTitle = "Select Private Key File"; of.Flags = OFN_ALLOWMULTISELECT | OFN_EXPLORER; if (request_file(keypath, &of, TRUE, FALSE)) { if(strlen(filelist) > of.nFileOffset) { /* Only one filename returned? */ Filename *fn = filename_from_str(filelist); add_keyfile(fn); filename_free(fn); } else { /* we are returned a bunch of strings, end to * end. first string is the directory, the * rest the filenames. terminated with an * empty string. */ char *dir = filelist; char *filewalker = filelist + strlen(dir) + 1; while (*filewalker != '\0') { char *filename = dupcat(dir, "\\", filewalker, NULL); Filename *fn = filename_from_str(filename); add_keyfile(fn); filename_free(fn); sfree(filename); filewalker += strlen(filewalker) + 1; } } keylist_update(); forget_passphrases(); } sfree(filelist); } /* * Dialog-box function for the key list box. */ static int CALLBACK KeyListProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { struct RSAKey *rkey; struct ssh2_userkey *skey; switch (msg) { case WM_INITDIALOG: /* * Centre the window. */ { /* centre the window */ |
︙ | ︙ | |||
1909 1910 1911 1912 1913 1914 1915 | prompt_add_keyfile(); } return 0; case 102: /* remove key */ if (HIWORD(wParam) == BN_CLICKED || HIWORD(wParam) == BN_DOUBLECLICKED) { int i; | | | 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 | prompt_add_keyfile(); } return 0; case 102: /* remove key */ if (HIWORD(wParam) == BN_CLICKED || HIWORD(wParam) == BN_DOUBLECLICKED) { int i; int rCount, sCount; int *selectedArray; /* our counter within the array of selected items */ int itemNum; /* get the number of items selected in the list */ int numSelected = |
︙ | ︙ | |||
1933 1934 1935 1936 1937 1938 1939 | selectedArray = snewn(numSelected, int); SendDlgItemMessage(hwnd, 100, LB_GETSELITEMS, numSelected, (WPARAM)selectedArray); itemNum = numSelected - 1; rCount = count234(rsakeys); sCount = count234(ssh2keys); | < < < < < < < < < < < < < < | 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 | selectedArray = snewn(numSelected, int); SendDlgItemMessage(hwnd, 100, LB_GETSELITEMS, numSelected, (WPARAM)selectedArray); itemNum = numSelected - 1; rCount = count234(rsakeys); sCount = count234(ssh2keys); /* go through the non-rsakeys until we've covered them all, * and/or we're out of selected items to check. note that * we go *backwards*, to avoid complications from deleting * things hence altering the offset of subsequent items */ for (i = sCount - 1; (itemNum >= 0) && (i >= 0); i--) { skey = index234(ssh2keys, i); if (selectedArray[itemNum] == rCount + i) { del234(ssh2keys, skey); skey->alg->freekey(skey->data); sfree(skey); |
︙ | ︙ | |||
1986 1987 1988 1989 1990 1991 1992 | return 0; case 103: /* help */ if (HIWORD(wParam) == BN_CLICKED || HIWORD(wParam) == BN_DOUBLECLICKED) { launch_help(hwnd, WINHELP_CTX_pageant_general); } return 0; | < < < < < < | < < < < < < < < < < < < < < < < < < < | < < < < < < < < < < < < < < | 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 | return 0; case 103: /* help */ if (HIWORD(wParam) == BN_CLICKED || HIWORD(wParam) == BN_DOUBLECLICKED) { launch_help(hwnd, WINHELP_CTX_pageant_general); } return 0; } return 0; case WM_HELP: { int id = ((LPHELPINFO)lParam)->iCtrlId; char *topic = NULL; switch (id) { case 100: topic = WINHELP_CTX_pageant_keylist; break; case 101: topic = WINHELP_CTX_pageant_addkey; break; |
︙ | ︙ | |||
2069 2070 2071 2072 2073 2074 2075 | tnid.cbSize = sizeof(NOTIFYICONDATA); tnid.hWnd = hwnd; tnid.uID = 1; /* unique within this systray use */ tnid.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP; tnid.uCallbackMessage = WM_SYSTRAY; tnid.hIcon = hicon = LoadIcon(hinst, MAKEINTRESOURCE(201)); | | | 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 | tnid.cbSize = sizeof(NOTIFYICONDATA); tnid.hWnd = hwnd; tnid.uID = 1; /* unique within this systray use */ tnid.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP; tnid.uCallbackMessage = WM_SYSTRAY; tnid.hIcon = hicon = LoadIcon(hinst, MAKEINTRESOURCE(201)); strcpy(tnid.szTip, "Pageant (PuTTY authentication agent)"); res = Shell_NotifyIcon(NIM_ADD, &tnid); if (hicon) DestroyIcon(hicon); return res; } |
︙ | ︙ | |||
2341 2342 2343 2344 2345 2346 2347 2348 2349 2350 2351 2352 2353 2354 2355 2356 2357 2358 2359 2360 2361 2362 2363 2364 2365 2366 2367 2368 2369 2370 2371 2372 2373 2374 2375 2376 2377 2378 2379 2380 2381 2382 2383 2384 2385 2386 2387 2388 2389 2390 | #ifndef NO_SECURITY int rc; if (has_security) { if ((ourself = get_user_sid()) == NULL) { #ifdef DEBUG_IPC debug(("couldn't get user SID\n")); #endif return 0; } if ((ourself2 = get_default_sid()) == NULL) { #ifdef DEBUG_IPC debug(("couldn't get default SID\n")); #endif return 0; } if ((rc = p_GetSecurityInfo(filemap, SE_KERNEL_OBJECT, OWNER_SECURITY_INFORMATION, &mapowner, NULL, NULL, NULL, &psd) != ERROR_SUCCESS)) { #ifdef DEBUG_IPC debug(("couldn't get owner info for filemap: %d\n", rc)); #endif return 0; } #ifdef DEBUG_IPC { LPTSTR ours, ours2, theirs; ConvertSidToStringSid(mapowner, &theirs); ConvertSidToStringSid(ourself, &ours); ConvertSidToStringSid(ourself2, &ours2); debug(("got sids:\n oursnew=%s\n oursold=%s\n" " theirs=%s\n", ours, ours2, theirs)); LocalFree(ours); LocalFree(ours2); LocalFree(theirs); } #endif if (!EqualSid(mapowner, ourself) && !EqualSid(mapowner, ourself2)) { CloseHandle(filemap); return 0; /* security ID mismatch! */ } #ifdef DEBUG_IPC debug(("security stuff matched\n")); #endif LocalFree(psd); sfree(ourself); | > > > > > > > > > | 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 | #ifndef NO_SECURITY int rc; if (has_security) { if ((ourself = get_user_sid()) == NULL) { #ifdef DEBUG_IPC debug(("couldn't get user SID\n")); #endif CloseHandle(filemap); return 0; } if ((ourself2 = get_default_sid()) == NULL) { #ifdef DEBUG_IPC debug(("couldn't get default SID\n")); #endif CloseHandle(filemap); sfree(ourself); return 0; } if ((rc = p_GetSecurityInfo(filemap, SE_KERNEL_OBJECT, OWNER_SECURITY_INFORMATION, &mapowner, NULL, NULL, NULL, &psd) != ERROR_SUCCESS)) { #ifdef DEBUG_IPC debug(("couldn't get owner info for filemap: %d\n", rc)); #endif CloseHandle(filemap); sfree(ourself); sfree(ourself2); return 0; } #ifdef DEBUG_IPC { LPTSTR ours, ours2, theirs; ConvertSidToStringSid(mapowner, &theirs); ConvertSidToStringSid(ourself, &ours); ConvertSidToStringSid(ourself2, &ours2); debug(("got sids:\n oursnew=%s\n oursold=%s\n" " theirs=%s\n", ours, ours2, theirs)); LocalFree(ours); LocalFree(ours2); LocalFree(theirs); } #endif if (!EqualSid(mapowner, ourself) && !EqualSid(mapowner, ourself2)) { CloseHandle(filemap); LocalFree(psd); sfree(ourself); sfree(ourself2); return 0; /* security ID mismatch! */ } #ifdef DEBUG_IPC debug(("security stuff matched\n")); #endif LocalFree(psd); sfree(ourself); |
︙ | ︙ | |||
2450 2451 2452 2453 2454 2455 2456 | int flags = FLAG_SYNCAGENT; int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) { WNDCLASS wndclass; MSG msg; | < < < < < | 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 | int flags = FLAG_SYNCAGENT; int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) { WNDCLASS wndclass; MSG msg; char *command = NULL; int added_keys = 0; int argc, i; char **argv, **argstart; hinst = inst; hwnd = NULL; /* * Determine whether we're an NT system (should have security * APIs) or a non-NT system (don't do security). */ |
︙ | ︙ | |||
2481 2482 2483 2484 2485 2486 2487 | has_security = FALSE; if (has_security) { #ifndef NO_SECURITY /* * Attempt to get the security API we need. */ | | < < > | 2056 2057 2058 2059 2060 2061 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 | has_security = FALSE; if (has_security) { #ifndef NO_SECURITY /* * Attempt to get the security API we need. */ if (!got_advapi()) { MessageBox(NULL, "Unable to access security APIs. Pageant will\n" "not run, in case it causes a security breach.", "Pageant Fatal Error", MB_ICONERROR | MB_OK); return 1; } #else MessageBox(NULL, "This program has been compiled for Win9X and will\n" "not run on NT, in case it causes a security breach.", "Pageant Fatal Error", MB_ICONERROR | MB_OK); return 1; #endif } /* * See if we can find our Help file. */ init_help(); /* |
︙ | ︙ | |||
2535 2536 2537 2538 2539 2540 2541 | /* * Initialise storage for RSA keys. */ if (!already_running) { rsakeys = newtree234(cmpkeys_rsa); ssh2keys = newtree234(cmpkeys_ssh2); | < < < < < > | > | 2109 2110 2111 2112 2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 2124 2125 2126 2127 2128 2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141 2142 2143 2144 2145 2146 2147 2148 2149 2150 2151 2152 | /* * Initialise storage for RSA keys. */ if (!already_running) { rsakeys = newtree234(cmpkeys_rsa); ssh2keys = newtree234(cmpkeys_ssh2); } /* * Initialise storage for short-term passphrase cache. */ passphrases = newtree234(NULL); /* * Process the command line and add keys as listed on it. */ split_into_argv(cmdline, &argc, &argv, &argstart); for (i = 0; i < argc; i++) { if (!strcmp(argv[i], "-pgpfp")) { pgp_fingerprints(); return 1; } else if (!strcmp(argv[i], "-c")) { /* * If we see `-c', then the rest of the * command line should be treated as a * command to be spawned. */ if (i < argc-1) command = argstart[i+1]; else command = ""; break; } else { Filename *fn = filename_from_str(argv[i]); add_keyfile(fn); filename_free(fn); added_keys = TRUE; } } /* * Forget any passphrase that we retained while going over * command line keyfiles. |
︙ | ︙ | |||
2601 2602 2603 2604 2605 2606 2607 | * keys), complain. */ if (already_running) { if (!command && !added_keys) { MessageBox(NULL, "Pageant is already running", "Pageant Error", MB_ICONERROR | MB_OK); } | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 2172 2173 2174 2175 2176 2177 2178 2179 2180 2181 2182 2183 2184 2185 2186 2187 2188 2189 2190 2191 2192 2193 2194 2195 2196 2197 2198 2199 2200 2201 2202 2203 2204 2205 | * keys), complain. */ if (already_running) { if (!command && !added_keys) { MessageBox(NULL, "Pageant is already running", "Pageant Error", MB_ICONERROR | MB_OK); } return 0; } if (!prev) { wndclass.style = 0; wndclass.lpfnWndProc = WndProc; wndclass.cbClsExtra = 0; wndclass.cbWndExtra = 0; wndclass.hInstance = inst; wndclass.hIcon = LoadIcon(inst, MAKEINTRESOURCE(IDI_MAINICON)); wndclass.hCursor = LoadCursor(NULL, IDC_IBEAM); wndclass.hbrBackground = GetStockObject(BLACK_BRUSH); wndclass.lpszMenuName = NULL; wndclass.lpszClassName = APPNAME; RegisterClass(&wndclass); } keylist = NULL; hwnd = CreateWindow(APPNAME, APPNAME, WS_OVERLAPPEDWINDOW | WS_VSCROLL, CW_USEDEFAULT, CW_USEDEFAULT, 100, 100, NULL, NULL, inst, NULL); /* Set up a system tray icon */ AddTrayIcon(hwnd); |
︙ | ︙ | |||
2742 2743 2744 2745 2746 2747 2748 | Shell_NotifyIcon(NIM_DELETE, &tnid); DestroyMenu(systray_menu); } if (keypath) filereq_free(keypath); | < < < | 2251 2252 2253 2254 2255 2256 2257 2258 2259 2260 | Shell_NotifyIcon(NIM_DELETE, &tnid); DestroyMenu(systray_menu); } if (keypath) filereq_free(keypath); cleanup_exit(msg.wParam); return msg.wParam; /* just in case optimiser complains */ } |
Changes to windows/winpgntc.c.
1 2 3 4 5 6 7 8 9 10 | /* * Pageant client code. */ #include <stdio.h> #include <stdlib.h> #include "putty.h" #ifndef NO_SECURITY | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | /* * Pageant client code. */ #include <stdio.h> #include <stdlib.h> #include "putty.h" #ifndef NO_SECURITY #include "winsecur.h" #endif #define AGENT_COPYDATA_ID 0x804e50ba /* random goop */ #define AGENT_MAX_MSGLEN 8192 int agent_exists(void) { |
︙ | ︙ | |||
66 67 68 69 70 71 72 | agent_schedule_callback(data->callback, data->callback_ctx, ret, retlen); return 0; } #endif | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 66 67 68 69 70 71 72 73 74 75 76 77 78 79 | agent_schedule_callback(data->callback, data->callback_ctx, ret, retlen); return 0; } #endif int agent_query(void *in, int inlen, void **out, int *outlen, void (*callback)(void *, void *, int), void *callback_ctx) { HWND hwnd; char *mapname; HANDLE filemap; unsigned char *p, *ret; |
︙ | ︙ | |||
169 170 171 172 173 174 175 176 | *outlen = 0; hwnd = FindWindow("Pageant", "Pageant"); if (!hwnd) return 1; /* *out == NULL, so failure */ mapname = dupprintf("PageantRequest%08x", (unsigned)GetCurrentThreadId()); #ifndef NO_SECURITY | > | < | 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 | *outlen = 0; hwnd = FindWindow("Pageant", "Pageant"); if (!hwnd) return 1; /* *out == NULL, so failure */ mapname = dupprintf("PageantRequest%08x", (unsigned)GetCurrentThreadId()); psa = NULL; #ifndef NO_SECURITY if (got_advapi()) { /* * Make the file mapping we create for communication with * Pageant owned by the user SID rather than the default. This * should make communication between processes with slightly * different contexts more reliable: in particular, command * prompts launched as administrator should still be able to * run PSFTPs which refer back to the owning user's * unprivileged Pageant. */ usersid = get_user_sid(); if (usersid) { psd = (PSECURITY_DESCRIPTOR) LocalAlloc(LPTR, SECURITY_DESCRIPTOR_MIN_LENGTH); if (psd) { if (p_InitializeSecurityDescriptor (psd, SECURITY_DESCRIPTOR_REVISION) && p_SetSecurityDescriptorOwner(psd, usersid, FALSE)) { |
︙ | ︙ | |||
205 206 207 208 209 210 211 | } } } #endif /* NO_SECURITY */ filemap = CreateFileMapping(INVALID_HANDLE_VALUE, psa, PAGE_READWRITE, 0, AGENT_MAX_MSGLEN, mapname); | | > > | 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 | } } } #endif /* NO_SECURITY */ filemap = CreateFileMapping(INVALID_HANDLE_VALUE, psa, PAGE_READWRITE, 0, AGENT_MAX_MSGLEN, mapname); if (filemap == NULL || filemap == INVALID_HANDLE_VALUE) { sfree(mapname); return 1; /* *out == NULL, so failure */ } p = MapViewOfFile(filemap, FILE_MAP_WRITE, 0, 0, 0); memcpy(p, in, inlen); cds.dwData = AGENT_COPYDATA_ID; cds.cbData = 1 + strlen(mapname); cds.lpData = mapname; #ifdef WINDOWS_ASYNC_AGENT if (callback != NULL && !(flags & FLAG_SYNCAGENT)) { |
︙ | ︙ | |||
233 234 235 236 237 238 239 240 241 242 243 244 245 246 | data->mapname = mapname; data->callback = callback; data->callback_ctx = callback_ctx; data->cds = cds; /* structure copy */ data->hwnd = hwnd; if (CreateThread(NULL, 0, agent_query_thread, data, 0, &threadid)) return 0; sfree(data); } #endif /* * The user either passed a null callback (indicating that the * query is required to be synchronous) or CreateThread failed. | > | 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 | data->mapname = mapname; data->callback = callback; data->callback_ctx = callback_ctx; data->cds = cds; /* structure copy */ data->hwnd = hwnd; if (CreateThread(NULL, 0, agent_query_thread, data, 0, &threadid)) return 0; sfree(mapname); sfree(data); } #endif /* * The user either passed a null callback (indicating that the * query is required to be synchronous) or CreateThread failed. |
︙ | ︙ | |||
254 255 256 257 258 259 260 261 262 263 264 265 | memcpy(ret, p, retlen); *out = ret; *outlen = retlen; } } UnmapViewOfFile(p); CloseHandle(filemap); if (psd) LocalFree(psd); sfree(usersid); return 1; } | > | 175 176 177 178 179 180 181 182 183 184 185 186 187 | memcpy(ret, p, retlen); *out = ret; *outlen = retlen; } } UnmapViewOfFile(p); CloseHandle(filemap); sfree(mapname); if (psd) LocalFree(psd); sfree(usersid); return 1; } |
Changes to windows/winplink.c.
︙ | ︙ | |||
44 45 46 47 48 49 50 51 52 53 54 55 56 57 | va_end(ap); fputc('\n', stderr); if (logctx) { log_free(logctx); logctx = NULL; } cleanup_exit(1); } void connection_fatal(void *frontend, char *p, ...) { va_list ap; fprintf(stderr, "FATAL ERROR: "); va_start(ap, p); vfprintf(stderr, p, ap); | > > > > > > > > > | 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 | va_end(ap); fputc('\n', stderr); if (logctx) { log_free(logctx); logctx = NULL; } cleanup_exit(1); } void nonfatal(char *p, ...) { va_list ap; fprintf(stderr, "ERROR: "); va_start(ap, p); vfprintf(stderr, p, ap); va_end(ap); fputc('\n', stderr); } void connection_fatal(void *frontend, char *p, ...) { va_list ap; fprintf(stderr, "FATAL ERROR: "); va_start(ap, p); vfprintf(stderr, p, ap); |
︙ | ︙ | |||
79 80 81 82 83 84 85 | DWORD orig_console_mode; int connopen; WSAEVENT netevent; static Backend *back; static void *backhandle; | | | 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 | DWORD orig_console_mode; int connopen; WSAEVENT netevent; static Backend *back; static void *backhandle; static Conf *conf; int term_ldisc(Terminal *term, int mode) { return FALSE; } void ldisc_update(void *frontend, int echo, int edit) { |
︙ | ︙ | |||
125 126 127 128 129 130 131 132 133 134 135 136 137 138 | /* * No "untrusted" output should get here (the way the code is * currently, it's all diverted by FLAG_STDERR). */ assert(!"Unexpected call to from_backend_untrusted()"); return 0; /* not reached */ } int get_userpass_input(prompts_t *p, unsigned char *in, int inlen) { int ret; ret = cmdline_get_passwd_input(p, in, inlen); if (ret == -1) ret = console_get_userpass_input(p, in, inlen); | > > > > > > | 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 | /* * No "untrusted" output should get here (the way the code is * currently, it's all diverted by FLAG_STDERR). */ assert(!"Unexpected call to from_backend_untrusted()"); return 0; /* not reached */ } int from_backend_eof(void *frontend_handle) { handle_write_eof(stdout_handle); return FALSE; /* do not respond to incoming EOF with outgoing */ } int get_userpass_input(prompts_t *p, unsigned char *in, int inlen) { int ret; ret = cmdline_get_passwd_input(p, in, inlen); if (ret == -1) ret = console_get_userpass_input(p, in, inlen); |
︙ | ︙ | |||
269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 | } if (connopen && back->connected(backhandle)) { back->unthrottle(backhandle, (handle_backlog(stdout_handle) + handle_backlog(stderr_handle))); } } int main(int argc, char **argv) { int sending; int portnumber = -1; SOCKET *sklist; int skcount, sksize; int exitcode; int errors; int got_host = FALSE; int use_subsystem = 0; | > > > | > | | | | | > > | | | > > | | | | | < > > | | 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 | } if (connopen && back->connected(backhandle)) { back->unthrottle(backhandle, (handle_backlog(stdout_handle) + handle_backlog(stderr_handle))); } } const int share_can_be_downstream = TRUE; const int share_can_be_upstream = TRUE; int main(int argc, char **argv) { int sending; int portnumber = -1; SOCKET *sklist; int skcount, sksize; int exitcode; int errors; int got_host = FALSE; int use_subsystem = 0; unsigned long now, next, then; sklist = NULL; skcount = sksize = 0; /* * Initialise port and protocol to sensible defaults. (These * will be overridden by more or less anything.) */ default_protocol = PROT_SSH; default_port = 22; flags = FLAG_STDERR; /* * Process the command line. */ conf = conf_new(); do_defaults(NULL, conf); loaded_session = FALSE; default_protocol = conf_get_int(conf, CONF_protocol); default_port = conf_get_int(conf, CONF_port); errors = 0; { /* * Override the default protocol if PLINK_PROTOCOL is set. */ char *p = getenv("PLINK_PROTOCOL"); if (p) { const Backend *b = backend_from_name(p); if (b) { default_protocol = b->protocol; default_port = b->default_port; conf_set_int(conf, CONF_protocol, default_protocol); conf_set_int(conf, CONF_port, default_port); } } } while (--argc) { char *p = *++argv; if (*p == '-') { int ret = cmdline_process_param(p, (argc > 1 ? argv[1] : NULL), 1, conf); if (ret == -2) { fprintf(stderr, "plink: option \"%s\" requires an argument\n", p); errors = 1; } else if (ret == 2) { --argc, ++argv; } else if (ret == 1) { continue; } else if (!strcmp(p, "-batch")) { console_batch_mode = 1; } else if (!strcmp(p, "-s")) { /* Save status to write to conf later. */ use_subsystem = 1; } else if (!strcmp(p, "-V") || !strcmp(p, "--version")) { version(); } else if (!strcmp(p, "--help")) { usage(); } else if (!strcmp(p, "-pgpfp")) { pgp_fingerprints(); exit(1); } else { fprintf(stderr, "plink: unknown option \"%s\"\n", p); errors = 1; } } else if (*p) { if (!conf_launchable(conf) || !(got_host || loaded_session)) { char *q = p; /* * If the hostname starts with "telnet:", set the * protocol to Telnet and process the string as a * Telnet URL. */ if (!strncmp(q, "telnet:", 7)) { char c; q += 7; if (q[0] == '/' && q[1] == '/') q += 2; conf_set_int(conf, CONF_protocol, PROT_TELNET); p = q; while (*p && *p != ':' && *p != '/') p++; c = *p; if (*p) *p++ = '\0'; if (c == ':') conf_set_int(conf, CONF_port, atoi(p)); else conf_set_int(conf, CONF_port, -1); conf_set_str(conf, CONF_host, q); got_host = TRUE; } else { char *r, *user, *host; /* * Before we process the [user@]host string, we * first check for the presence of a protocol * prefix (a protocol name followed by ","). */ r = strchr(p, ','); if (r) { const Backend *b; *r = '\0'; b = backend_from_name(p); if (b) { default_protocol = b->protocol; conf_set_int(conf, CONF_protocol, default_protocol); portnumber = b->default_port; } p = r + 1; } /* * A nonzero length string followed by an @ is treated |
︙ | ︙ | |||
407 408 409 410 411 412 413 | } /* * Now attempt to load a saved session with the * same name as the hostname. */ { | | | | | < | | > | < < | 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 | } /* * Now attempt to load a saved session with the * same name as the hostname. */ { Conf *conf2 = conf_new(); do_defaults(host, conf2); if (loaded_session || !conf_launchable(conf2)) { /* No settings for this host; use defaults */ /* (or session was already loaded with -load) */ conf_set_str(conf, CONF_host, host); conf_set_int(conf, CONF_port, default_port); got_host = TRUE; } else { conf_copy_into(conf, conf2); loaded_session = TRUE; } conf_free(conf2); } if (user) { /* Patch in specified username. */ conf_set_str(conf, CONF_username, user); } } } else { char *command; int cmdlen, cmdsize; cmdlen = cmdsize = 0; |
︙ | ︙ | |||
453 454 455 456 457 458 459 | command = sresize(command, cmdsize, char); } command[cmdlen++]=' '; /* always add trailing space */ if (--argc) p = *++argv; } if (cmdlen) command[--cmdlen]='\0'; /* change trailing blank to NUL */ | | | | | | | | > | > > > > > | > > | | < | | | | > | > > > > | > > > > > > > > > | > > > > > | | < < < < | < < < < < < < < < < < < < < | | | | | | | > > | > | 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 | command = sresize(command, cmdsize, char); } command[cmdlen++]=' '; /* always add trailing space */ if (--argc) p = *++argv; } if (cmdlen) command[--cmdlen]='\0'; /* change trailing blank to NUL */ conf_set_str(conf, CONF_remote_cmd, command); conf_set_str(conf, CONF_remote_cmd2, ""); conf_set_int(conf, CONF_nopty, TRUE); /* command => no tty */ break; /* done with cmdline */ } } } if (errors) return 1; if (!conf_launchable(conf) || !(got_host || loaded_session)) { usage(); } /* * Muck about with the hostname in various ways. */ { char *hostbuf = dupstr(conf_get_str(conf, CONF_host)); char *host = hostbuf; char *p, *q; /* * Trim leading whitespace. */ host += strspn(host, " \t"); /* * See if host is of the form user@host, and separate out * the username if so. */ if (host[0] != '\0') { char *atsign = strrchr(host, '@'); if (atsign) { *atsign = '\0'; conf_set_str(conf, CONF_username, host); host = atsign + 1; } } /* * Trim off a colon suffix if it's there. */ host[strcspn(host, ":")] = '\0'; /* * Remove any remaining whitespace. */ p = hostbuf; q = host; while (*q) { if (*q != ' ' && *q != '\t') *p++ = *q; q++; } *p = '\0'; conf_set_str(conf, CONF_host, hostbuf); sfree(hostbuf); } /* * Perform command-line overrides on session configuration. */ cmdline_run_saved(conf); /* * Apply subsystem status. */ if (use_subsystem) conf_set_int(conf, CONF_ssh_subsys, TRUE); if (!*conf_get_str(conf, CONF_remote_cmd) && !*conf_get_str(conf, CONF_remote_cmd2) && !*conf_get_str(conf, CONF_ssh_nc_host)) flags |= FLAG_INTERACTIVE; /* * Select protocol. This is farmed out into a table in a * separate file to enable an ssh-free variant. */ back = backend_from_proto(conf_get_int(conf, CONF_protocol)); if (back == NULL) { fprintf(stderr, "Internal fault: Unsupported protocol found\n"); return 1; } /* * Select port. */ if (portnumber != -1) conf_set_int(conf, CONF_port, portnumber); sk_init(); if (p_WSAEventSelect == NULL) { fprintf(stderr, "Plink requires WinSock 2\n"); return 1; } logctx = log_init(NULL, conf); console_provide_logctx(logctx); /* * Start up the connection. */ netevent = CreateEvent(NULL, FALSE, FALSE, NULL); { const char *error; char *realhost; /* nodelay is only useful if stdin is a character device (console) */ int nodelay = conf_get_int(conf, CONF_tcp_nodelay) && (GetFileType(GetStdHandle(STD_INPUT_HANDLE)) == FILE_TYPE_CHAR); error = back->init(NULL, &backhandle, conf, conf_get_str(conf, CONF_host), conf_get_int(conf, CONF_port), &realhost, nodelay, conf_get_int(conf, CONF_tcp_keepalives)); if (error) { fprintf(stderr, "Unable to open connection:\n%s", error); return 1; } back->provide_logctx(backhandle, logctx); sfree(realhost); } |
︙ | ︙ | |||
610 611 612 613 614 615 616 | if (!sending && back->sendok(backhandle)) { stdin_handle = handle_input_new(inhandle, stdin_gotdata, NULL, 0); sending = TRUE; } | > > | > | > | > > | 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 | if (!sending && back->sendok(backhandle)) { stdin_handle = handle_input_new(inhandle, stdin_gotdata, NULL, 0); sending = TRUE; } if (toplevel_callback_pending()) { ticks = 0; } else if (run_timers(now, &next)) { then = now; now = GETTICKCOUNT(); if (now - then > next - then) ticks = 0; else ticks = next - now; } else { ticks = INFINITE; } handles = handle_get_events(&nhandles); handles = sresize(handles, nhandles+1, HANDLE); handles[nhandles] = netevent; |
︙ | ︙ | |||
695 696 697 698 699 700 701 702 703 704 705 706 707 708 | WM_AGENT_CALLBACK, WM_AGENT_CALLBACK, PM_REMOVE)) { struct agent_callback *c = (struct agent_callback *)msg.lParam; c->callback(c->callback_ctx, c->data, c->len); sfree(c); } } if (n == WAIT_TIMEOUT) { now = next; } else { now = GETTICKCOUNT(); } | > > | 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 | WM_AGENT_CALLBACK, WM_AGENT_CALLBACK, PM_REMOVE)) { struct agent_callback *c = (struct agent_callback *)msg.lParam; c->callback(c->callback_ctx, c->data, c->len); sfree(c); } } run_toplevel_callbacks(); if (n == WAIT_TIMEOUT) { now = next; } else { now = GETTICKCOUNT(); } |
︙ | ︙ |
Changes to windows/winprint.c.
︙ | ︙ | |||
14 15 16 17 18 19 20 | } info; }; struct printer_job_tag { HANDLE hprinter; }; | | | | | | | | | | | | < < < | 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 | } info; }; struct printer_job_tag { HANDLE hprinter; }; static int printer_add_enum(int param, DWORD level, char **buffer, int offset, int *nprinters_ptr) { DWORD needed = 0, nprinters = 0; *buffer = sresize(*buffer, offset+512, char); /* * Exploratory call to EnumPrinters to determine how much space * we'll need for the output. Discard the return value since it * will almost certainly be a failure due to lack of space. */ EnumPrinters(param, NULL, level, (*buffer)+offset, 512, &needed, &nprinters); if (needed < 512) needed = 512; *buffer = sresize(*buffer, offset+needed, char); if (EnumPrinters(param, NULL, level, (*buffer)+offset, needed, &needed, &nprinters) == 0) return FALSE; *nprinters_ptr += nprinters; return TRUE; } printer_enum *printer_start_enum(int *nprinters_ptr) { printer_enum *ret = snew(printer_enum); char *buffer = NULL; *nprinters_ptr = 0; /* default return value */ buffer = snewn(512, char); /* * Determine what enumeration level to use. * When enumerating printers, we need to use PRINTER_INFO_4 on * NT-class systems to avoid Windows looking too hard for them and * slowing things down; and we need to avoid PRINTER_INFO_5 as * we've seen network printers not show up. * On 9x-class systems, PRINTER_INFO_4 isn't available and * PRINTER_INFO_5 is recommended. * Bletch. */ if (osVersion.dwPlatformId != VER_PLATFORM_WIN32_NT) { ret->enum_level = 5; } else { ret->enum_level = 4; } if (!printer_add_enum(PRINTER_ENUM_LOCAL | PRINTER_ENUM_CONNECTIONS, ret->enum_level, &buffer, 0, nprinters_ptr)) goto error; switch (ret->enum_level) { case 4: ret->info.i4 = (LPPRINTER_INFO_4)buffer; break; case 5: ret->info.i5 = (LPPRINTER_INFO_5)buffer; |
︙ | ︙ |
Changes to windows/winproxy.c.
︙ | ︙ | |||
9 10 11 12 13 14 15 | #define DEFINE_PLUG_METHOD_MACROS #include "tree234.h" #include "putty.h" #include "network.h" #include "proxy.h" | < | < < < | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | < < < < < < < < < < < < < < | | > > > < < < < < > | > | > > > < | > > < < | < < < < | < < < < < | 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 | #define DEFINE_PLUG_METHOD_MACROS #include "tree234.h" #include "putty.h" #include "network.h" #include "proxy.h" Socket make_handle_socket(HANDLE send_H, HANDLE recv_H, Plug plug, int overlapped); Socket platform_new_connection(SockAddr addr, char *hostname, int port, int privport, int oobinline, int nodelay, int keepalive, Plug plug, Conf *conf) { char *cmd; HANDLE us_to_cmd, us_from_cmd, cmd_to_us, cmd_from_us; SECURITY_ATTRIBUTES sa; STARTUPINFO si; PROCESS_INFORMATION pi; if (conf_get_int(conf, CONF_proxy_type) != PROXY_CMD) return NULL; cmd = format_telnet_command(addr, port, conf); /* We are responsible for this and don't need it any more */ sk_addr_free(addr); { char *msg = dupprintf("Starting local proxy command: %s", cmd); /* We're allowed to pass NULL here, because we're part of the Windows * front end so we know logevent doesn't expect any data. */ logevent(NULL, msg); sfree(msg); } /* * Create the pipes to the proxy command, and spawn the proxy * command process. */ sa.nLength = sizeof(sa); sa.lpSecurityDescriptor = NULL; /* default */ sa.bInheritHandle = TRUE; if (!CreatePipe(&us_from_cmd, &cmd_to_us, &sa, 0)) { Socket ret = new_error_socket("Unable to create pipes for proxy command", plug); sfree(cmd); return ret; } if (!CreatePipe(&cmd_from_us, &us_to_cmd, &sa, 0)) { Socket ret = new_error_socket("Unable to create pipes for proxy command", plug); sfree(cmd); CloseHandle(us_from_cmd); CloseHandle(cmd_to_us); return ret; } SetHandleInformation(us_to_cmd, HANDLE_FLAG_INHERIT, 0); SetHandleInformation(us_from_cmd, HANDLE_FLAG_INHERIT, 0); si.cb = sizeof(si); si.lpReserved = NULL; si.lpDesktop = NULL; si.lpTitle = NULL; si.dwFlags = STARTF_USESTDHANDLES; si.cbReserved2 = 0; si.lpReserved2 = NULL; si.hStdInput = cmd_from_us; si.hStdOutput = cmd_to_us; si.hStdError = NULL; CreateProcess(NULL, cmd, NULL, NULL, TRUE, CREATE_NO_WINDOW | NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi); CloseHandle(pi.hProcess); CloseHandle(pi.hThread); sfree(cmd); CloseHandle(cmd_from_us); CloseHandle(cmd_to_us); return make_handle_socket(us_to_cmd, us_from_cmd, plug, FALSE); } |
Added windows/winsecur.c.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 | /* * winsecur.c: implementation of winsecur.h. */ #include <stdio.h> #include <stdlib.h> #include "putty.h" #if !defined NO_SECURITY #define WINSECUR_GLOBAL #include "winsecur.h" int got_advapi(void) { static int attempted = FALSE; static int successful; static HMODULE advapi; if (!attempted) { attempted = TRUE; advapi = load_system32_dll("advapi32.dll"); successful = advapi && GET_WINDOWS_FUNCTION(advapi, GetSecurityInfo) && GET_WINDOWS_FUNCTION(advapi, OpenProcessToken) && GET_WINDOWS_FUNCTION(advapi, GetTokenInformation) && GET_WINDOWS_FUNCTION(advapi, InitializeSecurityDescriptor) && GET_WINDOWS_FUNCTION(advapi, SetSecurityDescriptorOwner) && GET_WINDOWS_FUNCTION(advapi, SetEntriesInAclA); } return successful; } int got_crypt(void) { static int attempted = FALSE; static int successful; static HMODULE crypt; if (!attempted) { attempted = TRUE; crypt = load_system32_dll("crypt32.dll"); successful = crypt && GET_WINDOWS_FUNCTION(crypt, CryptProtectMemory); } return successful; } PSID get_user_sid(void) { HANDLE proc = NULL, tok = NULL; TOKEN_USER *user = NULL; DWORD toklen, sidlen; PSID sid = NULL, ret = NULL; if (!got_advapi()) goto cleanup; if ((proc = OpenProcess(MAXIMUM_ALLOWED, FALSE, GetCurrentProcessId())) == NULL) goto cleanup; if (!p_OpenProcessToken(proc, TOKEN_QUERY, &tok)) goto cleanup; if (!p_GetTokenInformation(tok, TokenUser, NULL, 0, &toklen) && GetLastError() != ERROR_INSUFFICIENT_BUFFER) goto cleanup; if ((user = (TOKEN_USER *)LocalAlloc(LPTR, toklen)) == NULL) goto cleanup; if (!p_GetTokenInformation(tok, TokenUser, user, toklen, &toklen)) goto cleanup; sidlen = GetLengthSid(user->User.Sid); sid = (PSID)smalloc(sidlen); if (!CopySid(sidlen, sid, user->User.Sid)) goto cleanup; /* Success. Move sid into the return value slot, and null it out * to stop the cleanup code freeing it. */ ret = sid; sid = NULL; cleanup: if (proc != NULL) CloseHandle(proc); if (tok != NULL) CloseHandle(tok); if (user != NULL) LocalFree(user); if (sid != NULL) sfree(sid); return ret; } int make_private_security_descriptor(DWORD permissions, PSECURITY_DESCRIPTOR *psd, PSID *networksid, PACL *acl, char **error) { SID_IDENTIFIER_AUTHORITY nt_auth = SECURITY_NT_AUTHORITY; EXPLICIT_ACCESS ea[3]; int ret = FALSE; *psd = NULL; *networksid = NULL; *acl = NULL; *error = NULL; if (!got_advapi()) { *error = dupprintf("unable to load advapi32.dll"); goto cleanup; } if (!AllocateAndInitializeSid(&nt_auth, 1, SECURITY_NETWORK_RID, 0, 0, 0, 0, 0, 0, 0, networksid)) { *error = dupprintf("unable to construct SID for " "local same-user access only: %s", win_strerror(GetLastError())); goto cleanup; } memset(ea, 0, sizeof(ea)); ea[0].grfAccessPermissions = permissions; ea[0].grfAccessMode = REVOKE_ACCESS; ea[0].grfInheritance = NO_INHERITANCE; ea[0].Trustee.TrusteeForm = TRUSTEE_IS_NAME; ea[0].Trustee.ptstrName = "EVERYONE"; ea[1].grfAccessPermissions = permissions; ea[1].grfAccessMode = GRANT_ACCESS; ea[1].grfInheritance = NO_INHERITANCE; ea[1].Trustee.TrusteeForm = TRUSTEE_IS_NAME; ea[1].Trustee.ptstrName = "CURRENT_USER"; ea[2].grfAccessPermissions = permissions; ea[2].grfAccessMode = REVOKE_ACCESS; ea[2].grfInheritance = NO_INHERITANCE; ea[2].Trustee.TrusteeForm = TRUSTEE_IS_SID; ea[2].Trustee.ptstrName = (LPTSTR)*networksid; if (p_SetEntriesInAclA(2, ea, NULL, acl) != ERROR_SUCCESS || *acl == NULL) { *error = dupprintf("unable to construct ACL: %s", win_strerror(GetLastError())); goto cleanup; } *psd = (PSECURITY_DESCRIPTOR) LocalAlloc(LPTR, SECURITY_DESCRIPTOR_MIN_LENGTH); if (!*psd) { *error = dupprintf("unable to allocate security descriptor: %s", win_strerror(GetLastError())); goto cleanup; } if (!InitializeSecurityDescriptor(*psd, SECURITY_DESCRIPTOR_REVISION)) { *error = dupprintf("unable to initialise security descriptor: %s", win_strerror(GetLastError())); goto cleanup; } if (!SetSecurityDescriptorDacl(*psd, TRUE, *acl, FALSE)) { *error = dupprintf("unable to set DACL in security descriptor: %s", win_strerror(GetLastError())); goto cleanup; } ret = TRUE; cleanup: if (!ret) { if (*psd) { LocalFree(*psd); *psd = NULL; } if (*networksid) { LocalFree(*networksid); *networksid = NULL; } if (*acl) { LocalFree(*acl); *acl = NULL; } } else { sfree(*error); *error = NULL; } return ret; } #endif /* !defined NO_SECURITY */ |
Added windows/winsecur.h.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 | /* * winsecur.h: some miscellaneous security-related helper functions, * defined in winsecur.c, that use the advapi32 library. Also * centralises the machinery for dynamically loading that library. */ #if !defined NO_SECURITY #include <aclapi.h> #ifndef WINSECUR_GLOBAL #define WINSECUR_GLOBAL extern #endif /* * Functions loaded from advapi32.dll. */ DECL_WINDOWS_FUNCTION(WINSECUR_GLOBAL, BOOL, OpenProcessToken, (HANDLE, DWORD, PHANDLE)); DECL_WINDOWS_FUNCTION(WINSECUR_GLOBAL, BOOL, GetTokenInformation, (HANDLE, TOKEN_INFORMATION_CLASS, LPVOID, DWORD, PDWORD)); DECL_WINDOWS_FUNCTION(WINSECUR_GLOBAL, BOOL, InitializeSecurityDescriptor, (PSECURITY_DESCRIPTOR, DWORD)); DECL_WINDOWS_FUNCTION(WINSECUR_GLOBAL, BOOL, SetSecurityDescriptorOwner, (PSECURITY_DESCRIPTOR, PSID, BOOL)); DECL_WINDOWS_FUNCTION(WINSECUR_GLOBAL, DWORD, GetSecurityInfo, (HANDLE, SE_OBJECT_TYPE, SECURITY_INFORMATION, PSID *, PSID *, PACL *, PACL *, PSECURITY_DESCRIPTOR *)); DECL_WINDOWS_FUNCTION(WINSECUR_GLOBAL, DWORD, SetEntriesInAclA, (ULONG, PEXPLICIT_ACCESS, PACL, PACL *)); int got_advapi(void); /* * Functions loaded from crypt32.dll. */ DECL_WINDOWS_FUNCTION(WINSECUR_GLOBAL, BOOL, CryptProtectMemory, (LPVOID, DWORD, DWORD)); int got_crypt(void); /* * Find the SID describing the current user. The return value (if not * NULL for some error-related reason) is smalloced. */ PSID get_user_sid(void); /* * Construct a PSECURITY_DESCRIPTOR of the type used for named pipe * servers, i.e. allowing access only to the current user id and also * only local (i.e. not over SMB) connections. * * If this function returns TRUE, then 'psd', 'networksid' and 'acl' * will all have been filled in with memory allocated using LocalAlloc * (and hence must be freed later using LocalFree). If it returns * FALSE, then instead 'error' has been filled with a dynamically * allocated error message. */ int make_private_security_descriptor(DWORD permissions, PSECURITY_DESCRIPTOR *psd, PSID *networksid, PACL *acl, char **error); #endif |
Changes to windows/winser.c.
︙ | ︙ | |||
83 84 85 86 87 88 89 | connection_fatal(serial->frontend, "%s", error_msg); } else { serial->bufsize = new_backlog; } } | | | 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 | connection_fatal(serial->frontend, "%s", error_msg); } else { serial->bufsize = new_backlog; } } static const char *serial_configure(Serial serial, HANDLE serport, Conf *conf) { DCB dcb; COMMTIMEOUTS timeouts; /* * Set up the serial port parameters. If we can't even * GetCommState, we ignore the problem on the grounds that the |
︙ | ︙ | |||
117 118 119 120 121 122 123 | dcb.fAbortOnError = FALSE; dcb.fOutxCtsFlow = FALSE; dcb.fOutxDsrFlow = FALSE; /* * Configurable parameters. */ | | | | | | | | | 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 | dcb.fAbortOnError = FALSE; dcb.fOutxCtsFlow = FALSE; dcb.fOutxDsrFlow = FALSE; /* * Configurable parameters. */ dcb.BaudRate = conf_get_int(conf, CONF_serspeed); msg = dupprintf("Configuring baud rate %d", dcb.BaudRate); logevent(serial->frontend, msg); sfree(msg); dcb.ByteSize = conf_get_int(conf, CONF_serdatabits); msg = dupprintf("Configuring %d data bits", dcb.ByteSize); logevent(serial->frontend, msg); sfree(msg); switch (conf_get_int(conf, CONF_serstopbits)) { case 2: dcb.StopBits = ONESTOPBIT; str = "1"; break; case 3: dcb.StopBits = ONE5STOPBITS; str = "1.5"; break; case 4: dcb.StopBits = TWOSTOPBITS; str = "2"; break; default: return "Invalid number of stop bits (need 1, 1.5 or 2)"; } msg = dupprintf("Configuring %s data bits", str); logevent(serial->frontend, msg); sfree(msg); switch (conf_get_int(conf, CONF_serparity)) { case SER_PAR_NONE: dcb.Parity = NOPARITY; str = "no"; break; case SER_PAR_ODD: dcb.Parity = ODDPARITY; str = "odd"; break; case SER_PAR_EVEN: dcb.Parity = EVENPARITY; str = "even"; break; case SER_PAR_MARK: dcb.Parity = MARKPARITY; str = "mark"; break; case SER_PAR_SPACE: dcb.Parity = SPACEPARITY; str = "space"; break; } msg = dupprintf("Configuring %s parity", str); logevent(serial->frontend, msg); sfree(msg); switch (conf_get_int(conf, CONF_serflow)) { case SER_FLOW_NONE: str = "no"; break; case SER_FLOW_XONXOFF: dcb.fOutX = dcb.fInX = TRUE; str = "XON/XOFF"; break; |
︙ | ︙ | |||
195 196 197 198 199 200 201 | * * Returns an error message, or NULL on success. * * Also places the canonical host name into `realhost'. It must be * freed by the caller. */ static const char *serial_init(void *frontend_handle, void **backend_handle, | < | | > > | | 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 | * * Returns an error message, or NULL on success. * * Also places the canonical host name into `realhost'. It must be * freed by the caller. */ static const char *serial_init(void *frontend_handle, void **backend_handle, Conf *conf, char *host, int port, char **realhost, int nodelay, int keepalive) { Serial serial; HANDLE serport; const char *err; char *serline; serial = snew(struct serial_backend_data); serial->port = INVALID_HANDLE_VALUE; serial->out = serial->in = NULL; serial->bufsize = 0; serial->break_in_progress = FALSE; *backend_handle = serial; serial->frontend = frontend_handle; serline = conf_get_str(conf, CONF_serline); { char *msg = dupprintf("Opening serial device %s", serline); logevent(serial->frontend, msg); } { /* * Munge the string supplied by the user into a Windows filename. * |
︙ | ︙ | |||
242 243 244 245 246 247 248 | * 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 = | | < < | | | | | 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 | * 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(serline, '\\') ? "" : "\\\\.\\", 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"; err = serial_configure(serial, serport, conf); if (err) return err; serial->port = serport; serial->out = handle_output_new(serport, serial_sentdata, serial, HANDLE_FLAG_OVERLAPPED); serial->in = handle_input_new(serport, serial_gotdata, serial, HANDLE_FLAG_OVERLAPPED | HANDLE_FLAG_IGNOREEOF | HANDLE_FLAG_UNITBUFFER); *realhost = dupstr(serline); /* * Specials are always available. */ update_specials_menu(serial->frontend); return NULL; } static void serial_free(void *handle) { Serial serial = (Serial) handle; serial_terminate(serial); expire_timer_context(serial); sfree(serial); } static void serial_reconfig(void *handle, Conf *conf) { Serial serial = (Serial) handle; const char *err; err = serial_configure(serial, serial->port, conf); /* * FIXME: what should we do if err returns something? */ } /* |
︙ | ︙ | |||
328 329 330 331 332 333 334 | */ static void serial_size(void *handle, int width, int height) { /* Do nothing! */ return; } | | | | 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 | */ static void serial_size(void *handle, int width, int height) { /* Do nothing! */ return; } static void serbreak_timer(void *ctx, unsigned long now) { Serial serial = (Serial)ctx; if (now == serial->clearbreak_time && serial->port) { ClearCommBreak(serial->port); serial->break_in_progress = FALSE; logevent(serial->frontend, "Finished serial break"); } } /* |
︙ | ︙ |
Changes to windows/winsftp.c.
︙ | ︙ | |||
16 17 18 19 20 21 22 | int ret; ret = cmdline_get_passwd_input(p, in, inlen); if (ret == -1) ret = console_get_userpass_input(p, in, inlen); return ret; } | | | 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | int ret; ret = cmdline_get_passwd_input(p, in, inlen); if (ret == -1) ret = console_get_userpass_input(p, in, inlen); return ret; } void platform_get_x11_auth(struct X11Display *display, Conf *conf) { /* Do nothing, therefore no auth. */ } const int platform_uses_x11_unix_by_default = TRUE; /* ---------------------------------------------------------------------- * File access abstraction. |
︙ | ︙ | |||
84 85 86 87 88 89 90 | } while(0) struct RFile { HANDLE h; }; RFile *open_existing_file(char *name, uint64 *size, | | > | 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 | } while(0) struct RFile { HANDLE h; }; RFile *open_existing_file(char *name, uint64 *size, unsigned long *mtime, unsigned long *atime, long *perms) { HANDLE h; RFile *ret; h = CreateFile(name, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0); if (h == INVALID_HANDLE_VALUE) |
︙ | ︙ | |||
109 110 111 112 113 114 115 116 117 118 119 120 121 122 | GetFileTime(h, NULL, &actime, &wrtime); if (atime) TIME_WIN_TO_POSIX(actime, *atime); if (mtime) TIME_WIN_TO_POSIX(wrtime, *mtime); } return ret; } int read_from_file(RFile *f, void *buffer, int length) { int ret; DWORD read; | > > > | 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 | GetFileTime(h, NULL, &actime, &wrtime); if (atime) TIME_WIN_TO_POSIX(actime, *atime); if (mtime) TIME_WIN_TO_POSIX(wrtime, *mtime); } if (perms) *perms = -1; return ret; } int read_from_file(RFile *f, void *buffer, int length) { int ret; DWORD read; |
︙ | ︙ | |||
133 134 135 136 137 138 139 | sfree(f); } struct WFile { HANDLE h; }; | | | 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 | sfree(f); } struct WFile { HANDLE h; }; WFile *open_new_file(char *name, long perms) { HANDLE h; WFile *ret; h = CreateFile(name, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0); if (h == INVALID_HANDLE_VALUE) |
︙ | ︙ | |||
478 479 480 481 482 483 484 | return NULL; } extern int select_result(WPARAM, LPARAM); int do_eventsel_loop(HANDLE other_event) { int n, nhandles, nallhandles, netindex, otherindex; | | > | > > | > | > | > > | 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 | return NULL; } extern int select_result(WPARAM, LPARAM); int do_eventsel_loop(HANDLE other_event) { int n, nhandles, nallhandles, netindex, otherindex; unsigned long next, then; long ticks; HANDLE *handles; SOCKET *sklist; int skcount; unsigned long now = GETTICKCOUNT(); if (toplevel_callback_pending()) { ticks = 0; } else if (run_timers(now, &next)) { then = now; now = GETTICKCOUNT(); if (now - then > next - then) ticks = 0; else ticks = next - now; } else { ticks = INFINITE; } handles = handle_get_events(&nhandles); handles = sresize(handles, nhandles+2, HANDLE); nallhandles = nhandles; |
︙ | ︙ | |||
572 573 574 575 576 577 578 579 580 581 582 583 584 585 | } sfree(sklist); } sfree(handles); if (n == WAIT_TIMEOUT) { now = next; } else { now = GETTICKCOUNT(); } if (otherindex >= 0 && n == WAIT_OBJECT_0 + otherindex) | > > | 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 | } sfree(sklist); } sfree(handles); run_toplevel_callbacks(); if (n == WAIT_TIMEOUT) { now = next; } else { now = GETTICKCOUNT(); } if (otherindex >= 0 && n == WAIT_OBJECT_0 + otherindex) |
︙ | ︙ | |||
598 599 600 601 602 603 604 | * ssh_sftp_get_cmdline() using a parallel mechanism. */ int ssh_sftp_loop_iteration(void) { if (p_WSAEventSelect == NULL) { fd_set readfds; int ret; | | | > > | > | > | | 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 | * ssh_sftp_get_cmdline() using a parallel mechanism. */ int ssh_sftp_loop_iteration(void) { if (p_WSAEventSelect == NULL) { fd_set readfds; int ret; unsigned long now = GETTICKCOUNT(), then; if (sftp_ssh_socket == INVALID_SOCKET) return -1; /* doom */ if (socket_writable(sftp_ssh_socket)) select_result((WPARAM) sftp_ssh_socket, (LPARAM) FD_WRITE); do { unsigned long next; long ticks; struct timeval tv, *ptv; if (run_timers(now, &next)) { then = now; now = GETTICKCOUNT(); if (now - then > next - then) ticks = 0; else ticks = next - now; tv.tv_sec = ticks / 1000; tv.tv_usec = ticks % 1000 * 1000; ptv = &tv; } else { ptv = NULL; } |
︙ | ︙ |
Added windows/winshare.c.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 | /* * Windows implementation of SSH connection-sharing IPC setup. */ #include <stdio.h> #include <assert.h> #define DEFINE_PLUG_METHOD_MACROS #include "tree234.h" #include "putty.h" #include "network.h" #include "proxy.h" #include "ssh.h" #if !defined NO_SECURITY #include "winsecur.h" #define CONNSHARE_PIPE_PREFIX "\\\\.\\pipe\\putty-connshare" #define CONNSHARE_MUTEX_PREFIX "Local\\putty-connshare-mutex" static char *obfuscate_name(const char *realname) { /* * Windows's named pipes all live in the same namespace, so one * user can see what pipes another user has open. This is an * undesirable privacy leak and in particular permits one user to * know what username@host another user is SSHing to, so we * protect that information by using CryptProtectMemory (which * uses a key built in to each user's account). */ char *cryptdata; int cryptlen; SHA256_State sha; unsigned char lenbuf[4]; unsigned char digest[32]; char retbuf[65]; int i; cryptlen = strlen(realname) + 1; cryptlen += CRYPTPROTECTMEMORY_BLOCK_SIZE - 1; cryptlen /= CRYPTPROTECTMEMORY_BLOCK_SIZE; cryptlen *= CRYPTPROTECTMEMORY_BLOCK_SIZE; cryptdata = snewn(cryptlen, char); memset(cryptdata, 0, cryptlen); strcpy(cryptdata, realname); /* * CRYPTPROTECTMEMORY_CROSS_PROCESS causes CryptProtectMemory to * use the same key in all processes with this user id, meaning * that the next PuTTY process calling this function with the same * input will get the same data. * * (Contrast with CryptProtectData, which invents a new session * key every time since its API permits returning more data than * was input, so calling _that_ and hashing the output would not * be stable.) * * We don't worry too much if this doesn't work for some reason. * Omitting this step still has _some_ privacy value (in that * another user can test-hash things to confirm guesses as to * where you might be connecting to, but cannot invert SHA-256 in * the absence of any plausible guess). So we don't abort if we * can't call CryptProtectMemory at all, or if it fails. */ if (got_crypt()) p_CryptProtectMemory(cryptdata, cryptlen, CRYPTPROTECTMEMORY_CROSS_PROCESS); /* * We don't want to give away the length of the hostname either, * so having got it back out of CryptProtectMemory we now hash it. */ SHA256_Init(&sha); PUT_32BIT_MSB_FIRST(lenbuf, cryptlen); SHA256_Bytes(&sha, lenbuf, 4); SHA256_Bytes(&sha, cryptdata, cryptlen); SHA256_Final(&sha, digest); sfree(cryptdata); /* * Finally, make printable. */ for (i = 0; i < 32; i++) { sprintf(retbuf + 2*i, "%02x", digest[i]); /* the last of those will also write the trailing NUL */ } return dupstr(retbuf); } static char *make_name(const char *prefix, const char *name) { char *username, *retname; username = get_username(); retname = dupprintf("%s.%s.%s", prefix, username, name); sfree(username); return retname; } Socket new_named_pipe_client(const char *pipename, Plug plug); Socket new_named_pipe_listener(const char *pipename, Plug plug); int platform_ssh_share(const char *pi_name, Conf *conf, Plug downplug, Plug upplug, Socket *sock, char **logtext, char **ds_err, char **us_err, int can_upstream, int can_downstream) { char *name, *mutexname, *pipename; HANDLE mutex; Socket retsock; PSECURITY_DESCRIPTOR psd; PACL acl; PSID networksid; /* * Transform the platform-independent version of the connection * identifier into the obfuscated version we'll use for our * Windows named pipe and mutex. A side effect of doing this is * that it also eliminates any characters illegal in Windows pipe * names. */ name = obfuscate_name(pi_name); if (!name) { *logtext = dupprintf("Unable to call CryptProtectMemory: %s", win_strerror(GetLastError())); return SHARE_NONE; } /* * Make a mutex name out of the connection identifier, and lock it * while we decide whether to be upstream or downstream. */ { SECURITY_ATTRIBUTES sa; mutexname = make_name(CONNSHARE_MUTEX_PREFIX, name); if (!make_private_security_descriptor(MUTEX_ALL_ACCESS, &psd, &networksid, &acl, logtext)) { sfree(mutexname); return SHARE_NONE; } memset(&sa, 0, sizeof(sa)); sa.nLength = sizeof(sa); sa.lpSecurityDescriptor = psd; sa.bInheritHandle = FALSE; mutex = CreateMutex(&sa, FALSE, mutexname); if (!mutex) { *logtext = dupprintf("CreateMutex(\"%s\") failed: %s", mutexname, win_strerror(GetLastError())); sfree(mutexname); LocalFree(psd); LocalFree(networksid); LocalFree(acl); return SHARE_NONE; } sfree(mutexname); LocalFree(psd); LocalFree(networksid); LocalFree(acl); WaitForSingleObject(mutex, INFINITE); } pipename = make_name(CONNSHARE_PIPE_PREFIX, name); *logtext = NULL; if (can_downstream) { retsock = new_named_pipe_client(pipename, downplug); if (sk_socket_error(retsock) == NULL) { sfree(*logtext); *logtext = pipename; *sock = retsock; sfree(name); ReleaseMutex(mutex); CloseHandle(mutex); return SHARE_DOWNSTREAM; } sfree(*ds_err); *ds_err = dupprintf("%s: %s", pipename, sk_socket_error(retsock)); sk_close(retsock); } if (can_upstream) { retsock = new_named_pipe_listener(pipename, upplug); if (sk_socket_error(retsock) == NULL) { sfree(*logtext); *logtext = pipename; *sock = retsock; sfree(name); ReleaseMutex(mutex); CloseHandle(mutex); return SHARE_UPSTREAM; } sfree(*us_err); *us_err = dupprintf("%s: %s", pipename, sk_socket_error(retsock)); sk_close(retsock); } /* One of the above clauses ought to have happened. */ assert(*logtext || *ds_err || *us_err); sfree(pipename); sfree(name); ReleaseMutex(mutex); CloseHandle(mutex); return SHARE_NONE; } void platform_ssh_share_cleanup(const char *name) { } #else /* !defined NO_SECURITY */ #include "noshare.c" #endif /* !defined NO_SECURITY */ |
Changes to windows/winstore.c.
︙ | ︙ | |||
146 147 148 149 150 151 152 | } sfree(p); return (void *) sesskey; } | | | | > > > | > > > > > > | | > > > | | | > | > | > | > | | > > > > | | > > > > | | | | | > > > > | > | | | | | | | > > > > > > | | | 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 | } sfree(p); return (void *) sesskey; } char *read_setting_s(void *handle, const char *key) { DWORD type, size; char *ret; if (!handle) return NULL; /* Find out the type and size of the data. */ if (RegQueryValueEx((HKEY) handle, key, 0, &type, NULL, &size) != ERROR_SUCCESS || type != REG_SZ) return NULL; ret = snewn(size+1, char); if (RegQueryValueEx((HKEY) handle, key, 0, &type, ret, &size) != ERROR_SUCCESS || type != REG_SZ) { sfree(ret); return NULL; } return ret; } int read_setting_i(void *handle, const char *key, int defvalue) { DWORD type, val, size; size = sizeof(val); if (!handle || RegQueryValueEx((HKEY) handle, key, 0, &type, (BYTE *) &val, &size) != ERROR_SUCCESS || size != sizeof(val) || type != REG_DWORD) return defvalue; else return val; } FontSpec *read_setting_fontspec(void *handle, const char *name) { char *settingname; char *fontname; FontSpec *ret; int isbold, height, charset; fontname = read_setting_s(handle, name); if (!fontname) return NULL; settingname = dupcat(name, "IsBold", NULL); isbold = read_setting_i(handle, settingname, -1); sfree(settingname); if (isbold == -1) { sfree(fontname); return NULL; } settingname = dupcat(name, "CharSet", NULL); charset = read_setting_i(handle, settingname, -1); sfree(settingname); if (charset == -1) { sfree(fontname); return NULL; } settingname = dupcat(name, "Height", NULL); height = read_setting_i(handle, settingname, INT_MIN); sfree(settingname); if (height == INT_MIN) { sfree(fontname); return NULL; } ret = fontspec_new(fontname, isbold, height, charset); sfree(fontname); return ret; } void write_setting_fontspec(void *handle, const char *name, FontSpec *font) { char *settingname; write_setting_s(handle, name, font->name); settingname = dupcat(name, "IsBold", NULL); write_setting_i(handle, settingname, font->isbold); sfree(settingname); settingname = dupcat(name, "CharSet", NULL); write_setting_i(handle, settingname, font->charset); sfree(settingname); settingname = dupcat(name, "Height", NULL); write_setting_i(handle, settingname, font->height); sfree(settingname); } Filename *read_setting_filename(void *handle, const char *name) { char *tmp = read_setting_s(handle, name); if (tmp) { Filename *ret = filename_from_str(tmp); sfree(tmp); return ret; } else return NULL; } void write_setting_filename(void *handle, const char *name, Filename *result) { write_setting_s(handle, name, result->path); } void close_settings_r(void *handle) { RegCloseKey((HKEY) handle); } |
︙ | ︙ | |||
316 317 318 319 320 321 322 | len = 1 + strlen(key); /* * Now read a saved key in from the registry and see what it * says. */ | < | > | > > | 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 | len = 1 + strlen(key); /* * Now read a saved key in from the registry and see what it * says. */ regname = snewn(3 * (strlen(hostname) + strlen(keytype)) + 15, char); hostkey_regname(regname, hostname, port, keytype); if (RegOpenKey(HKEY_CURRENT_USER, PUTTY_REG_POS "\\SshHostKeys", &rkey) != ERROR_SUCCESS) { sfree(regname); return 1; /* key does not exist in registry */ } readlen = len; otherstr = snewn(len, char); ret = RegQueryValueEx(rkey, regname, NULL, &type, otherstr, &readlen); if (ret != ERROR_SUCCESS && ret != ERROR_MORE_DATA && !strcmp(keytype, "rsa")) { /* * Key didn't exist. If the key type is RSA, we'll try * another trick, which is to look up the _old_ key format |
︙ | ︙ | |||
388 389 390 391 392 393 394 395 396 397 398 399 400 401 | * format. If not, we'll assume something odd went * wrong, and hyper-cautiously do nothing. */ if (!strcmp(otherstr, key)) RegSetValueEx(rkey, regname, 0, REG_SZ, otherstr, strlen(otherstr) + 1); } } RegCloseKey(rkey); compare = strcmp(otherstr, key); sfree(otherstr); | > > | 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 | * format. If not, we'll assume something odd went * wrong, and hyper-cautiously do nothing. */ if (!strcmp(otherstr, key)) RegSetValueEx(rkey, regname, 0, REG_SZ, otherstr, strlen(otherstr) + 1); } sfree(oldstyle); } RegCloseKey(rkey); compare = strcmp(otherstr, key); sfree(otherstr); |
︙ | ︙ | |||
432 433 434 435 436 437 438 | /* * Open (or delete) the random seed file. */ enum { DEL, OPEN_R, OPEN_W }; static int try_random_seed(char const *path, int action, HANDLE *ret) { if (action == DEL) { | | > > > | 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 | /* * Open (or delete) the random seed file. */ enum { DEL, OPEN_R, OPEN_W }; static int try_random_seed(char const *path, int action, HANDLE *ret) { if (action == DEL) { if (!DeleteFile(path) && GetLastError() != ERROR_FILE_NOT_FOUND) { nonfatal("Unable to delete '%s': %s", path, win_strerror(GetLastError())); } *ret = INVALID_HANDLE_VALUE; return FALSE; /* so we'll do the next ones too */ } *ret = CreateFile(path, action == OPEN_W ? GENERIC_WRITE : GENERIC_READ, action == OPEN_W ? 0 : (FILE_SHARE_READ | |
︙ | ︙ | |||
703 704 705 706 707 708 709 | old_value = new_value; } else ret = ERROR_SUCCESS; /* * Either return or free the result. */ | | | 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 | old_value = new_value; } else ret = ERROR_SUCCESS; /* * Either return or free the result. */ if (out && ret == ERROR_SUCCESS) *out = old_value; else sfree(old_value); /* Clean up and return. */ RegCloseKey(pjumplist_key); |
︙ | ︙ | |||
736 737 738 739 740 741 742 | /* Returns the jumplist entries from the registry. Caller must free * the returned pointer. */ char *get_jumplist_registry_entries (void) { char *list_value; | | | 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 | /* Returns the jumplist entries from the registry. Caller must free * the returned pointer. */ char *get_jumplist_registry_entries (void) { char *list_value; if (transform_jumplist_registry(NULL,NULL,&list_value) != JUMPLISTREG_OK) { list_value = snewn(2, char); *list_value = '\0'; *(list_value + 1) = '\0'; } return list_value; } |
︙ | ︙ |
Changes to windows/winstuff.h.
︙ | ︙ | |||
12 13 14 15 16 17 18 | #include <stdio.h> /* for FILENAME_MAX */ #include "tree234.h" #include "winhelp.h" struct Filename { | | | | > > | 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | #include <stdio.h> /* for FILENAME_MAX */ #include "tree234.h" #include "winhelp.h" struct Filename { char *path; }; #define f_open(filename, mode, isprivate) ( fopen((filename)->path, (mode)) ) struct FontSpec { char *name; int isbold; int height; int charset; }; struct FontSpec *fontspec_new(const char *name, int bold, int height, int charset); #ifndef CLEARTYPE_QUALITY #define CLEARTYPE_QUALITY 5 #endif #define FONT_QUALITY(fq) ( \ (fq) == FQ_DEFAULT ? DEFAULT_QUALITY : \ (fq) == FQ_ANTIALIASED ? ANTIALIASED_QUALITY : \ |
︙ | ︙ | |||
69 70 71 72 73 74 75 76 77 78 79 80 81 82 | #define LONG_PTR LONG #endif #define BOXFLAGS DLGWINDOWEXTRA #define BOXRESULT (DLGWINDOWEXTRA + sizeof(LONG_PTR)) #define DF_END 0x0001 /* * Dynamically linked functions. These come in two flavours: * * - GET_WINDOWS_FUNCTION does not expose "name" to the preprocessor, * so will always dynamically link against exactly what is specified * in "name". If you're not sure, use this one. * | > > > > | 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 | #define LONG_PTR LONG #endif #define BOXFLAGS DLGWINDOWEXTRA #define BOXRESULT (DLGWINDOWEXTRA + sizeof(LONG_PTR)) #define DF_END 0x0001 #ifndef NO_SECUREZEROMEMORY #define PLATFORM_HAS_SMEMCLR /* inhibit cross-platform one in misc.c */ #endif /* * Dynamically linked functions. These come in two flavours: * * - GET_WINDOWS_FUNCTION does not expose "name" to the preprocessor, * so will always dynamically link against exactly what is specified * in "name". If you're not sure, use this one. * |
︙ | ︙ | |||
111 112 113 114 115 116 117 | #else #define GLOBAL extern #endif #endif #ifndef DONE_TYPEDEFS #define DONE_TYPEDEFS | | | 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 | #else #define GLOBAL extern #endif #endif #ifndef DONE_TYPEDEFS #define DONE_TYPEDEFS typedef struct conf_tag Conf; typedef struct backend_tag Backend; typedef struct terminal_tag Terminal; #endif #define PUTTY_REG_POS "Software\\SimonTatham\\PuTTY" #define PUTTY_REG_PARENT "Software\\SimonTatham" #define PUTTY_REG_PARENT_CHILD "PuTTY" |
︙ | ︙ | |||
139 140 141 142 143 144 145 146 147 148 149 150 151 152 | #define PUTTY_HELP_CONTENTS "putty.cnt" #define GETTICKCOUNT GetTickCount #define CURSORBLINK GetCaretBlinkTime() #define TICKSPERSEC 1000 /* GetTickCount returns milliseconds */ #define DEFAULT_CODEPAGE CP_ACP typedef HDC Context; typedef unsigned int uint32; /* int is 32-bits on Win32 and Win64. */ #define PUTTY_UINT32_DEFINED #ifndef NO_GSSAPI | > | 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 | #define PUTTY_HELP_CONTENTS "putty.cnt" #define GETTICKCOUNT GetTickCount #define CURSORBLINK GetCaretBlinkTime() #define TICKSPERSEC 1000 /* GetTickCount returns milliseconds */ #define DEFAULT_CODEPAGE CP_ACP #define USES_VTLINE_HACK typedef HDC Context; typedef unsigned int uint32; /* int is 32-bits on Win32 and Win64. */ #define PUTTY_UINT32_DEFINED #ifndef NO_GSSAPI |
︙ | ︙ | |||
230 231 232 233 234 235 236 | "All Files (*.*)\0*\0\0\0") #define FILTER_WAVE_FILES ("Wave Files (*.wav)\0*.WAV\0" \ "All Files (*.*)\0*\0\0\0") #define FILTER_DYNLIB_FILES ("Dynamic Library Files (*.dll)\0*.dll\0" \ "All Files (*.*)\0*\0\0\0") /* | | < < < < < > | 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 | "All Files (*.*)\0*\0\0\0") #define FILTER_WAVE_FILES ("Wave Files (*.wav)\0*.WAV\0" \ "All Files (*.*)\0*\0\0\0") #define FILTER_DYNLIB_FILES ("Dynamic Library Files (*.dll)\0*.dll\0" \ "All Files (*.*)\0*\0\0\0") /* * Exports from winnet.c. */ extern int select_result(WPARAM, LPARAM); /* * winnet.c dynamically loads WinSock 2 or WinSock 1 depending on * what it can get, which means any WinSock routines used outside * that module must be exported from it as function pointers. So * here they are. */ |
︙ | ︙ | |||
281 282 283 284 285 286 287 288 289 290 291 292 293 294 | * Exports from winutils.c. */ typedef struct filereq_tag filereq; /* cwd for file requester */ BOOL request_file(filereq *state, OPENFILENAME *of, int preserve, int save); filereq *filereq_new(void); void filereq_free(filereq *state); int message_box(LPCTSTR text, LPCTSTR caption, DWORD style, DWORD helpctxid); void split_into_argv(char *, int *, char ***, char ***); /* * Private structure for prefslist state. Only in the header file * so that we can delegate allocation to callers. */ struct prefslist { | > | 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 | * Exports from winutils.c. */ typedef struct filereq_tag filereq; /* cwd for file requester */ BOOL request_file(filereq *state, OPENFILENAME *of, int preserve, int save); filereq *filereq_new(void); void filereq_free(filereq *state); int message_box(LPCTSTR text, LPCTSTR caption, DWORD style, DWORD helpctxid); char *GetDlgItemText_alloc(HWND hwnd, int id); void split_into_argv(char *, int *, char ***, char ***); /* * Private structure for prefslist state. Only in the header file * so that we can delegate allocation to callers. */ struct prefslist { |
︙ | ︙ | |||
458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 | /* * Exports from winmisc.c. */ extern OSVERSIONINFO osVersion; BOOL init_winver(void); HMODULE load_system32_dll(const char *libname); /* * Exports from sizetip.c. */ void UpdateSizeTip(HWND src, int cx, int cy); void EnableSizeTip(int bEnable); /* * Exports from unicode.c. */ struct unicode_data; | > | > > > < < < < < < < < | 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 | /* * Exports from winmisc.c. */ extern OSVERSIONINFO osVersion; BOOL init_winver(void); HMODULE load_system32_dll(const char *libname); const char *win_strerror(int error); /* * Exports from sizetip.c. */ void UpdateSizeTip(HWND src, int cx, int cy); void EnableSizeTip(int bEnable); /* * Exports from unicode.c. */ struct unicode_data; void init_ucs(Conf *, struct unicode_data *); /* * Exports from winhandl.c. */ #define HANDLE_FLAG_OVERLAPPED 1 #define HANDLE_FLAG_IGNOREEOF 2 #define HANDLE_FLAG_UNITBUFFER 4 struct handle; typedef int (*handle_inputfn_t)(struct handle *h, void *data, int len); typedef void (*handle_outputfn_t)(struct handle *h, int new_backlog); struct handle *handle_input_new(HANDLE handle, handle_inputfn_t gotdata, void *privdata, int flags); struct handle *handle_output_new(HANDLE handle, handle_outputfn_t sentdata, void *privdata, int flags); int handle_write(struct handle *h, const void *data, int len); void handle_write_eof(struct handle *h); HANDLE *handle_get_events(int *nevents); void handle_free(struct handle *h); void handle_got_event(HANDLE event); void handle_unthrottle(struct handle *h, int backlog); int handle_backlog(struct handle *h); void *handle_get_privdata(struct handle *h); struct handle *handle_add_foreign_event(HANDLE event, void (*callback)(void *), void *ctx); /* * winpgntc.c needs to schedule callbacks for asynchronous agent * requests. This has to be done differently in GUI and console, so * there's an exported function used for the purpose. * * Also, we supply FLAG_SYNCAGENT to force agent requests to be * synchronous in pscp and psftp. */ void agent_schedule_callback(void (*callback)(void *, void *, int), void *callback_ctx, void *data, int len); #define FLAG_SYNCAGENT 0x1000 /* * Exports from winser.c. */ extern Backend serial_backend; /* * Exports from winjump.c. |
︙ | ︙ |
Changes to windows/winucs.c.
︙ | ︙ | |||
386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 | char *name; int codepage; int cp_size; const wchar_t *cp_table; }; static const struct cp_list_item cp_list[] = { {"ISO-8859-1:1998 (Latin-1, West Europe)", 0, 96, iso_8859_1}, {"ISO-8859-2:1999 (Latin-2, East Europe)", 0, 96, iso_8859_2}, {"ISO-8859-3:1999 (Latin-3, South Europe)", 0, 96, iso_8859_3}, {"ISO-8859-4:1998 (Latin-4, North Europe)", 0, 96, iso_8859_4}, {"ISO-8859-5:1999 (Latin/Cyrillic)", 0, 96, iso_8859_5}, {"ISO-8859-6:1999 (Latin/Arabic)", 0, 96, iso_8859_6}, {"ISO-8859-7:1987 (Latin/Greek)", 0, 96, iso_8859_7}, {"ISO-8859-8:1999 (Latin/Hebrew)", 0, 96, iso_8859_8}, {"ISO-8859-9:1999 (Latin-5, Turkish)", 0, 96, iso_8859_9}, {"ISO-8859-10:1998 (Latin-6, Nordic)", 0, 96, iso_8859_10}, {"ISO-8859-11:2001 (Latin/Thai)", 0, 96, iso_8859_11}, {"ISO-8859-13:1998 (Latin-7, Baltic)", 0, 96, iso_8859_13}, {"ISO-8859-14:1998 (Latin-8, Celtic)", 0, 96, iso_8859_14}, {"ISO-8859-15:1999 (Latin-9, \"euro\")", 0, 96, iso_8859_15}, {"ISO-8859-16:2001 (Latin-10, Balkan)", 0, 96, iso_8859_16}, | > > < < > | > | > > | | | | | 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 | char *name; int codepage; int cp_size; const wchar_t *cp_table; }; static const struct cp_list_item cp_list[] = { {"UTF-8", CP_UTF8}, {"ISO-8859-1:1998 (Latin-1, West Europe)", 0, 96, iso_8859_1}, {"ISO-8859-2:1999 (Latin-2, East Europe)", 0, 96, iso_8859_2}, {"ISO-8859-3:1999 (Latin-3, South Europe)", 0, 96, iso_8859_3}, {"ISO-8859-4:1998 (Latin-4, North Europe)", 0, 96, iso_8859_4}, {"ISO-8859-5:1999 (Latin/Cyrillic)", 0, 96, iso_8859_5}, {"ISO-8859-6:1999 (Latin/Arabic)", 0, 96, iso_8859_6}, {"ISO-8859-7:1987 (Latin/Greek)", 0, 96, iso_8859_7}, {"ISO-8859-8:1999 (Latin/Hebrew)", 0, 96, iso_8859_8}, {"ISO-8859-9:1999 (Latin-5, Turkish)", 0, 96, iso_8859_9}, {"ISO-8859-10:1998 (Latin-6, Nordic)", 0, 96, iso_8859_10}, {"ISO-8859-11:2001 (Latin/Thai)", 0, 96, iso_8859_11}, {"ISO-8859-13:1998 (Latin-7, Baltic)", 0, 96, iso_8859_13}, {"ISO-8859-14:1998 (Latin-8, Celtic)", 0, 96, iso_8859_14}, {"ISO-8859-15:1999 (Latin-9, \"euro\")", 0, 96, iso_8859_15}, {"ISO-8859-16:2001 (Latin-10, Balkan)", 0, 96, iso_8859_16}, {"KOI8-U", 0, 128, koi8_u}, {"KOI8-R", 20866}, {"HP-ROMAN8", 0, 96, roman8}, {"VSCII", 0, 256, vscii}, {"DEC-MCS", 0, 96, dec_mcs}, {"Win1250 (Central European)", 1250}, {"Win1251 (Cyrillic)", 1251}, {"Win1252 (Western)", 1252}, {"Win1253 (Greek)", 1253}, {"Win1254 (Turkish)", 1254}, {"Win1255 (Hebrew)", 1255}, {"Win1256 (Arabic)", 1256}, {"Win1257 (Baltic)", 1257}, {"Win1258 (Vietnamese)", 1258}, {"CP437", 437}, {"CP620 (Mazovia)", 0, 128, mazovia}, {"CP819", 28591}, {"CP852", 852}, {"CP878", 20866}, {"Use font encoding", -1}, {0, 0} }; static void link_font(WCHAR * line_tbl, WCHAR * font_tbl, WCHAR attr); void init_ucs(Conf *conf, struct unicode_data *ucsdata) { int i, j; int used_dtf = 0; char tbuf[256]; int vtmode; for (i = 0; i < 256; i++) tbuf[i] = i; /* Decide on the Line and Font codepages */ ucsdata->line_codepage = decode_codepage(conf_get_str(conf, CONF_line_codepage)); if (ucsdata->font_codepage <= 0) { ucsdata->font_codepage=0; ucsdata->dbcs_screenfont=0; } vtmode = conf_get_int(conf, CONF_vtmode); if (vtmode == VT_OEMONLY) { ucsdata->font_codepage = 437; ucsdata->dbcs_screenfont = 0; if (ucsdata->line_codepage <= 0) ucsdata->line_codepage = GetACP(); } else if (ucsdata->line_codepage <= 0) ucsdata->line_codepage = ucsdata->font_codepage; /* Collect screen font ucs table */ if (ucsdata->dbcs_screenfont || ucsdata->font_codepage == 0) { get_unitab(ucsdata->font_codepage, ucsdata->unitab_font, 2); for (i = 128; i < 256; i++) ucsdata->unitab_font[i] = (WCHAR) (CSET_ACP + i); } else { get_unitab(ucsdata->font_codepage, ucsdata->unitab_font, 1); /* CP437 fonts are often broken ... */ if (ucsdata->font_codepage == 437) ucsdata->unitab_font[0] = ucsdata->unitab_font[255] = 0xFFFF; } if (vtmode == VT_XWINDOWS) memcpy(ucsdata->unitab_font + 1, unitab_xterm_std, sizeof(unitab_xterm_std)); /* Collect OEMCP ucs table */ get_unitab(CP_OEMCP, ucsdata->unitab_oemcp, 1); /* Collect CP437 ucs table for SCO acs */ if (vtmode == VT_OEMANSI || vtmode == VT_XWINDOWS) memcpy(ucsdata->unitab_scoacs, ucsdata->unitab_oemcp, sizeof(ucsdata->unitab_scoacs)); else get_unitab(437, ucsdata->unitab_scoacs, 1); /* Collect line set ucs table */ if (ucsdata->line_codepage == ucsdata->font_codepage && (ucsdata->dbcs_screenfont || vtmode == VT_POORMAN || ucsdata->font_codepage==0)) { /* For DBCS and POOR fonts force direct to font */ used_dtf = 1; for (i = 0; i < 32; i++) ucsdata->unitab_line[i] = (WCHAR) i; for (i = 32; i < 256; i++) ucsdata->unitab_line[i] = (WCHAR) (CSET_ACP + i); |
︙ | ︙ | |||
556 557 558 559 560 561 562 | || (ucsdata->unitab_line[i] >= 0x7F && ucsdata->unitab_line[i] < 0xA0)) ucsdata->unitab_ctrl[i] = i; else ucsdata->unitab_ctrl[i] = 0xFF; /* Generate line->screen direct conversion links. */ | | | | | 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 | || (ucsdata->unitab_line[i] >= 0x7F && ucsdata->unitab_line[i] < 0xA0)) ucsdata->unitab_ctrl[i] = i; else ucsdata->unitab_ctrl[i] = 0xFF; /* Generate line->screen direct conversion links. */ if (vtmode == VT_OEMANSI || vtmode == VT_XWINDOWS) link_font(ucsdata->unitab_scoacs, ucsdata->unitab_oemcp, CSET_OEMCP); link_font(ucsdata->unitab_line, ucsdata->unitab_font, CSET_ACP); link_font(ucsdata->unitab_scoacs, ucsdata->unitab_font, CSET_ACP); link_font(ucsdata->unitab_xterm, ucsdata->unitab_font, CSET_ACP); if (vtmode == VT_OEMANSI || vtmode == VT_XWINDOWS) { link_font(ucsdata->unitab_line, ucsdata->unitab_oemcp, CSET_OEMCP); link_font(ucsdata->unitab_xterm, ucsdata->unitab_oemcp, CSET_OEMCP); } if (ucsdata->dbcs_screenfont && ucsdata->font_codepage != ucsdata->line_codepage) { /* F***ing Microsoft fonts, Japanese and Korean codepage fonts * have a currency symbol at 0x5C but their unicode value is * still given as U+005C not the correct U+00A5. */ ucsdata->unitab_line['\\'] = CSET_OEMCP + '\\'; } /* Last chance, if !unicode then try poorman links. */ if (vtmode != VT_UNICODE) { static const char poorman_scoacs[] = "CueaaaaceeeiiiAAE**ooouuyOUc$YPsaiounNao?++**!<>###||||++||++++++--|-+||++--|-+----++++++++##||#aBTPEsyt******EN=+><++-=... n2* "; static const char poorman_latin1[] = " !cL.Y|S\"Ca<--R~o+23'u|.,1o>///?AAAAAAACEEEEIIIIDNOOOOOxOUUUUYPBaaaaaaaceeeeiiiionooooo/ouuuuypy"; static const char poorman_vt100[] = "*#****o~**+++++-----++++|****L."; for (i = 160; i < 256; i++) |
︙ | ︙ | |||
1008 1009 1010 1011 1012 1013 1014 | int decode_codepage(char *cp_name) { char *s, *d; const struct cp_list_item *cpi; int codepage = -1; CPINFO cpinfo; | | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | | < < < | | | | | | | | | | | | | | | | | | | | | | | | | < | | > | | > | | | | | | | | | | | < | 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 | int decode_codepage(char *cp_name) { char *s, *d; const struct cp_list_item *cpi; int codepage = -1; CPINFO cpinfo; if (!cp_name || !*cp_name) return CP_UTF8; /* default */ for (cpi = cp_list; cpi->name; cpi++) { s = cp_name; d = cpi->name; for (;;) { while (*s && !isalnum(*s) && *s != ':') s++; while (*d && !isalnum(*d) && *d != ':') d++; if (*s == 0) { codepage = cpi->codepage; if (codepage == CP_UTF8) goto break_break; if (codepage == -1) return codepage; if (codepage == 0) { codepage = 65536 + (cpi - cp_list); goto break_break; } if (GetCPInfo(codepage, &cpinfo) != 0) goto break_break; } if (tolower((unsigned char)*s++) != tolower((unsigned char)*d++)) break; } } d = cp_name; if (tolower((unsigned char)d[0]) == 'c' && tolower((unsigned char)d[1]) == 'p') d += 2; if (tolower((unsigned char)d[0]) == 'i' && tolower((unsigned char)d[1]) == 'b' && tolower((unsigned char)d[2]) == 'm') d += 3; for (s = d; *s >= '0' && *s <= '9'; s++); if (*s == 0 && s != d) codepage = atoi(d); /* CP999 or IBM999 */ if (codepage == CP_ACP) codepage = GetACP(); if (codepage == CP_OEMCP) codepage = GetOEMCP(); if (codepage > 65535) codepage = -2; break_break:; if (codepage != -1) { if (codepage != CP_UTF8 && codepage < 65536) { if (GetCPInfo(codepage, &cpinfo) == 0) { codepage = -2; } else if (cpinfo.MaxCharSize > 1) |
︙ | ︙ | |||
1197 1198 1199 1200 1201 1202 1203 | for (i = 0; i < max; i++) unitab[i] = i; for (i = j; i < max; i++) unitab[i] = cp_list[codepage & 0xFFFF].cp_table[i - j]; } } | | | 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 | for (i = 0; i < max; i++) unitab[i] = i; for (i = j; i < max; i++) unitab[i] = cp_list[codepage & 0xFFFF].cp_table[i - j]; } } int wc_to_mb(int codepage, int flags, const wchar_t *wcstr, int wclen, char *mbstr, int mblen, char *defchr, int *defused, struct unicode_data *ucsdata) { char *p; int i; if (ucsdata && codepage == ucsdata->line_codepage && ucsdata->uni_tbl) { /* Do this by array lookup if we can. */ |
︙ | ︙ | |||
1235 1236 1237 1238 1239 1240 1241 | } return p - mbstr; } else return WideCharToMultiByte(codepage, flags, wcstr, wclen, mbstr, mblen, defchr, defused); } | | | 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 | } return p - mbstr; } else return WideCharToMultiByte(codepage, flags, wcstr, wclen, mbstr, mblen, defchr, defused); } int mb_to_wc(int codepage, int flags, const char *mbstr, int mblen, wchar_t *wcstr, int wclen) { return MultiByteToWideChar(codepage, flags, mbstr, mblen, wcstr, wclen); } int is_dbcs_leadbyte(int codepage, char byte) { return IsDBCSLeadByteEx(codepage, byte); } |
Changes to windows/winutils.c.
︙ | ︙ | |||
147 148 149 150 151 152 153 | "PuTTY Master Key (DSA), 1024-bit:\n" " " PGP_DSA_MASTER_KEY_FP, "PGP fingerprints", MB_ICONINFORMATION | MB_OK, HELPCTXID(pgp_fingerprints)); } /* | > > > > > > > > > > > > > > > > > > > | | | | > | < > > > > > > > > > | > | 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 | "PuTTY Master Key (DSA), 1024-bit:\n" " " PGP_DSA_MASTER_KEY_FP, "PGP fingerprints", MB_ICONINFORMATION | MB_OK, HELPCTXID(pgp_fingerprints)); } /* * Handy wrapper around GetDlgItemText which doesn't make you invent * an arbitrary length limit on the output string. Returned string is * dynamically allocated; caller must free. */ char *GetDlgItemText_alloc(HWND hwnd, int id) { char *ret = NULL; int size = 0; do { size = size * 4 / 3 + 512; ret = sresize(ret, size, char); GetDlgItemText(hwnd, id, ret, size); } while (!memchr(ret, '\0', size-1)); return ret; } /* * Split a complete command line into argc/argv, attempting to do it * exactly the same way the Visual Studio C library would do it (so * that our console utilities, which receive argc and argv already * broken apart by the C library, will have their command lines * processed in the same way as the GUI utilities which get a whole * command line and must call this function). * * Does not modify the input command line. * * The final parameter (argstart) is used to return a second array * of char * pointers, the same length as argv, each one pointing * at the start of the corresponding element of argv in the * original command line. So if you get half way through processing * your command line in argc/argv form and then decide you want to * treat the rest as a raw string, you can. If you don't want to, * `argstart' can be safely left NULL. */ void split_into_argv(char *cmdline, int *argc, char ***argv, char ***argstart) { char *p; char *outputline, *q; char **outputargv, **outputargstart; int outputargc; /* * These argument-breaking rules apply to Visual Studio 7, which * is currently the compiler expected to be used for PuTTY. Visual * Studio 10 has different rules, lacking the curious mod 3 * behaviour of consecutive quotes described below; I presume they * fixed a bug. As and when we migrate to a newer compiler, we'll * have to adjust this to match; however, for the moment we * faithfully imitate in our GUI utilities what our CLI utilities * can't be prevented from doing. * * When I investigated this, at first glance the rules appeared to * be: * * - Single quotes are not special characters. * * - Double quotes are removed, but within them spaces cease * to be special. * * - Backslashes are _only_ special when a sequence of them |
︙ | ︙ |
Changes to windows/winx11.c.
1 2 3 4 5 6 7 8 9 10 11 | /* * winx11.c: fetch local auth data for X forwarding. */ #include <ctype.h> #include <assert.h> #include <stdlib.h> #include "putty.h" #include "ssh.h" | | > | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | /* * winx11.c: fetch local auth data for X forwarding. */ #include <ctype.h> #include <assert.h> #include <stdlib.h> #include "putty.h" #include "ssh.h" void platform_get_x11_auth(struct X11Display *disp, Conf *conf) { char *xauthpath = conf_get_filename(conf, CONF_xauthfile)->path; if (xauthpath[0]) x11_get_auth_from_authfile(disp, xauthpath); } const int platform_uses_x11_unix_by_default = FALSE; |
Changes to x11fwd.c.
︙ | ︙ | |||
22 23 24 25 26 27 28 | }; struct XDMSeen { unsigned int time; unsigned char clientid[6]; }; | | > > | | | 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | }; struct XDMSeen { unsigned int time; unsigned char clientid[6]; }; struct X11Connection { const struct plug_function_table *fn; /* the above variable absolutely *must* be the first in this structure */ unsigned char firstpkt[12]; /* first X data packet */ tree234 *authtree; struct X11Display *disp; char *auth_protocol; unsigned char *auth_data; int data_read, auth_plen, auth_psize, auth_dlen, auth_dsize; int verified; int throttled, throttle_override; int no_data_sent_to_x_client; char *peer_addr; int peer_port; struct ssh_channel *c; /* channel structure held by ssh.c */ Socket s; }; static int xdmseen_cmp(void *a, void *b) { struct XDMSeen *sa = a, *sb = b; return sa->time > sb->time ? 1 : |
︙ | ︙ | |||
58 59 60 61 62 63 64 | const char *error_msg, int error_code) { } static int dummy_plug_closing (Plug p, const char *error_msg, int error_code, int calling_back) { return 1; } static int dummy_plug_receive(Plug p, int urgent, char *data, int len) { return 1; } static void dummy_plug_sent(Plug p, int bufsize) { } | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | < < | 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 | const char *error_msg, int error_code) { } static int dummy_plug_closing (Plug p, const char *error_msg, int error_code, int calling_back) { return 1; } static int dummy_plug_receive(Plug p, int urgent, char *data, int len) { return 1; } static void dummy_plug_sent(Plug p, int bufsize) { } static int dummy_plug_accepting(Plug p, accept_fn_t constructor, accept_ctx_t ctx) { return 1; } static const struct plug_function_table dummy_plug = { dummy_plug_log, dummy_plug_closing, dummy_plug_receive, dummy_plug_sent, dummy_plug_accepting }; struct X11FakeAuth *x11_invent_fake_auth(tree234 *authtree, int authtype) { struct X11FakeAuth *auth = snew(struct X11FakeAuth); int i; /* * This function has the job of inventing a set of X11 fake auth * data, and adding it to 'authtree'. We must preserve the * property that for any given actual authorisation attempt, _at * most one_ thing in the tree can possibly match it. * * For MIT-MAGIC-COOKIE-1, that's not too difficult: the match * criterion is simply that the entire cookie is correct, so we * just have to make sure we don't make up two cookies the same. * (Vanishingly unlikely, but we check anyway to be sure, and go * round again inventing a new cookie if add234 tells us the one * we thought of is already in use.) * * For XDM-AUTHORIZATION-1, it's a little more fiddly. The setup * with XA1 is that half the cookie is used as a DES key with * which to CBC-encrypt an assortment of stuff. Happily, the stuff * encrypted _begins_ with the other half of the cookie, and the * IV is always zero, which means that any valid XA1 authorisation * attempt for a given cookie must begin with the same cipher * block, consisting of the DES ECB encryption of the first half * of the cookie using the second half as a key. So we compute * that cipher block here and now, and use it as the sorting key * for distinguishing XA1 entries in the tree. */ if (authtype == X11_MIT) { auth->proto = X11_MIT; /* MIT-MAGIC-COOKIE-1. Cookie size is 128 bits (16 bytes). */ auth->datalen = 16; auth->data = snewn(auth->datalen, unsigned char); auth->xa1_firstblock = NULL; while (1) { for (i = 0; i < auth->datalen; i++) auth->data[i] = random_byte(); if (add234(authtree, auth) == auth) break; } auth->xdmseen = NULL; } else { assert(authtype == X11_XDM); auth->proto = X11_XDM; /* XDM-AUTHORIZATION-1. Cookie size is 16 bytes; byte 8 is zero. */ auth->datalen = 16; auth->data = snewn(auth->datalen, unsigned char); auth->xa1_firstblock = snewn(8, unsigned char); memset(auth->xa1_firstblock, 0, 8); while (1) { for (i = 0; i < auth->datalen; i++) auth->data[i] = (i == 8 ? 0 : random_byte()); memcpy(auth->xa1_firstblock, auth->data, 8); des_encrypt_xdmauth(auth->data + 9, auth->xa1_firstblock, 8); if (add234(authtree, auth) == auth) break; } auth->xdmseen = newtree234(xdmseen_cmp); } auth->protoname = dupstr(x11_authnames[auth->proto]); auth->datastring = snewn(auth->datalen * 2 + 1, char); for (i = 0; i < auth->datalen; i++) sprintf(auth->datastring + i*2, "%02x", auth->data[i]); auth->disp = NULL; auth->share_cs = auth->share_chan = NULL; return auth; } void x11_free_fake_auth(struct X11FakeAuth *auth) { if (auth->data) smemclr(auth->data, auth->datalen); sfree(auth->data); sfree(auth->protoname); sfree(auth->datastring); sfree(auth->xa1_firstblock); if (auth->xdmseen != NULL) { struct XDMSeen *seen; while ((seen = delpos234(auth->xdmseen, 0)) != NULL) sfree(seen); freetree234(auth->xdmseen); } sfree(auth); } int x11_authcmp(void *av, void *bv) { struct X11FakeAuth *a = (struct X11FakeAuth *)av; struct X11FakeAuth *b = (struct X11FakeAuth *)bv; if (a->proto < b->proto) return -1; else if (a->proto > b->proto) return +1; if (a->proto == X11_MIT) { if (a->datalen < b->datalen) return -1; else if (a->datalen > b->datalen) return +1; return memcmp(a->data, b->data, a->datalen); } else { assert(a->proto == X11_XDM); return memcmp(a->xa1_firstblock, b->xa1_firstblock, 8); } } struct X11Display *x11_setup_display(char *display, Conf *conf) { struct X11Display *disp = snew(struct X11Display); char *localcopy; if (!display || !*display) { localcopy = platform_get_x_display(); if (!localcopy || !*localcopy) { sfree(localcopy); localcopy = dupstr(":0"); /* plausible default for any platform */ } |
︙ | ︙ | |||
162 163 164 165 166 167 168 | * Look up the display hostname, if we need to. */ if (!disp->unixdomain) { const char *err; disp->port = 6000 + disp->displaynum; disp->addr = name_lookup(disp->hostname, disp->port, | | > | 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 | * Look up the display hostname, if we need to. */ if (!disp->unixdomain) { const char *err; disp->port = 6000 + disp->displaynum; disp->addr = name_lookup(disp->hostname, disp->port, &disp->realhost, conf, ADDRTYPE_UNSPEC); if ((err = sk_addr_error(disp->addr)) != NULL) { sk_addr_free(disp->addr); sfree(disp->hostname); sfree(disp->unixsocketpath); sfree(disp); return NULL; /* FIXME: report an error */ } } /* * Try upgrading an IP-style localhost display to a Unix-socket * display (as the standard X connection libraries do). |
︙ | ︙ | |||
208 209 210 211 212 213 214 | if (disp->unixsocketpath) disp->realhost = dupstr(disp->unixsocketpath); else disp->realhost = dupprintf("unix:%d", disp->displaynum); disp->port = 0; } | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | < < < < < < | < < < < < | | > > > > > > > > | < > > > > > | | > > > > > > > > > > | > | > | | > > > > > | | | | | | | > > | | 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 | if (disp->unixsocketpath) disp->realhost = dupstr(disp->unixsocketpath); else disp->realhost = dupprintf("unix:%d", disp->displaynum); disp->port = 0; } /* * Fetch the local authorisation details. */ disp->localauthproto = X11_NO_AUTH; disp->localauthdata = NULL; disp->localauthdatalen = 0; platform_get_x11_auth(disp, conf); return disp; } void x11_free_display(struct X11Display *disp) { sfree(disp->hostname); sfree(disp->unixsocketpath); if (disp->localauthdata) smemclr(disp->localauthdata, disp->localauthdatalen); sfree(disp->localauthdata); sk_addr_free(disp->addr); sfree(disp); } #define XDM_MAXSKEW 20*60 /* 20 minute clock skew should be OK */ static char *x11_verify(unsigned long peer_ip, int peer_port, tree234 *authtree, char *proto, unsigned char *data, int dlen, struct X11FakeAuth **auth_ret) { struct X11FakeAuth match_dummy; /* for passing to find234 */ struct X11FakeAuth *auth; /* * First, do a lookup in our tree to find the only authorisation * record that _might_ match. */ if (!strcmp(proto, x11_authnames[X11_MIT])) { /* * Just look up the whole cookie that was presented to us, * which x11_authcmp will compare against the cookies we * currently believe in. */ match_dummy.proto = X11_MIT; match_dummy.datalen = dlen; match_dummy.data = data; } else if (!strcmp(proto, x11_authnames[X11_XDM])) { /* * Look up the first cipher block, against the stored first * cipher blocks for the XDM-AUTHORIZATION-1 cookies we * currently know. (See comment in x11_invent_fake_auth.) */ match_dummy.proto = X11_XDM; match_dummy.xa1_firstblock = data; } else { return "Unsupported authorisation protocol"; } if ((auth = find234(authtree, &match_dummy, 0)) == NULL) return "Authorisation not recognised"; /* * If we're using MIT-MAGIC-COOKIE-1, that was all we needed. If * we're doing XDM-AUTHORIZATION-1, though, we have to check the * rest of the auth data. */ if (auth->proto == X11_XDM) { unsigned long t; time_t tim; int i; struct XDMSeen *seen, *ret; if (dlen != 24) return "XDM-AUTHORIZATION-1 data was wrong length"; if (peer_port == -1) return "cannot do XDM-AUTHORIZATION-1 without remote address data"; des_decrypt_xdmauth(auth->data+9, data, 24); if (memcmp(auth->data, data, 8) != 0) return "XDM-AUTHORIZATION-1 data failed check"; /* cookie wrong */ if (GET_32BIT_MSB_FIRST(data+8) != peer_ip) return "XDM-AUTHORIZATION-1 data failed check"; /* IP wrong */ if ((int)GET_16BIT_MSB_FIRST(data+12) != peer_port) return "XDM-AUTHORIZATION-1 data failed check"; /* port wrong */ t = GET_32BIT_MSB_FIRST(data+14); for (i = 18; i < 24; i++) if (data[i] != 0) /* zero padding wrong */ return "XDM-AUTHORIZATION-1 data failed check"; tim = time(NULL); if (abs(t - tim) > XDM_MAXSKEW) return "XDM-AUTHORIZATION-1 time stamp was too far out"; seen = snew(struct XDMSeen); seen->time = t; memcpy(seen->clientid, data+8, 6); assert(auth->xdmseen != NULL); ret = add234(auth->xdmseen, seen); if (ret != seen) { sfree(seen); return "XDM-AUTHORIZATION-1 data replayed"; } /* While we're here, purge entries too old to be replayed. */ for (;;) { seen = index234(auth->xdmseen, 0); assert(seen != NULL); if (t - seen->time <= XDM_MAXSKEW) break; sfree(delpos234(auth->xdmseen, 0)); } } /* implement other protocols here if ever required */ *auth_ret = auth; return NULL; } void x11_get_auth_from_authfile(struct X11Display *disp, const char *authfilename) { FILE *authfp; char *buf, *ptr, *str[4]; int len[4]; int family, protocol; int ideal_match = FALSE; char *ourhostname; /* * Normally we should look for precisely the details specified in * `disp'. However, there's an oddity when the display is local: * displays like "localhost:0" usually have their details stored * in a Unix-domain-socket record (even if there isn't actually a * real Unix-domain socket available, as with OpenSSH's proxy X11 |
︙ | ︙ | |||
368 369 370 371 372 373 374 375 376 377 378 379 380 381 | * fall back to an IP-based entry if we can find one. */ int localhost = !disp->unixdomain && sk_address_is_local(disp->addr); authfp = fopen(authfilename, "rb"); if (!authfp) return; /* Records in .Xauthority contain four strings of up to 64K each */ buf = snewn(65537 * 4, char); while (!ideal_match) { int c, i, j, match = FALSE; | > > | 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 | * fall back to an IP-based entry if we can find one. */ int localhost = !disp->unixdomain && sk_address_is_local(disp->addr); authfp = fopen(authfilename, "rb"); if (!authfp) return; ourhostname = get_hostname(); /* Records in .Xauthority contain four strings of up to 64K each */ buf = snewn(65537 * 4, char); while (!ideal_match) { int c, i, j, match = FALSE; |
︙ | ︙ | |||
484 485 486 487 488 489 490 | memcpy(disp->localauthdata, str[3], len[3]); disp->localauthdatalen = len[3]; } } done: fclose(authfp); | | > > > | > | | > > > > > > > > | > > > > | > > > > | > | > | | | | > | | | | 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 | memcpy(disp->localauthdata, str[3], len[3]); disp->localauthdatalen = len[3]; } } done: fclose(authfp); smemclr(buf, 65537 * 4); sfree(buf); sfree(ourhostname); } static void x11_log(Plug p, int type, SockAddr addr, int port, const char *error_msg, int error_code) { /* We have no interface to the logging module here, so we drop these. */ } static void x11_send_init_error(struct X11Connection *conn, const char *err_message); static int x11_closing(Plug plug, const char *error_msg, int error_code, int calling_back) { struct X11Connection *xconn = (struct X11Connection *) plug; if (error_msg) { /* * Socket error. If we're still at the connection setup stage, * construct an X11 error packet passing on the problem. */ if (xconn->no_data_sent_to_x_client) { char *err_message = dupprintf("unable to connect to forwarded " "X server: %s", error_msg); x11_send_init_error(xconn, err_message); sfree(err_message); } /* * Whether we did that or not, now we slam the connection * shut. */ sshfwd_unclean_close(xconn->c, error_msg); } else { /* * Ordinary EOF received on socket. Send an EOF on the SSH * channel. */ if (xconn->c) sshfwd_write_eof(xconn->c); } return 1; } static int x11_receive(Plug plug, int urgent, char *data, int len) { struct X11Connection *xconn = (struct X11Connection *) plug; if (sshfwd_write(xconn->c, data, len) > 0) { xconn->throttled = 1; xconn->no_data_sent_to_x_client = FALSE; sk_set_frozen(xconn->s, 1); } return 1; } static void x11_sent(Plug plug, int bufsize) { struct X11Connection *xconn = (struct X11Connection *) plug; sshfwd_unthrottle(xconn->c, bufsize); } /* * When setting up X forwarding, we should send the screen number * from the specified local display. This function extracts it from * the display string. */ |
︙ | ︙ | |||
548 549 550 551 552 553 554 | n = strcspn(display, "."); if (!display[n]) return 0; return atoi(display + n + 1); } /* | | | < < | | < < | | | | | | | | > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > | > > > > | > > > > | > > > > | > > > > > > > > > > > > > > > > > > > > > | | | > > > | > < | | | | < < < < < < < < | < | < < < < | < < < < | < < < < < < < < < < < < < < < < < < < < < < < < < < | < | < | | | | | | | | | | | > | | > | | | | | > > > > > > > > > > > > | > | | < < < > | < | | < < > > > > > | | > > | < | < < > > > > > > > > | < | < < < < < | | | | | < | < > | > | | | > | > > > | | | < < < < < > | < | < < < > | | > > > | < < | > | | < | < | < | | | > | < | < | < | > > | > > > | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 | n = strcspn(display, "."); if (!display[n]) return 0; return atoi(display + n + 1); } /* * Called to set up the X11Connection structure, though this does not * yet connect to an actual server. */ struct X11Connection *x11_init(tree234 *authtree, void *c, const char *peeraddr, int peerport) { static const struct plug_function_table fn_table = { x11_log, x11_closing, x11_receive, x11_sent, NULL }; struct X11Connection *xconn; /* * Open socket. */ xconn = snew(struct X11Connection); xconn->fn = &fn_table; xconn->auth_protocol = NULL; xconn->authtree = authtree; xconn->verified = 0; xconn->data_read = 0; xconn->throttled = xconn->throttle_override = 0; xconn->no_data_sent_to_x_client = TRUE; xconn->c = c; /* * We don't actually open a local socket to the X server just yet, * because we don't know which one it is. Instead, we'll wait * until we see the incoming authentication data, which may tell * us what display to connect to, or whether we have to divert * this X forwarding channel to a connection-sharing downstream * rather than handling it ourself. */ xconn->disp = NULL; xconn->s = NULL; /* * Stash the peer address we were given in its original text form. */ xconn->peer_addr = peeraddr ? dupstr(peeraddr) : NULL; xconn->peer_port = peerport; return xconn; } void x11_close(struct X11Connection *xconn) { if (!xconn) return; if (xconn->auth_protocol) { sfree(xconn->auth_protocol); sfree(xconn->auth_data); } if (xconn->s) sk_close(xconn->s); sfree(xconn->peer_addr); sfree(xconn); } void x11_unthrottle(struct X11Connection *xconn) { if (!xconn) return; xconn->throttled = 0; if (xconn->s) sk_set_frozen(xconn->s, xconn->throttled || xconn->throttle_override); } void x11_override_throttle(struct X11Connection *xconn, int enable) { if (!xconn) return; xconn->throttle_override = enable; if (xconn->s) sk_set_frozen(xconn->s, xconn->throttled || xconn->throttle_override); } static void x11_send_init_error(struct X11Connection *xconn, const char *err_message) { char *full_message; int msglen, msgsize; unsigned char *reply; full_message = dupprintf("%s X11 proxy: %s\n", appname, err_message); msglen = strlen(full_message); reply = snewn(8 + msglen+1 + 4, unsigned char); /* include zero */ msgsize = (msglen + 3) & ~3; reply[0] = 0; /* failure */ reply[1] = msglen; /* length of reason string */ memcpy(reply + 2, xconn->firstpkt + 2, 4); /* major/minor proto vsn */ PUT_16BIT(xconn->firstpkt[0], reply + 6, msgsize >> 2);/* data len */ memset(reply + 8, 0, msgsize); memcpy(reply + 8, full_message, msglen); sshfwd_write(xconn->c, (char *)reply, 8 + msgsize); sshfwd_write_eof(xconn->c); xconn->no_data_sent_to_x_client = FALSE; sfree(reply); sfree(full_message); } static int x11_parse_ip(const char *addr_string, unsigned long *ip) { /* * See if we can make sense of this string as an IPv4 address, for * XDM-AUTHORIZATION-1 purposes. */ int i[4]; if (addr_string && 4 == sscanf(addr_string, "%d.%d.%d.%d", i+0, i+1, i+2, i+3)) { *ip = (i[0] << 24) | (i[1] << 16) | (i[2] << 8) | i[3]; return TRUE; } else { return FALSE; } } /* * Called to send data down the raw connection. */ int x11_send(struct X11Connection *xconn, char *data, int len) { if (!xconn) return 0; /* * Read the first packet. */ while (len > 0 && xconn->data_read < 12) xconn->firstpkt[xconn->data_read++] = (unsigned char) (len--, *data++); if (xconn->data_read < 12) return 0; /* * If we have not allocated the auth_protocol and auth_data * strings, do so now. */ if (!xconn->auth_protocol) { xconn->auth_plen = GET_16BIT(xconn->firstpkt[0], xconn->firstpkt + 6); xconn->auth_dlen = GET_16BIT(xconn->firstpkt[0], xconn->firstpkt + 8); xconn->auth_psize = (xconn->auth_plen + 3) & ~3; xconn->auth_dsize = (xconn->auth_dlen + 3) & ~3; /* Leave room for a terminating zero, to make our lives easier. */ xconn->auth_protocol = snewn(xconn->auth_psize + 1, char); xconn->auth_data = snewn(xconn->auth_dsize, unsigned char); } /* * Read the auth_protocol and auth_data strings. */ while (len > 0 && xconn->data_read < 12 + xconn->auth_psize) xconn->auth_protocol[xconn->data_read++ - 12] = (len--, *data++); while (len > 0 && xconn->data_read < 12 + xconn->auth_psize + xconn->auth_dsize) xconn->auth_data[xconn->data_read++ - 12 - xconn->auth_psize] = (unsigned char) (len--, *data++); if (xconn->data_read < 12 + xconn->auth_psize + xconn->auth_dsize) return 0; /* * If we haven't verified the authorisation, do so now. */ if (!xconn->verified) { const char *err; struct X11FakeAuth *auth_matched = NULL; unsigned long peer_ip; int peer_port; int protomajor, protominor; void *greeting; int greeting_len; unsigned char *socketdata; int socketdatalen; char new_peer_addr[32]; int new_peer_port; protomajor = GET_16BIT(xconn->firstpkt[0], xconn->firstpkt + 2); protominor = GET_16BIT(xconn->firstpkt[0], xconn->firstpkt + 4); assert(!xconn->s); xconn->auth_protocol[xconn->auth_plen] = '\0'; /* ASCIZ */ peer_ip = 0; /* placate optimiser */ if (x11_parse_ip(xconn->peer_addr, &peer_ip)) peer_port = xconn->peer_port; else peer_port = -1; /* signal no peer address data available */ err = x11_verify(peer_ip, peer_port, xconn->authtree, xconn->auth_protocol, xconn->auth_data, xconn->auth_dlen, &auth_matched); if (err) { x11_send_init_error(xconn, err); return 0; } assert(auth_matched); /* * If this auth points to a connection-sharing downstream * rather than an X display we know how to connect to * directly, pass it off to the sharing module now. */ if (auth_matched->share_cs) { sshfwd_x11_sharing_handover(xconn->c, auth_matched->share_cs, auth_matched->share_chan, xconn->peer_addr, xconn->peer_port, xconn->firstpkt[0], protomajor, protominor, data, len); return 0; } /* * Now we know we're going to accept the connection, and what * X display to connect to. Actually connect to it. */ sshfwd_x11_is_local(xconn->c); xconn->disp = auth_matched->disp; xconn->s = new_connection(sk_addr_dup(xconn->disp->addr), xconn->disp->realhost, xconn->disp->port, 0, 1, 0, 0, (Plug) xconn, sshfwd_get_conf(xconn->c)); if ((err = sk_socket_error(xconn->s)) != NULL) { char *err_message = dupprintf("unable to connect to" " forwarded X server: %s", err); x11_send_init_error(xconn, err_message); sfree(err_message); return 0; } /* * Write a new connection header containing our replacement * auth data. */ socketdata = sk_getxdmdata(xconn->s, &socketdatalen); if (socketdata && socketdatalen==6) { sprintf(new_peer_addr, "%d.%d.%d.%d", socketdata[0], socketdata[1], socketdata[2], socketdata[3]); new_peer_port = GET_16BIT_MSB_FIRST(socketdata + 4); } else { strcpy(new_peer_addr, "0.0.0.0"); new_peer_port = 0; } greeting = x11_make_greeting(xconn->firstpkt[0], protomajor, protominor, xconn->disp->localauthproto, xconn->disp->localauthdata, xconn->disp->localauthdatalen, new_peer_addr, new_peer_port, &greeting_len); sk_write(xconn->s, greeting, greeting_len); smemclr(greeting, greeting_len); sfree(greeting); /* * Now we're done. */ xconn->verified = 1; } /* * After initialisation, just copy data simply. */ return sk_write(xconn->s, data, len); } void x11_send_eof(struct X11Connection *xconn) { if (xconn->s) { sk_write_eof(xconn->s); } else { /* * If EOF is received from the X client before we've got to * the point of actually connecting to an X server, then we * should send an EOF back to the client so that the * forwarded channel will be terminated. */ if (xconn->c) sshfwd_write_eof(xconn->c); } } /* * Utility functions used by connection sharing to convert textual * representations of an X11 auth protocol name + hex cookie into our * usual integer protocol id and binary auth data. */ int x11_identify_auth_proto(const char *protoname) { int protocol; for (protocol = 1; protocol < lenof(x11_authnames); protocol++) if (!strcmp(protoname, x11_authnames[protocol])) return protocol; return -1; } void *x11_dehexify(const char *hex, int *outlen) { int len, i; unsigned char *ret; len = strlen(hex) / 2; ret = snewn(len, unsigned char); for (i = 0; i < len; i++) { char bytestr[3]; unsigned val = 0; bytestr[0] = hex[2*i]; bytestr[1] = hex[2*i+1]; bytestr[2] = '\0'; sscanf(bytestr, "%x", &val); ret[i] = val; } *outlen = len; return ret; } /* * Construct an X11 greeting packet, including making up the right * authorisation data. */ void *x11_make_greeting(int endian, int protomajor, int protominor, int auth_proto, const void *auth_data, int auth_len, const char *peer_addr, int peer_port, int *outlen) { unsigned char *greeting; unsigned char realauthdata[64]; const char *authname; const unsigned char *authdata; int authnamelen, authnamelen_pad; int authdatalen, authdatalen_pad; int greeting_len; authname = x11_authnames[auth_proto]; authnamelen = strlen(authname); authnamelen_pad = (authnamelen + 3) & ~3; if (auth_proto == X11_MIT) { authdata = auth_data; authdatalen = auth_len; } else if (auth_proto == X11_XDM && auth_len == 16) { time_t t; unsigned long peer_ip = 0; x11_parse_ip(peer_addr, &peer_ip); authdata = realauthdata; authdatalen = 24; memset(realauthdata, 0, authdatalen); memcpy(realauthdata, auth_data, 8); PUT_32BIT_MSB_FIRST(realauthdata+8, peer_ip); PUT_16BIT_MSB_FIRST(realauthdata+12, peer_port); t = time(NULL); PUT_32BIT_MSB_FIRST(realauthdata+14, t); des_encrypt_xdmauth((const unsigned char *)auth_data + 9, realauthdata, authdatalen); } else { authdata = realauthdata; authdatalen = 0; } authdatalen_pad = (authdatalen + 3) & ~3; greeting_len = 12 + authnamelen_pad + authdatalen_pad; greeting = snewn(greeting_len, unsigned char); memset(greeting, 0, greeting_len); greeting[0] = endian; PUT_16BIT(endian, greeting+2, protomajor); PUT_16BIT(endian, greeting+4, protominor); PUT_16BIT(endian, greeting+6, authnamelen); PUT_16BIT(endian, greeting+8, authdatalen); memcpy(greeting+12, authname, authnamelen); memcpy(greeting+12+authnamelen_pad, authdata, authdatalen); smemclr(realauthdata, sizeof(realauthdata)); *outlen = greeting_len; return greeting; } |